diff --git a/.clang-format b/.clang-format
index 78ca1364..d6f60e37 100644
--- a/.clang-format
+++ b/.clang-format
@@ -10,7 +10,7 @@ BraceWrapping:
AfterFunction: true
AfterStruct: false
AfterUnion: false
- AfterExternBlock: true
+ AfterExternBlock: false
BeforeElse: false
BeforeWhile: false
IndentCaseLabels: false
@@ -22,6 +22,8 @@ BinPackArguments: false
BinPackParameters: false
AllowAllArgumentsOnNextLine: false
AllowShortFunctionsOnASingleLine: Empty
-AlwaysBreakAfterReturnType: None
+BreakAfterReturnType: ExceptShortType
+AlwaysBreakAfterReturnType: AllDefinitions
AlignEscapedNewlines: Left
ForEachMacros: ['ssh_callbacks_iterate']
+AlignConsecutiveMacros: 'Consecutive'
diff --git a/.clang-format-ignore b/.clang-format-ignore
new file mode 100644
index 00000000..459f5916
--- /dev/null
+++ b/.clang-format-ignore
@@ -0,0 +1 @@
+src/external/*
diff --git a/.cmake-format.yaml b/.cmake-format.yaml
new file mode 100644
index 00000000..f4117928
--- /dev/null
+++ b/.cmake-format.yaml
@@ -0,0 +1,6 @@
+---
+line_width: 80
+tab_size: 4
+use_tabchars: false
+separate_ctrl_name_with_space: true
+separate_fn_name_with_space: false
diff --git a/.editorconfig b/.editorconfig
index 59eb92bc..bb3dfbb0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,7 +12,12 @@ indent_style = space
indent_size = 4
tab_width = 4
-[{CMakeLists.txt,*.cmake}]
+[CMakeLists.txt]
indent_style = space
indent_size = 4
-tab_width = 4
\ No newline at end of file
+tab_width = 4
+
+[*.cmake]
+indent_style = space
+indent_size = 4
+tab_width = 4
diff --git a/.gitignore b/.gitignore
index 47eb46ac..aebe55f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ compile_commands.json
tags
/build
/obj*
+doc/tags.xml
+.DS_Store
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c3ee5286..258a1054 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,6 +3,7 @@ variables:
BUILD_IMAGES_PROJECT: libssh/build-images
CENTOS8_BUILD: buildenv-c8s
CENTOS9_BUILD: buildenv-c9s
+ CENTOS10_BUILD: buildenv-c10s
FEDORA_BUILD: buildenv-fedora
MINGW_BUILD: buildenv-mingw
TUMBLEWEED_BUILD: buildenv-tumbleweed
@@ -15,13 +16,28 @@ stages:
- test
- analysis
-.build:
- stage: build
+# This is some black magic to select between branch pipelines and
+# merge request pipelines to avoid running same pipelines twice
+workflow:
+ rules:
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
+ when: never
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
+ when: never
+ - if: '$CI_COMMIT_BRANCH'
+
+.build_options:
variables:
CMAKE_DEFAULT_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON"
- CMAKE_BUILD_OPTIONS: "-DWITH_BLOWFISH_CIPHER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_DEBUG_PACKET=ON -DWITH_DEBUG_CALLTRACE=ON"
- CMAKE_TEST_OPTIONS: "-DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DWITH_BENCHMARKS=ON"
+ CMAKE_DEFAULT_DEBUG_OPTIONS: "-DCMAKE_C_FLAGS='-O0 -g -ggdb' -DPICKY_DEVELOPER=ON"
+ CMAKE_BUILD_OPTIONS: "-DWITH_BLOWFISH_CIPHER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_FIDO2=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_DEBUG_PACKET=ON -DWITH_DEBUG_CALLTRACE=ON"
+ CMAKE_TEST_OPTIONS: "-DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DGSSAPI_TESTING=ON -DWITH_BENCHMARKS=ON -DFUZZ_TESTING=ON"
CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS $CMAKE_BUILD_OPTIONS $CMAKE_TEST_OPTIONS
+
+.build:
+ extends: .build_options
+ stage: build
before_script: &build
- uname -a
- cat /etc/os-release
@@ -36,7 +52,11 @@ stages:
make -j$(nproc) install
# Do not use after_script as it does not make the targets fail
tags:
- - shared
+ - saas-linux-small-amd64
+ only:
+ - merge_requests
+ - branches
+
except:
- tags
artifacts:
@@ -59,12 +79,46 @@ stages:
.fedora:
extends: .tests
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
- variables:
- CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
.tumbleweed:
extends: .tests
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
+ script:
+ # torture_gssapi_key_exchange_null is excluded because of a bug
+ # https://bugzilla.opensuse.org/show_bug.cgi?id=1254680
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
+
+.centos:
+ extends: .tests
+ variables:
+ OPENSSL_ENABLE_SHA1_SIGNATURES: 1
+
+.centos10:
+ extends: .centos
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
+
+.centos9:
+ extends: .centos
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+
+# Unit testing only, no client and pkd testing, because cwrap is not available
+# for MinGW
+.mingw:
+ extends: .tests
+ variables:
+ WINEDEBUG: -all
+ script:
+ - $WINEBIN $CMAKE_DEFAULT_OPTIONS
+ -DWITH_SFTP=ON
+ -DWITH_SERVER=ON
+ -DWITH_ZLIB=ON
+ -DWITH_PCAP=ON
+ -DWITH_FIDO2=ON
+ -DUNIT_TESTING=ON .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure -E torture_rand
.fips:
extends: .tests
@@ -82,30 +136,78 @@ stages:
- update-crypto-policies --set FIPS
- update-crypto-policies --show
+###############################################################################
+# Review #
+###############################################################################
+review:
+ variables:
+ GIT_DEPTH: 100
+ stage: review
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ script:
+ - ERROR=0
+ codespell --ignore-words-list=keypair,sorce,ned,nd,ue,pendin || ERROR=1;
+ ./.gitlab-ci/clang-format-check.sh || ERROR=1;
+ ./.gitlab-ci/git-check-signoff-trailer.sh ${CI_MERGE_REQUEST_DIFF_BASE_SHA} || ERROR=1;
+ ./.gitlab-ci/shellcheck.sh || ERROR=1;
+ exit $ERROR
+ # the format is not always matching our intentions
+ allow_failure: true
+ tags:
+ - saas-linux-small-amd64
+ only:
+ - merge_requests
###############################################################################
# CentOS builds #
###############################################################################
-centos9s/openssl_3.0.x/x86_64:
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
- extends: .tests
+centos10s/openssl_3.5.x/x86_64:
+ extends: .centos10
variables:
- CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
script:
- - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure
-centos9s/openssl_3.0.x/x86_64/fips:
+centos10s/openssl_3.5.x/x86_64/fips:
extends: .fips
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
+ OPENSSL_ENABLE_SHA1_SIGNATURES: 1
script:
- - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
+centos9s/openssl_3.5.x/x86_64:
+ extends: .centos9
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
+ script:
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure
+
+centos9s/mbedtls_2.x/x86_64:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+ extends: .tests
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF"
+
+centos9s/openssl_3.5.x/x86_64/fips:
+ extends: .fips
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+ variables:
+ OPENSSL_ENABLE_SHA1_SIGNATURES: 1
+ script:
+ # torture_gssapi_key_exchange_* tests are excluded because gssapi-keyex is disabled
+ # by OpenSSH in FIPS mode in RHEL 9
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi_key_exchange.*"
+
centos8s/openssl_1.1.1/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
extends: .tests
@@ -120,9 +222,11 @@ centos8s/openssl_1.1.1/x86_64/fips:
extends: .fips
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
script:
+ # torture_gssapi_key_exchange_* and torture_gssapi_server_key_exchange_* tests are excluded
+ # because gssapi-keyex is not allowed in FIPS mode in RHEL 8
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
- OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
+ OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi.*key_exchange.*"
###############################################################################
# Fedora builds #
@@ -135,20 +239,22 @@ fedora/docs:
extends: .build
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
script:
- - cmake .. && make docs_coverage && make docs
+ - cmake -DWITH_INTERNAL_DOC=ON .. && make docs_coverage && make docs
coverage: '/^Documentation coverage is \d+.\d+%/'
fedora/ninja:
extends: .fedora
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ variables:
+ CTEST_OUTPUT_ON_FAILURE: 1
script:
- - cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && CTEST_OUTPUT_ON_FAILURE=1 ninja test
+ - cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && ninja test
fedora/coverage:
extends: .fedora
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
variables:
- CMAKE_ADDITIONAL_OPTIONS: -DCMAKE_BUILD_TYPE=Coverage
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DWITH_COVERAGE=ON"
script:
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
@@ -162,33 +268,31 @@ fedora/coverage:
coverage_format: cobertura
path: obj/coverage_xml.xml
-fedora/openssl_3.0.x/x86_64:
+fedora/openssl_3.x/x86_64:
extends: .fedora
-fedora/openssl_3.0.x/x86_64/pkcs11-provider:
+fedora/openssl_3.x/x86_64/pkcs11-provider:
variables:
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
extends: .fedora
-fedora/openssl_3.0.x/x86_64/minimal:
+fedora/openssl_3.x/x86_64/minimal:
extends: .fedora
variables:
script:
- cmake $CMAKE_DEFAULT_OPTIONS
+ -DWITH_EXEC=OFF
-DWITH_SFTP=OFF
-DWITH_SERVER=OFF
-DWITH_ZLIB=OFF
-DWITH_PCAP=OFF
+ -DWITH_GSSAPI=OFF
+ -DWITH_GEX=OFF
-DUNIT_TESTING=ON
- -DCLIENT_TESTING=ON
- -DWITH_GEX=OFF .. &&
+ -DCLIENT_TESTING=ON .. &&
make -j$(nproc)
-# The PKCS#11 support is turned off as it brings dozens of memory issues from
-# engine_pkcs11 or openssl itself
-fedora/valgrind:
- variables:
- CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=OFF
+.valgrind:
extends: .fedora
stage: analysis
script:
@@ -197,6 +301,54 @@ fedora/valgrind:
make test_memcheck
- cat Testing/Temporary/MemoryChecker.*.log | wc -l | grep "^0$"
+fedora/libressl/x86_64:
+ extends: .fedora
+ stage: test
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ variables:
+ LIBRESSL_VERSION: "4.2.1"
+ CMAKE_ADDITIONAL_OPTIONS: >
+ -DCMAKE_C_FLAGS="-I/opt/libressl/include"
+ -DOPENSSL_ROOT_DIR=/opt/libressl
+ -DOPENSSL_INCLUDE_DIR=/opt/libressl/include
+ -DOPENSSL_CRYPTO_LIBRARY=/opt/libressl/lib/libcrypto.so
+ -DOPENSSL_SSL_LIBRARY=/opt/libressl/lib/libssl.so
+ -DWITH_GSSAPI=OFF
+ -DWITH_FIDO2=OFF
+ before_script:
+ - *build
+ - dnf install -y perl-core autoconf automake libtool pkgconf-pkg-config
+ - curl -LO https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz
+ - tar xf libressl-${LIBRESSL_VERSION}.tar.gz
+ - cd libressl-${LIBRESSL_VERSION}
+ - ./configure --prefix=/opt/libressl
+ - make -j$(nproc)
+ - make install
+ - cd ..
+ script:
+ - export PKG_CONFIG_PATH=/opt/libressl/lib/pkgconfig
+ - export LD_LIBRARY_PATH=/opt/libressl/lib
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure
+
+# The PKCS#11 support is turned off as it brings dozens of memory issues from
+# engine_pkcs11 or openssl itself
+fedora/valgrind/openssl:
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=OFF
+ extends: .valgrind
+
+fedora/valgrind/mbedtls:
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_MBEDTLS=ON
+ extends: .valgrind
+
+fedora/valgrind/libgcrypt:
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_GCRYPT=ON
+ extends: .valgrind
+
# Address sanitizer doesn't mix well with LD_PRELOAD used in the testsuite
# so, this is only enabled for unit tests right now.
# TODO: add -DCLIENT_TESTING=ON -DSERVER_TESTING=ON
@@ -250,47 +402,26 @@ fedora/undefined-sanitizer:
fedora/libgcrypt/x86_64:
extends: .fedora
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON -DWITH_DEBUG_CRYPTO=ON"
+ CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON"
-fedora/mbedtls/x86_64:
+fedora/mbedtls_3.x/x86_64:
extends: .fedora
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON "
+ CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON"
-# Unit testing only, no client and pkd testing, because cwrap is not available
-# for MinGW
fedora/mingw64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$MINGW_BUILD
- extends: .tests
- script:
- - export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin
- - export WINEDEBUG=-all
- - mingw64-cmake $CMAKE_DEFAULT_OPTIONS
- -DWITH_SFTP=ON
- -DWITH_SERVER=ON
- -DWITH_ZLIB=ON
- -DWITH_PCAP=ON
- -DUNIT_TESTING=ON .. &&
- make -j$(nproc) &&
- ctest --output-on-failure
+ extends: .mingw
+ variables:
+ WINEPATH: /usr/x86_64-w64-mingw32/sys-root/mingw/bin
+ WINEBIN: mingw64-cmake
-# Unit testing only, no client and pkd testing, because cwrap is not available
-# for MinGW
fedora/mingw32:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$MINGW_BUILD
- extends: .tests
- script:
- - export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin
- - export WINEDEBUG=-all
- - mingw32-cmake $CMAKE_DEFAULT_OPTIONS
- -DWITH_SFTP=ON
- -DWITH_SERVER=ON
- -DWITH_ZLIB=ON
- -DWITH_PCAP=ON
- -DUNIT_TESTING=ON .. &&
- make -j$(nproc) &&
- ctest --output-on-failure
-
+ extends: .mingw
+ variables:
+ WINEPATH: /usr/i686-w64-mingw32/sys-root/mingw/bin
+ WINEBIN: mingw32-cmake
###############################################################################
# Fedora csbuild #
@@ -299,9 +430,16 @@ fedora/mingw32:
stage: analysis
variables:
GIT_DEPTH: "100"
+ CSCPPC_ADD_OPTS: "--library=./.gitlab-ci/cmocka.cfg"
+ CMAKE_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON"
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
before_script:
- |
+ # for merge requests
+ if [[ -n "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]]; then
+ export CI_COMMIT_BEFORE_SHA="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
+ fi
+ # for branches run
if [[ -z "$CI_COMMIT_BEFORE_SHA" ]]; then
export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
fi
@@ -311,46 +449,95 @@ fedora/mingw32:
git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
export CI_COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
+ script:
+ - csbuild
+ --build-dir=obj-csbuild
+ --build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS @SRCDIR@ && make clean && make -j$(nproc)"
+ --git-commit-range $CI_COMMIT_RANGE
+ --color
+ --print-current --print-fixed
+
tags:
- - shared
+ - saas-linux-small-amd64
except:
- tags
+ only:
+ - merge_requests
artifacts:
expire_in: 1 week
when: on_failure
paths:
- obj-csbuild/
-fedora/csbuild/openssl_3.0.x:
+fedora/csbuild/openssl_3.x:
extends: .csbuild
- script:
- - csbuild
- --build-dir=obj-csbuild
- --build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
fedora/csbuild/libgcrypt:
extends: .csbuild
- script:
- - csbuild
- --build-dir=obj-csbuild
- --build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON -DWITH_GCRYPT=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_GCRYPT=ON
fedora/csbuild/mbedtls:
extends: .csbuild
- script:
- - csbuild
- --build-dir=obj-csbuild
- --build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON -DWITH_MBEDTLS=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_MBEDTLS=ON
+
+###############################################################################
+# Fedora abidiff #
+###############################################################################
+
+fedora/abidiff:
+ stage: analysis
+ variables:
+ GIT_DEPTH: "100"
+ CMAKE_OPTIONS: $CMAKE_DEFAULT_DEBUG_OPTIONS $CMAKE_BUILD_OPTIONS
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ before_script:
+ - uname -a
+ - cat /etc/os-release
+ - mount
+ - df -h
+ - cat /proc/swaps
+ - free -h
+ - |
+ # for merge requests
+ if [[ -n "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]]; then
+ export CI_COMMIT_BEFORE_SHA="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
+ fi
+ # for branches run
+ if [[ -z "$CI_COMMIT_BEFORE_SHA" ]]; then
+ export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
+ fi
+
+ # Check if the commit exists in this branch
+ # This is not the case for a force push
+ git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
+ - mkdir -p obj-${CI_COMMIT_BEFORE_SHA}
+ - mkdir -p obj-${CI_COMMIT_SHA}
+
+ - export INSTALL_DIR1=$(pwd)/install/${CI_COMMIT_BEFORE_SHA}
+ - export INSTALL_DIR2=$(pwd)/install/${CI_COMMIT_SHA}
+ script:
+ - git checkout ${CI_COMMIT_BEFORE_SHA}
+ - pushd obj-${CI_COMMIT_BEFORE_SHA}
+ - cmake ${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR1} .. &&
+ make -j$(nproc) && make -j$(nproc) install
+ - popd
+ - ls -l ${INSTALL_DIR1}/lib*/
+ - git checkout ${CI_COMMIT_SHA}
+ - pushd obj-${CI_COMMIT_SHA}
+ - cmake ${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR2} .. &&
+ make -j$(nproc) && make -j$(nproc) install
+ - popd
+ - ls -l ${INSTALL_DIR2}/lib*/
+ - ./.gitlab-ci/checkabi.sh ${INSTALL_DIR1} ${INSTALL_DIR2}
+ tags:
+ - saas-linux-small-amd64
+ except:
+ - tags
+ only:
+ - merge_requests
###############################################################################
# Ubuntu builds #
@@ -358,12 +545,18 @@ fedora/csbuild/mbedtls:
ubuntu/openssl_3.0.x/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$UBUNTU_BUILD
extends: .tests
+ script:
+ # torture_gssapi_key_exchange_null is excluded because of a bug
+ # https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/2134527
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
###############################################################################
# Alpine builds #
###############################################################################
-alpine/openssl_3.0.x/musl:
+alpine/openssl_3.x/musl:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$ALPINE_BUILD
extends: .tests
script:
@@ -380,12 +573,10 @@ alpine/openssl_3.0.x/musl:
###############################################################################
# Tumbleweed builds #
###############################################################################
-tumbleweed/openssl_3.0.x/x86_64/gcc:
+tumbleweed/openssl_3.x/x86_64/gcc:
extends: .tumbleweed
- variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
-tumbleweed/openssl_3.0.x/x86/gcc:
+tumbleweed/openssl_3.x/x86/gcc:
extends: .tumbleweed
script:
- cmake
@@ -398,12 +589,12 @@ tumbleweed/openssl_3.0.x/x86/gcc:
-DUNIT_TESTING=ON .. &&
make -j$(nproc)
-tumbleweed/openssl_3.0.x/x86_64/gcc7:
+tumbleweed/openssl_3.x/x86_64/gcc7:
extends: .tumbleweed
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7 -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7"
-tumbleweed/openssl_3.0.x/x86/gcc7:
+tumbleweed/openssl_3.x/x86/gcc7:
extends: .tumbleweed
script:
- cmake
@@ -415,18 +606,29 @@ tumbleweed/openssl_3.0.x/x86/gcc7:
make -j$(nproc) &&
ctest --output-on-failure
-tumbleweed/openssl_3.0.x/x86_64/clang:
+tumbleweed/openssl_3.x/x86_64/clang:
extends: .tumbleweed
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
+
+tumbleweed/mbedtls-3.6.x/x86_64/gcc:
+ extends: .tumbleweed
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: "-DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config -DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF "
+
+tumbleweed/mbedtls-3.6.x/x86_64/clang:
+ extends: .tumbleweed
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config -DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF "
tumbleweed/static-analysis:
extends: .tests
stage: analysis
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
+ variables:
+ CCC_CC: clang
+ CCC_CXX: clang++
script:
- - export CCC_CC=clang
- - export CCC_CXX=clang++
- scan-build cmake
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_C_COMPILER=clang
@@ -450,19 +652,12 @@ tumbleweed/static-analysis:
freebsd/openssl_1.1.1/x86_64:
image:
extends: .tests
+ variables:
+ CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DUNIT_TESTING=ON
before_script:
- - mkdir -p obj && cd obj && cmake
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
- -DUNIT_TESTING=ON ..
+ - mkdir -p obj && cd obj && cmake $CMAKE_OPTIONS ..
script:
- - cmake $CMAKE_DEFAULT_OPTIONS
- -DWITH_SFTP=ON
- -DWITH_SERVER=ON
- -DWITH_ZLIB=ON
- -DWITH_PCAP=ON
- -DUNIT_TESTING=ON .. &&
+ - cmake $CMAKE_OPTIONS .. &&
make &&
ctest --output-on-failure
tags:
@@ -472,12 +667,13 @@ freebsd/openssl_1.1.1/x86_64:
- branches@libssh/libssh-mirror
- branches@cryptomilk/libssh-mirror
- branches@jjelen/libssh-mirror
- - branches@marco.fortina/libssh-mirror
###############################################################################
# Visual Studio builds #
###############################################################################
+# 2024-05-13: These jobs run out of the stages as they take extremely long and
+# usually timeout with the update to Gitlab 17.0
.vs:
stage: test
cache:
@@ -490,8 +686,10 @@ freebsd/openssl_1.1.1/x86_64:
- cmake --build .
- ctest --output-on-failure
tags:
- - windows
- - shared-windows
+ - saas-windows-medium-amd64
+ only:
+ - merge_requests
+ - branches
except:
- tags
artifacts:
@@ -507,13 +705,14 @@ freebsd/openssl_1.1.1/x86_64:
- vcpkg install cmocka
- vcpkg install openssl
- vcpkg install zlib
+ - vcpkg install libfido2
- vcpkg integrate install
- mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
- cmake
-A $PLATFORM
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
-DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_FIDO2=ON
-DUNIT_TESTING=ON ..
visualstudio/x86_64:
@@ -555,7 +754,7 @@ coverity:
--form description="CI build"
https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
tags:
- - shared
+ - saas-linux-small-amd64
only:
refs:
- master
@@ -571,12 +770,36 @@ coverity:
- obj/cov-int/*.txt
###############################################################################
-# Codespell #
+# MacOS #
###############################################################################
-codespell:
- stage: review
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
- script:
- - codespell --ignore-words-list=keypair,sorce,ned,nd,ue
+.macos:
+ extends: .build_options
tags:
- - shared
+ - saas-macos-medium-m1
+ image: macos-14-xcode-15
+ before_script:
+ - echo "MacOS runner started"
+ - brew update
+ - brew install cmake openssl cmocka doxygen
+ - mkdir obj && cd obj
+ only:
+ - branches@libssh/libssh-mirror
+ - branches@cryptomilk/libssh-mirror
+ - branches@jjelen/libssh-mirror
+
+# TODO add -DFUZZ_TESTING=ON clang cant find _LLVMFuzzerInitialize on arm64
+macos-m1:
+ extends: .macos
+ variables:
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ CMAKE_TEST_OPTIONS: "-DUNIT_TESTING=ON"
+ stage: test
+ script:
+ - cmake $CMAKE_OPTIONS .. &&
+ make -j$(sysctl -n hw.logicalcpu) &&
+ ctest --output-on-failure
+ artifacts:
+ expire_in: 1 week
+ when: on_failure
+ paths:
+ - obj/
diff --git a/.gitlab-ci/checkabi.sh b/.gitlab-ci/checkabi.sh
new file mode 100755
index 00000000..7be336bb
--- /dev/null
+++ b/.gitlab-ci/checkabi.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+INSTALL_DIR1=${1}
+INSTALL_DIR2=${2}
+
+abidiff \
+ --headers-dir1 "${INSTALL_DIR1}/include/libssh/" \
+ --headers-dir2 "${INSTALL_DIR2}/include/libssh/" \
+ "${INSTALL_DIR1}/lib64/libssh.so" \
+ "${INSTALL_DIR2}/lib64/libssh.so" \
+ --fail-no-debug-info
+abiret=$?
+
+ABIDIFF_ERROR=$(((abiret & 0x01) != 0))
+ABIDIFF_USAGE_ERROR=$(((abiret & 0x02) != 0))
+ABIDIFF_ABI_CHANGE=$(((abiret & 0x04) != 0))
+ABIDIFF_ABI_INCOMPATIBLE_CHANGE=$(((abiret & 0x08) != 0))
+ABIDIFF_UNKNOWN_BIT_SET=$(((abiret & 0xf0) != 0))
+
+if [ $ABIDIFF_ERROR -ne 0 ]; then
+ echo "abidiff reported ABIDIFF_ERROR."
+ exit 1
+fi
+if [ $ABIDIFF_USAGE_ERROR -ne 0 ]; then
+ echo "abidiff reported ABIDIFF_USAGE_ERROR."
+ exit 1
+fi
+if [ $ABIDIFF_UNKNOWN_BIT_SET -ne 0 ]; then
+ echo "abidiff reported ABIDIFF_UNKNOWN_BIT_SET."
+ exit 1
+fi
+
+if [ $ABIDIFF_ABI_INCOMPATIBLE_CHANGE -ne 0 ]; then
+ echo "abidiff result ABIDIFF_ABI_INCOMPATIBLE_CHANGE, this breaks the API!"
+ exit 1
+fi
+
+if [ $ABIDIFF_ABI_CHANGE -ne 0 ]; then
+ echo "Ignoring abidiff result ABI_CHANGE"
+fi
+
+exit 0
diff --git a/.gitlab-ci/clang-format-check.sh b/.gitlab-ci/clang-format-check.sh
new file mode 100755
index 00000000..261918ae
--- /dev/null
+++ b/.gitlab-ci/clang-format-check.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Based on Github Action
+# https://github.com/yshui/git-clang-format-lint
+
+diff=$(git-clang-format --diff --commit "$CI_MERGE_REQUEST_DIFF_BASE_SHA")
+[ "$diff" = "no modified files to format" ] && exit 0
+[ "$diff" = "clang-format did not modify any files" ] && exit 0
+
+printf "You have introduced coding style breakages, suggested changes:\n\n"
+
+echo "${diff}" | colordiff
+exit 1
diff --git a/.gitlab-ci/cmocka.cfg b/.gitlab-ci/cmocka.cfg
new file mode 100644
index 00000000..756cbdd0
--- /dev/null
+++ b/.gitlab-ci/cmocka.cfg
@@ -0,0 +1,475 @@
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+ arg1 != 0
+
+
+
+
+
+
+
+
+
+
+
+
+ arg1 == 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arg1 == arg2
+
+
+
+
+
+
+
+
+
+
+
+ arg1 != arg2
+
+
+
+
+
+
+
+
+
+
+
+
+ arg1 == arg2
+
+
+
+
+
+
+
+
+
+
+ arg1 != arg2
+
+
+
+
+
+
+
+
+
+
+ arg1 == arg2
+
+
+
+
+
+
+
+
+
+
+ arg1 != arg2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arg2 <= arg1 && arg1 <= arg3
+
+
+
+
+
+
+
+
+
+
+
+ arg1 < arg2 || arg3 < arg1
+
+
+
+
+
+
+
+
+
+
+
+ arg2 <= arg1 && arg1 <= arg3
+
+
+
+
+
+
+
+
+
+
+
+ arg1 < arg2 || arg3 < arg1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 <= arg1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitlab-ci/git-check-signoff-trailer.sh b/.gitlab-ci/git-check-signoff-trailer.sh
new file mode 100755
index 00000000..e3819662
--- /dev/null
+++ b/.gitlab-ci/git-check-signoff-trailer.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+if [ $# != 1 ]; then
+ echo "Usage: $0 UPSTREAM_COMMIT_SHA"
+ exit 1
+fi
+
+failed=0
+
+if [ -z "$CI_COMMIT_SHA" ]; then
+ echo "CI_COMMIT_SHA is not set"
+ exit 1
+fi
+
+CI_COMMIT_RANGE="$1..$CI_COMMIT_SHA"
+
+red='\033[0;31m'
+blue='\033[0;34m'
+
+echo -e "${blue}Checking commit range: $CI_COMMIT_RANGE"
+echo
+echo
+
+for commit in $(git rev-list "$CI_COMMIT_RANGE"); do
+ git show -s --format=%B "$commit" | grep "^Signed-off-by: " >/dev/null 2>&1
+ ret=$?
+ if [ $ret -eq 1 ]; then
+ echo -e "${red} >>> Missing Signed-off-by trailer in commit $commit"
+ failed=$(("$failed" + 1))
+ fi
+done
+
+echo
+echo
+
+exit $failed
diff --git a/.gitlab-ci/local-ci.sh b/.gitlab-ci/local-ci.sh
new file mode 100755
index 00000000..039447ce
--- /dev/null
+++ b/.gitlab-ci/local-ci.sh
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+set -e
+
+RED="\033[1;31m"
+GREEN="\033[1;32m"
+YELLOW="\033[1;33m"
+BLUE="\033[1;34m"
+RESET="\033[0m"
+
+export GCL_IGNORE_PREDEFINED_VARS=CI_REGISTRY
+
+BASE_SHA=$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD~1)
+
+COMMON_ARGS=(
+ --variable "CI_MERGE_REQUEST_DIFF_BASE_SHA=$BASE_SHA"
+ --variable "CI_REGISTRY=registry.gitlab.com"
+ --json-schema-validation=false
+)
+
+check_requirements() {
+ for cmd in docker git gitlab-ci-local; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo -e "${RED}Missing dependency: $cmd${RESET}"
+ exit 1
+ fi
+ echo -e "${GREEN}Found: $cmd${RESET}"
+ done
+
+ if ! docker info >/dev/null 2>&1; then
+ echo -e "${RED}Docker daemon is not running or permission denied${RESET}"
+ exit 1
+ fi
+}
+
+list_jobs() {
+ gitlab-ci-local --list --json-schema-validation=false | awk 'NR>1 {print $1}'
+}
+
+run_job() {
+ JOB="$1"
+ echo -e "${YELLOW}Running CI job: $JOB${RESET}"
+ gitlab-ci-local "$JOB" "${COMMON_ARGS[@]}"
+}
+
+cleanup_images() {
+ echo -e "${BLUE}Removing libssh CI images only...${RESET}"
+ docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" \
+ | grep "$CI_REGISTRY/$BUILD_IMAGES_PROJECT" \
+ | awk '{print $2}' \
+ | xargs -r docker rmi -f
+}
+
+usage() {
+ echo
+ echo -e "${BLUE}Usage:${RESET}"
+ echo " $0 --list"
+ echo " $0 --run "
+ echo " $0 --all"
+ echo " $0 --run --clean"
+ echo " $0 --all --clean"
+ echo
+ exit 1
+}
+
+check_requirements
+
+CLEAN=0
+MODE=""
+JOB=""
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --list)
+ MODE="list"
+ shift
+ ;;
+ --run)
+ MODE="run"
+ JOB="$2"
+ shift 2
+ ;;
+ --all)
+ MODE="all"
+ shift
+ ;;
+ --clean)
+ CLEAN=1
+ shift
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+case "$MODE" in
+ list)
+ list_jobs
+ ;;
+ run)
+ [[ -z "$JOB" ]] && usage
+ run_job "$JOB"
+ [[ "$CLEAN" -eq 1 ]] && cleanup_images
+ ;;
+ all)
+ for job in $(list_jobs); do
+ run_job "$job"
+ [[ "$CLEAN" -eq 1 ]] && cleanup_images
+ done
+ ;;
+ *)
+ usage
+ ;;
+esac
+
+echo -e "${GREEN}Done.${RESET}"
diff --git a/.gitlab-ci/shellcheck.sh b/.gitlab-ci/shellcheck.sh
new file mode 100755
index 00000000..e7db0b63
--- /dev/null
+++ b/.gitlab-ci/shellcheck.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+# Simplified and de-github-ed version of
+# https://github.com/ludeeus/action-shellcheck/blob/master/action.yaml
+
+statuscode=0
+
+declare -a filepaths
+shebangregex="^#! */[^ ]*/(env *)?[abk]*sh"
+set -f # temporarily disable globbing so that globs in inputs aren't expanded
+
+while IFS= read -r -d '' file; do
+ filepaths+=("$file")
+done < <(find . \
+ -type f \
+ '(' \
+ -name '*.bash' \
+ -o -name '.bashrc' \
+ -o -name 'bashrc' \
+ -o -name '.bash_aliases' \
+ -o -name '.bash_completion' \
+ -o -name '.bash_login' \
+ -o -name '.bash_logout' \
+ -o -name '.bash_profile' \
+ -o -name 'bash_profile' \
+ -o -name '*.ksh' \
+ -o -name 'suid_profile' \
+ -o -name '*.zsh' \
+ -o -name '.zlogin' \
+ -o -name 'zlogin' \
+ -o -name '.zlogout' \
+ -o -name 'zlogout' \
+ -o -name '.zprofile' \
+ -o -name 'zprofile' \
+ -o -name '.zsenv' \
+ -o -name 'zsenv' \
+ -o -name '.zshrc' \
+ -o -name 'zshrc' \
+ -o -name '*.sh' \
+ -o -path '*/.profile' \
+ -o -path '*/profile' \
+ -o -name '*.shlib' \
+ ')' \
+ -print0)
+
+while IFS= read -r -d '' file; do
+ head -n1 "$file" | grep -Eqs "$shebangregex" || continue
+ filepaths+=("$file")
+done < <(find . \
+ -type f ! -name '*.*' -perm /111 \
+ -print0)
+
+shellcheck "${filepaths[@]}" || statuscode=$?
+
+set +f # re-enable globbing
+
+exit "$statuscode"
diff --git a/.gitlab/merge_request_templates/default.md b/.gitlab/merge_request_templates/default.md
new file mode 100644
index 00000000..6a95d6d6
--- /dev/null
+++ b/.gitlab/merge_request_templates/default.md
@@ -0,0 +1,16 @@
+Add a description of the new feature/bug fix. Reference any relevant bugs.
+
+## Checklist
+ * [ ] Commits have `Signed-off-by:` with name/author being identical to the commit author
+ * [ ] Code modified for feature
+ * [ ] Test suite updated with functionality tests
+ * [ ] Test suite updated with negative tests
+ * [ ] Documentation updated
+ * [ ] The project pipelines timeout is [extended](https://docs.gitlab.com/ee/ci/pipelines/settings.html#set-a-limit-for-how-long-jobs-can-run) at least to 2 hours.
+
+## Reviewer's checklist:
+ * [ ] Any issues marked for closing are addressed
+ * [ ] There is a test suite reasonably covering new functionality or modifications
+ * [ ] Function naming, parameters, return values, types, etc., are consistent and according to [CONTRIBUTING.md](https://gitlab.com/libssh/libssh-mirror/-/blob/master/CONTRIBUTING.md)
+ * [ ] This feature/change has adequate documentation added
+ * [ ] No obvious mistakes in the code
diff --git a/CHANGELOG b/CHANGELOG
index 52633dcf..780b0607 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,218 @@
CHANGELOG
=========
-version 0.10.0 (released 2022-07-xx)
+version 0.12.0 (released 2026-02-10)
+ * Deprecations and removals:
+ * Bumped minimal RSA key size to 1024 bits
+ * New functionality:
+ * Add support for hybrid key exchange mechanisms using Quantum Resistant
+ cryptography for all backends. These are now preferred:
+ * sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com
+ * mlkem768nistp256-sha256
+ * mlkem768x25519-sha256
+ * mlkem1024nistp384-sha384 (only OpenSSL 3.5+ and libgcrypt)
+ * New cmake option WITH_HERMETIC_USR
+ * Added support for Ed25519 keys through PKCS#11
+ * Support for host-bound public key authentication
+ (publickey-hostbound-v00@openssh.com)
+ * Use curve25519 implementation from mbedTLS and libgcrypt
+ * New functions for signing arbitrary data (commits) with SSH keys
+ * sshsig_sign()
+ * sshsig_verify()
+ * Support for FIDO/U2F keys (internal implementation using libfido2)
+ * Compatible with OpenSSH: should work out of the box
+ * Extensible with callbacks
+ * Add support for GSSAPI Key Exchange (RFC 4462, RFC 8732)
+ * Add support for new configuration options (client and server):
+ * RequiredRsaSize
+ * AddressFamily (client)
+ * GSSAPIKeyExchange
+ * GSSAPIKexAlgorithms
+ * New option to get list of configured identities (SSH_OPTIONS_NEXT_IDENTITY)
+ * More OpenSSH compatible percent expansion characters
+ * Add new server auth_kbdint_function() callback
+ * New PKI Context structure for key operations
+ * Stability and compatibility improvements of ProxyJump
+ * SFTP
+ * Prevent failures when SFTP status message does not contain error message
+ * Fix possible timeouts while waiting for SFTP messages
+ * Support for users-groups-by-id@openssh.com extension in client
+ * Support for SSH_FXF_TRUNC in server
+
+version 0.11.4 (released 2026-02-10)
+ * Security:
+ * CVE-2025-14821: libssh loads configuration files from the C:\etc directory
+ on Windows
+ * CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request()
+ * CVE-2026-0965: Possible Denial of Service when parsing unexpected
+ configuration files
+ * CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
+ * CVE-2026-0967: Specially crafted patterns could cause DoS
+ * CVE-2026-0968: OOB Read in sftp_parse_longname()
+ * libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP
+ extensions
+ * Stability and compatibility improvements of ProxyJump
+
+version 0.11.3 (released 2025-09-09)
+ * Security:
+ * CVE-2025-8114: Fix NULL pointer dereference after allocation failure
+ * CVE-2025-8277: Fix memory leak of ephemeral key pair during repeated wrong KEX
+ * Potential UAF when send() fails during key exchange
+ * Fix possible timeout during KEX if client sends authentication too early (#311)
+ * Cleanup OpenSSL PKCS#11 provider when loaded
+ * Zeroize buffers containing private key blobs during export
+
+version 0.11.2 (released 2025-06-24)
+ * Security:
+ * CVE-2025-4877 - Write beyond bounds in binary to base64 conversion
+ * CVE-2025-4878 - Use of uninitialized variable in privatekey_from_file()
+ * CVE-2025-5318 - Likely read beyond bounds in sftp server handle management
+ * CVE-2025-5351 - Double free in functions exporting keys
+ * CVE-2025-5372 - ssh_kdf() returns a success code on certain failures
+ * CVE-2025-5449 - Likely read beyond bounds in sftp server message decoding
+ * CVE-2025-5987 - Invalid return code for chacha20 poly1305 with OpenSSL
+ * Compatibility
+ * Fixed compatibility with CPM.cmake
+ * Compatibility with OpenSSH 10.0
+ * Tests compatibility with new Dropbear releases
+ * Removed p11-kit remoting from the pkcs11 testsuite
+ * Bugfixes
+ * Implement missing packet filter for DH GEX
+ * Properly process the SSH2_MSG_DEBUG message
+ * Allow escaping quotes in quoted arguments to ssh configuration
+ * Do not fail with unknown match keywords in ssh configuration
+ * Process packets before selecting signature algorithm during authentication
+ * Do not fail hard when the SFTP status message is not sent by noncompliant
+ servers
+
+version 0.11.1 (released 2024-08-30)
+ * Fixed default TTY modes that are set when stdin is not connected to tty (#270)
+ * Fixed zlib cleanup procedure, which could crash on i386
+ * Various test fixes improving their stability
+ * Fixed cygwin build
+
+version 0.11.0 (released 2024-07-31)
+ * Deprecations and Removals:
+ * Dropped support for DSA
+ * Deprecated Blowfish cipher (will be removed in next release)
+ * Deprecated SSH_BIND_OPTIONS_{RSA,ECDSA}KEY in favor of generic HOSTKEY
+ * Removed the usage of deprecated OpenSSL APIs (Note: Minimum supported
+ OpenSSL version is 1.1.1)
+ * Disabled preauth compression (zlib) by default
+ * Support for pkcs#11 engines are deprecated, pkcs11-provider is used instead
+ * Deprecation of old async SFTP API
+ * libgcrypt cryptographic backend is deprecated
+ * Deprecation of knownhosts hashing
+ * SFTP Improvements:
+ * Added support for async SFTP IO
+ * Added support for sftp_limits() and applied capping to SFTP read/write
+ operations accordingly
+ * Added sftp_home_directory() API support for sftp extension "home-directory"
+ * Added sftp_lsetstat() API for lsetstat extensions
+ * Added sftp_expand_path() to canonicalize path using expand-path@openssh.com
+ extension
+ * Implemented stat and realpath in sftpserver
+ * Added sftp_readlink() API to support hardlink@openssh.com
+ * New extensible callback based SFTP server
+ * Introduced the posix-rename@openssh.com extension
+ * New functions and features:
+ * Added support for PKCS #11 provider for OpenSSL 3.0
+ * Added testing for GSSAPI Authentication
+ * Implemented proxy jump using libssh
+ * Recategorized loglevels to show fatal errors and alignment with OpenSSH
+ log levels
+ * Added ssh_channel_request_pty_size_modes() API to set terminal modes for
+ PTYs
+ * Added function to check username syntax
+ * Added support to check all keys in authorized_keys instead of one in
+ example server implementation
+ * Handled hostkey similar to OpenSSH
+ * Added ssh_session_socket_close() API in order to not close socket passed
+ through options on error conditions
+ * Added option SSH_BIND_OPTIONS_IMPORT_KEY_STR to read user-supplied key
+ string in ssh_bind_options_set()
+ * Improved log handling around ssh_set_callbacks
+ * Added ssh_set_error_invalid in ssh_options_set()
+ * Prevented signature blob to start with 1 bit in libgcrypt
+ * Added support to unbreak key comparison of Ed25519 keys imported from PEM
+ or OpenSSH container
+ * Added support to calculate missing CRT parameters when building RSA key
+ * Added ssh_pki_export_privkey_base64_format() and
+ ssh_pki_export_privkey_file_format() to support exporting keys in different
+ formats (PEM, OpenSSH)
+ * Added support to compare certificates and handle automatic certificate
+ authentication
+ * Added support to make compile-commands generation conditional
+ * Built fuzzers for normal testing
+ * Avoided passing other events to callbacks when called recursively
+ * Added control master and path options
+ * Refactored channel_rcv_data, check for errors and report more useful errors
+ * Added support to connect to other host addresses than just the first one
+ * Terminated the server properly when the MaxAuthTries is reached
+ * Added support for no-more-sessions@openssh.com request in both client and
+ server
+ * Added callback to support forwarded-tcpip requests
+ * Bumped minimal CMake version to 3.12
+ * Added support for MBedTLS 3.6.x
+ * Added support for +,-,^ modifiers in front of algorithm lists in options
+ * Added callbacks for channel open response, and channel request response
+ * Replaced chroot() from chroot_wrapper internal library with chroot()
+ from priv_wrapper package
+ * Added a placeholder for non-expanded identities
+ * Improved handling of channel transfer window sizes
+
+version 0.10.6 (released 2023-12-18)
+ * Fix CVE-2023-6004: Command injection using proxycommand
+ * Fix CVE-2023-48795: Potential downgrade attack using strict kex
+ * Fix CVE-2023-6918: Missing checks for return values of MD functions
+ * Fix ssh_send_issue_banner() for CMD(PowerShell)
+ * Avoid passing other events to callbacks when poll is called recursively (#202)
+ * Allow @ in usernames when parsing from URI composes
+
+version 0.10.5 (released 2023-05-04)
+ * Fix CVE-2023-1667: a NULL dereference during rekeying with algorithm guessing
+ * Fix CVE-2023-2283: a possible authorization bypass in
+ pki_verify_data_signature under low-memory conditions.
+ * Fix several memory leaks in GSSAPI handling code
+ * Escape braces in ProxyCommand created from ProxyJump options for zsh
+ compatibility.
+ * Fix pkg-config path relocation for MinGW
+ * Improve doxygen documentation
+ * Fix build with cygwin due to the glob support
+ * Do not enqueue outgoing packets after sending SSH2_MSG_NEWKEYS
+ * Add support for SSH_SUPPRESS_DEPRECATED
+ * Avoid functions declarations without prototype to build with clang 15
+ * Fix spelling issues
+ * Avoid expanding KnownHosts, ProxyCommands and IdentityFiles repetitively
+ * Add support sk-* keys through configuration
+ * Improve checking for Argp library
+ * Log information about received extensions
+ * Correctly handle rekey with delayed compression
+ * Move the EC keys handling to OpenSSL 3.0 API
+ * Record peer disconnect message
+ * Avoid deadlock when write buffering occurs and we call poll recursively to
+ flush the output buffer
+ * Disable preauthentication compression by default
+ * Add CentOS 8 Stream / OpenSSL 1.1.1 to CI
+ * Add accidentally removed default compile flags
+ * Solve incorrect parsing of ProxyCommand option
+
+version 0.10.4 (released 2022-09-07)
+ * Fixed issues with KDF on big endian
+
+version 0.10.3 (released 2022-09-05)
+ * Fixed possible infinite loop in known hosts checking
+
+version 0.10.2 (released 2022-09-02)
+ * Fixed tilde expansion when handling include directives
+ * Fixed building the shared torture library
+ * Made rekey test more robust (fixes running on i586 build systems e.g koji)
+
+version 0.10.1 (released 2022-08-30)
+ * Fixed proxycommand support
+ * Fixed musl libc support
+
+version 0.10.0 (released 2022-08-26)
* Added support for OpenSSL 3.0
* Added support for mbedTLS 3
* Added support for Smart Cards (through openssl pkcs11 engine)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ba50f70..474437fd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.3.0)
-cmake_policy(SET CMP0048 NEW)
+cmake_minimum_required(VERSION 3.14.0)
# Specify search path for CMake modules to be loaded by include()
# and find_package()
@@ -10,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
include(DefineCMakeDefaults)
include(DefineCompilerFlags)
-project(libssh VERSION 0.10.90 LANGUAGES C CXX)
+project(libssh VERSION 0.12.00 LANGUAGES C)
# global needed variable
set(APPLICATION_NAME ${PROJECT_NAME})
@@ -22,7 +21,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
# Increment AGE. Set REVISION to 0
# If the source code was changed, but there were no interface changes:
# Increment REVISION.
-set(LIBRARY_VERSION "4.9.0")
+set(LIBRARY_VERSION "4.11.0")
set(LIBRARY_SOVERSION "4")
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
@@ -42,6 +41,8 @@ macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source buil
# Copy library files to a lib sub-directory
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
+set(LIBSSSH_PC_REQUIRES_PRIVATE "")
+
# search for libraries
if (WITH_ZLIB)
find_package(ZLIB REQUIRED)
@@ -49,32 +50,12 @@ endif (WITH_ZLIB)
if (WITH_GCRYPT)
find_package(GCrypt 1.5.0 REQUIRED)
- if (NOT GCRYPT_FOUND)
- message(FATAL_ERROR "Could not find GCrypt")
- endif (NOT GCRYPT_FOUND)
+ message(WARNING "libgcrypt cryptographic backend is deprecated and will be removed in future releases.")
elseif(WITH_MBEDTLS)
find_package(MbedTLS REQUIRED)
- if (NOT MBEDTLS_FOUND)
- message(FATAL_ERROR "Could not find mbedTLS")
- endif (NOT MBEDTLS_FOUND)
-else (WITH_GCRYPT)
- find_package(OpenSSL 1.1.1)
- if (OPENSSL_FOUND)
- # On CMake < 3.16, OPENSSL_CRYPTO_LIBRARIES is usually a synonym for OPENSSL_CRYPTO_LIBRARY, but is not defined
- # when building on Windows outside of Cygwin. We provide the synonym here, if FindOpenSSL didn't define it already.
- if (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
- set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- endif (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
- else (OPENSSL_FOUND)
- find_package(GCrypt)
- if (NOT GCRYPT_FOUND)
- find_package(MbedTLS)
- if (NOT MBEDTLS_FOUND)
- message(FATAL_ERROR "Could not find OpenSSL, GCrypt or mbedTLS")
- endif (NOT MBEDTLS_FOUND)
- endif (NOT GCRYPT_FOUND)
- endif (OPENSSL_FOUND)
-endif(WITH_GCRYPT)
+else()
+ find_package(OpenSSL 1.1.1 REQUIRED)
+endif()
if (UNIT_TESTING)
find_package(CMocka REQUIRED)
@@ -87,6 +68,7 @@ find_package(Threads)
if (WITH_GSSAPI)
find_package(GSSAPI)
+ list(APPEND LIBSSH_PC_REQUIRES_PRIVATE ${GSSAPI_PC_REQUIRES})
endif (WITH_GSSAPI)
if (WITH_NACL)
@@ -96,9 +78,19 @@ if (WITH_NACL)
endif (NOT NACL_FOUND)
endif (WITH_NACL)
+if (WITH_FIDO2)
+ find_package(libfido2)
+ if (LIBFIDO2_FOUND)
+ set(HAVE_LIBFIDO2 ON)
+ else (LIBFIDO2_FOUND)
+ set(HAVE_LIBFIDO2 OFF)
+ message(WARNING "libfido2 was not found. Internal support for interacting with FIDO2/U2F devices using the USB HID protocol will not be available.")
+ endif (LIBFIDO2_FOUND)
+endif (WITH_FIDO2)
+
# Disable symbol versioning in non UNIX platforms
if (UNIX)
- find_package(ABIMap 0.3.1)
+ find_package(ABIMap 0.4.0)
else (UNIX)
set(WITH_SYMBOL_VERSIONING OFF)
endif (UNIX)
@@ -189,6 +181,10 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
set(ALLOW_ABI_BREAK "BREAK_ABI")
endif()
+ if (WITH_FINAL)
+ set(FINAL "FINAL")
+ endif()
+
# Target we can depend on in 'make dist'
set(_SYMBOL_TARGET "${PROJECT_NAME}.map")
@@ -201,7 +197,7 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION}
CURRENT_MAP ${MAP_PATH}
COPY_TO ${MAP_PATH}
- FINAL
+ ${FINAL}
${ALLOW_ABI_BREAK})
# Write the current version to the source
@@ -210,7 +206,8 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
# Coverage
-if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
+if (WITH_COVERAGE)
+ ENABLE_LANGUAGE(CXX)
include(CodeCoverage)
setup_target_for_coverage_lcov(
NAME "coverage"
@@ -221,19 +218,23 @@ if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
NAME "coverage_xml"
EXECUTABLE make test
DEPENDENCIES ssh tests)
-endif (CMAKE_BUILD_TYPE STREQUAL "Coverage")
+endif (WITH_COVERAGE)
add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source DEPENDS ${_SYMBOL_TARGET} VERBATIM)
-# Link compile database for clangd
-execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
- "${CMAKE_BINARY_DIR}/compile_commands.json"
- "${CMAKE_SOURCE_DIR}/compile_commands.json")
+get_directory_property(hasParent PARENT_DIRECTORY)
+if(NOT(hasParent))
+ # Link compile database for clangd if we are the master project
+ execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+ "${CMAKE_BINARY_DIR}/compile_commands.json"
+ "${CMAKE_SOURCE_DIR}/compile_commands.json")
+endif()
message(STATUS "********************************************")
message(STATUS "********** ${PROJECT_NAME} build options : **********")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+message(STATUS "Coverage: ${WITH_COVERAGE}")
message(STATUS "zlib support: ${WITH_ZLIB}")
message(STATUS "libgcrypt support: ${WITH_GCRYPT}")
message(STATUS "libmbedTLS support: ${WITH_MBEDTLS}")
@@ -243,13 +244,18 @@ message(STATUS "Server support : ${WITH_SERVER}")
message(STATUS "GSSAPI support : ${WITH_GSSAPI}")
message(STATUS "GEX support : ${WITH_GEX}")
message(STATUS "Support insecure none cipher and MAC : ${WITH_INSECURE_NONE}")
+message(STATUS "Support exec : ${WITH_EXEC}")
message(STATUS "Pcap debugging support : ${WITH_PCAP}")
message(STATUS "Build shared library: ${BUILD_SHARED_LIBS}")
message(STATUS "Unit testing: ${UNIT_TESTING}")
message(STATUS "Client code testing: ${CLIENT_TESTING}")
-message(STATUS "Blowfish cipher support: ${WITH_BLOWFISH_CIPHER}")
+message(STATUS "Blowfish cipher support: ${HAVE_BLOWFISH}")
message(STATUS "PKCS #11 URI support: ${WITH_PKCS11_URI}")
message(STATUS "With PKCS #11 provider support: ${WITH_PKCS11_PROVIDER}")
+message(STATUS "With FIDO2/U2F support: ${WITH_FIDO2}")
+if (WITH_FIDO2)
+ message(STATUS "With libfido2 (internal usb-hid support): ${HAVE_LIBFIDO2}")
+endif (WITH_FIDO2)
set(_SERVER_TESTING OFF)
if (WITH_SERVER)
set(_SERVER_TESTING ${SERVER_TESTING})
@@ -264,9 +270,15 @@ message(STATUS "Benchmarks: ${WITH_BENCHMARKS}")
message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}")
message(STATUS "Allow ABI break: ${WITH_ABI_BREAK}")
message(STATUS "Release is final: ${WITH_FINAL}")
+if (WITH_HERMETIC_USR)
+ message(STATUS "User global client config: ${USR_GLOBAL_CLIENT_CONFIG}")
+endif ()
message(STATUS "Global client config: ${GLOBAL_CLIENT_CONFIG}")
if (WITH_SERVER)
-message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}")
+ if (WITH_HERMETIC_USR)
+ message(STATUS "User global bind config: ${USR_GLOBAL_BIND_CONFIG}")
+ endif ()
+ message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}")
endif()
message(STATUS "********************************************")
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8e26015c..6c606b9c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -117,6 +117,52 @@ libssh Developer's Certificate of Origin for each patch, or inside each
patch. Just the sign-off message is all that is required once we've
received the initial email.
+## Continuous Integration
+
+Contributing patches through Merge Request workflow on Gitlab allows us to run
+various checks on various configuration as part of Gitlab CI. Unfortunately,
+some pipelines are slower (as they involve building dependencies) so the default
+timeout of 1 hour needs to be extended at least to 2 hours. This can be done in
+project settings of your libssh fork:
+
+https://docs.gitlab.com/ee/ci/pipelines/settings.html#set-a-limit-for-how-long-jobs-can-run
+
+Otherwise you will encounter errors like these, usually on visualstudio builds:
+
+```
+ERROR: Job failed: execution took longer than 1h0m0s seconds
+The script exceeded the maximum execution time set for the job
+```
+
+Note, that the built dependencies are cached so after successful build in your
+namespace, the rebuilds should be much faster.
+
+## Running GitLab CI locally (optional helper)
+
+For contributors working on CI, build system changes, or adding new CI jobs, it can be useful to run GitLab CI pipelines locally before pushing.
+
+libssh provides a small helper script based on `gitlab-ci-local` that can:
+
+- List all jobs defined in `.gitlab-ci.yml`
+- Run a specific job or the full pipeline locally
+- Automatically pick up new jobs when they are added to the CI configuration
+- Optionally clean up CI Docker images after execution
+
+### Requirements
+
+- Docker (daemon running)
+- git
+- gitlab-ci-local
+ https://github.com/firecow/gitlab-ci-local
+
+### Usage
+
+```bash
+./.gitlab-ci/local-ci.sh --list
+./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64
+./.gitlab-ci/local-ci.sh --all
+./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64 --clean
+```
# Coding conventions in the libssh tree
@@ -517,6 +563,37 @@ Bad example:
break;
}
+## ABI Versioning and Symbol Management
+
+To maintain [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) stability
+and ensure backward compatibility, libssh uses **symbol versioning** to track and manage
+exported functions and variables. This allows libssh to introduce new symbols or modify
+existing functions in an ABI-compatible way.
+
+When introducing a new symbol:
+
+1. Use the `LIBSSH_API` macro to mark the symbol as part of the public API.
+2. If you have [abimap](https://github.com/ansasaki/abimap) installed, the new symbols are
+automatically generated in the `src/libssh_dev.map` file in the **build** directory and used automatically for building the updated library. But, depending on the version of `abimap` under use, you may face linker errors like: `unable to find version dependency LIBSSH_4_9_0`. In this case, you need to manually replace the existing `src/libssh.map` file with the generated `libssh_dev.map` file to update the symbol versioning.
+3. If you do not have abimap installed, the modified/added symbols must manually be added to the
+`src/libssh.map` file. The symbols must be added in the following format (assuming that 4_10_0 is the latest released version):
+
+```
+LIBSSH_AFTER_4_10_0
+{
+ global:
+ new_function;
+ new_variable;
+} LIBSSH_4_10_0;
+```
+4. After following either of the above steps, the library can be successfully built and
+tested without any linker errors.
+
+5. When submitting the patch, make sure that any new symbols have been added to `libssh.map` as described in step 3, so that the new additions may not be excluded from the next release due to human error.
+
+Also, to maintain ABI compatibility, existing symbols must not be removed. Instead, they can
+be marked as deprecated using the `LIBSSH_DEPRECATED` macro. This allows the symbol to be
+removed in a future release without breaking the ABI.
Have fun and happy libssh hacking!
diff --git a/CPackConfig.cmake b/CPackConfig.cmake
index 5bd52c56..81ed6d06 100644
--- a/CPackConfig.cmake
+++ b/CPackConfig.cmake
@@ -10,7 +10,7 @@ set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
# SOURCE GENERATOR
set(CPACK_SOURCE_GENERATOR "TXZ")
-set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git/;/[.]clangd/;/[.]cache/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
+set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]bare/;/[.]git/;/[.]git;/[.]clangd/;/[.]cache/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
### NSIS INSTALLER
diff --git a/CompilerChecks.cmake b/CompilerChecks.cmake
index 7a25b592..e9890e2f 100644
--- a/CompilerChecks.cmake
+++ b/CompilerChecks.cmake
@@ -16,7 +16,6 @@ if (UNIX)
endif()
endif()
- add_c_compiler_flag("-std=gnu99" SUPPORTED_COMPILER_FLAGS)
add_c_compiler_flag("-Wpedantic" SUPPORTED_COMPILER_FLAGS)
add_c_compiler_flag("-Wall" SUPPORTED_COMPILER_FLAGS)
add_c_compiler_flag("-Wshadow" SUPPORTED_COMPILER_FLAGS)
@@ -49,6 +48,7 @@ if (UNIX)
add_c_compiler_flag("-Werror=implicit-int" SUPPORTED_COMPILER_FLAGS)
add_c_compiler_flag("-Wint-conversion" SUPPORTED_COMPILER_FLAGS)
add_c_compiler_flag("-Werror=int-conversion" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Werror=unused-variable" SUPPORTED_COMPILER_FLAGS)
check_c_compiler_flag("-Wformat" REQUIRED_FLAGS_WFORMAT)
if (REQUIRED_FLAGS_WFORMAT)
@@ -92,9 +92,12 @@ if (UNIX)
endif (WITH_STACK_PROTECTOR_STRONG)
if (NOT WINDOWS AND NOT CYGWIN)
- check_c_compiler_flag_ssp("-fstack-clash-protection" WITH_STACK_CLASH_PROTECTION)
- if (WITH_STACK_CLASH_PROTECTION)
- list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-clash-protection")
+ # apple m* chips do not support this option
+ if (NOT ${CMAKE_SYSTEM_PROCESSOR} STREQUAL arm64)
+ check_c_compiler_flag_ssp("-fstack-clash-protection" WITH_STACK_CLASH_PROTECTION)
+ if (WITH_STACK_CLASH_PROTECTION)
+ list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-clash-protection")
+ endif()
endif()
endif()
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 334695f1..4e32ea53 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -64,6 +64,7 @@ check_include_file(arpa/inet.h HAVE_ARPA_INET_H)
check_include_file(byteswap.h HAVE_BYTESWAP_H)
check_include_file(glob.h HAVE_GLOB_H)
check_include_file(valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H)
+check_include_file(ifaddrs.h HAVE_IFADDRS_H)
if (WIN32)
check_include_file(io.h HAVE_IO_H)
@@ -77,51 +78,38 @@ endif (WIN32)
if (OPENSSL_FOUND)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ set(CMAKE_REQUIRED_LIBRARIES OpenSSL::Crypto)
+
check_include_file(openssl/des.h HAVE_OPENSSL_DES_H)
if (NOT HAVE_OPENSSL_DES_H)
message(FATAL_ERROR "Could not detect openssl/des.h")
endif()
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/aes.h HAVE_OPENSSL_AES_H)
if (NOT HAVE_OPENSSL_AES_H)
message(FATAL_ERROR "Could not detect openssl/aes.h")
endif()
if (WITH_BLOWFISH_CIPHER)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- check_include_file(openssl/blowfish.h HAVE_OPENSSL_BLOWFISH_H)
+ check_include_file(openssl/blowfish.h HAVE_BLOWFISH)
endif()
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ecdh.h HAVE_OPENSSL_ECDH_H)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ec.h HAVE_OPENSSL_EC_H)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ecdsa.h HAVE_OPENSSL_ECDSA_H)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_KDF_CTX_new_id HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_KDF_CTX_new HAVE_OPENSSL_EVP_KDF_CTX_NEW)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(FIPS_mode HAVE_OPENSSL_FIPS_MODE)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
+ # Check for ML-KEM availability (OpenSSL 3.5+)
+ if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
+ set(HAVE_OPENSSL_MLKEM 1)
+ set(HAVE_MLKEM1024 1)
+ endif ()
+
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
endif()
@@ -152,6 +140,7 @@ check_function_exists(strncpy HAVE_STRNCPY)
check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(strtoull HAVE_STRTOULL)
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
+check_function_exists(memset_explicit HAVE_MEMSET_EXPLICIT)
check_function_exists(memset_s HAVE_MEMSET_S)
if (HAVE_GLOB_H)
@@ -244,7 +233,12 @@ if (GCRYPT_FOUND)
endif (GCRYPT_VERSION VERSION_GREATER "1.4.6")
if (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
set(HAVE_GCRYPT_CHACHA_POLY 1)
+ set(HAVE_GCRYPT_CURVE25519 1)
endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
+ if (GCRYPT_VERSION VERSION_GREATER_EQUAL "1.10.1")
+ set(HAVE_GCRYPT_MLKEM 1)
+ set(HAVE_MLKEM1024 1)
+ endif ()
endif (GCRYPT_FOUND)
if (MBEDTLS_FOUND)
@@ -254,6 +248,17 @@ if (MBEDTLS_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${MBEDTLS_INCLUDE_DIR}/mbedtls")
check_include_file(chacha20.h HAVE_MBEDTLS_CHACHA20_H)
check_include_file(poly1305.h HAVE_MBEDTLS_POLY1305_H)
+ if (MBEDTLS_VERSION VERSION_LESS "3.0.0")
+ check_symbol_exists(MBEDTLS_ECP_DP_CURVE25519_ENABLED "config.h" HAVE_MBEDTLS_CURVE25519)
+ else()
+ check_symbol_exists(MBEDTLS_ECP_DP_CURVE25519_ENABLED "mbedtls_config.h" HAVE_MBEDTLS_CURVE25519)
+ endif()
+
+
+ if (WITH_BLOWFISH_CIPHER)
+ check_include_file(blowfish.h HAVE_BLOWFISH)
+ endif()
+
unset(CMAKE_REQUIRED_INCLUDES)
endif (MBEDTLS_FOUND)
@@ -471,6 +476,7 @@ if (WITH_PKCS11_URI)
if (NOT PKCS11_PROVIDER)
set(WITH_PKCS11_PROVIDER 0)
message(WARNING "Could not find pkcs11 provider! Falling back to engines")
+ message(WARNING "The support for engines is deprecated in OpenSSL and will be removed from libssh in the future releases.")
endif (NOT PKCS11_PROVIDER)
endif ()
endif()
diff --git a/DefineOptions.cmake b/DefineOptions.cmake
index 7401e86d..f788dec5 100644
--- a/DefineOptions.cmake
+++ b/DefineOptions.cmake
@@ -5,26 +5,45 @@ option(WITH_SERVER "Build with SSH server support" ON)
option(WITH_DEBUG_CRYPTO "Build with crypto debug output" OFF)
option(WITH_DEBUG_PACKET "Build with packet debug output" OFF)
option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON)
-option(WITH_GCRYPT "Compile against libgcrypt" OFF)
+option(WITH_GCRYPT "Compile against libgcrypt (deprecated)" OFF)
option(WITH_MBEDTLS "Compile against libmbedtls" OFF)
option(WITH_BLOWFISH_CIPHER "Compile with blowfish support" OFF)
option(WITH_PCAP "Compile with Pcap generation support" ON)
option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF)
-option(WITH_PKCS11_PROVIDER "Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
+option(WITH_PKCS11_PROVIDER
+ "Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
+option(WITH_FIDO2 "Build with FIDO2/U2F support" OFF)
option(UNIT_TESTING "Build with unit tests" OFF)
option(CLIENT_TESTING "Build with client tests; requires openssh" OFF)
-option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" OFF)
-option(WITH_BENCHMARKS "Build benchmarks tools; enables unit testing and client tests" OFF)
+option(SERVER_TESTING "Build with server tests; requires openssh and dropbear"
+ OFF)
+option(
+ GSSAPI_TESTING
+ "Build with GSSAPI tests; requires krb5-server,krb5-libs and krb5-workstation"
+ OFF)
+option(WITH_BENCHMARKS
+ "Build benchmarks tools; enables unit testing and client tests" OFF)
option(WITH_EXAMPLES "Build examples" ON)
option(WITH_NACL "Build with libnacl (curve25519)" ON)
option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
option(WITH_ABI_BREAK "Allow ABI break" OFF)
option(WITH_GEX "Enable DH Group exchange mechanisms" ON)
-option(WITH_INSECURE_NONE "Enable insecure none cipher and MAC algorithms (not suitable for production!)" OFF)
-option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF)
+option(
+ WITH_INSECURE_NONE
+ "Enable insecure none cipher and MAC algorithms (not suitable for production!)"
+ OFF)
+option(
+ WITH_EXEC
+ "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)."
+ ON)
+option(
+ FUZZ_TESTING
+ "Build with fuzzer for the server and client (automatically enables none cipher!)"
+ OFF)
option(PICKY_DEVELOPER "Build with picky developer flags" OFF)
+option(WITH_HERMETIC_USR "Build with support for hermetic /usr/" OFF)
if (WITH_ZLIB)
set(WITH_LIBZ ON)
@@ -33,30 +52,58 @@ else (WITH_ZLIB)
endif (WITH_ZLIB)
if (WITH_BENCHMARKS)
- set(UNIT_TESTING ON)
- set(CLIENT_TESTING ON)
-endif()
+ set(UNIT_TESTING ON)
+ set(CLIENT_TESTING ON)
+endif ()
-if (UNIT_TESTING OR CLIENT_TESTING OR SERVER_TESTING)
- set(BUILD_STATIC_LIB ON)
-endif()
+if (UNIT_TESTING
+ OR CLIENT_TESTING
+ OR SERVER_TESTING
+ OR GSSAPI_TESTING)
+ set(BUILD_STATIC_LIB ON)
+endif ()
if (WITH_NACL)
- set(WITH_NACL ON)
+ set(WITH_NACL ON)
endif (WITH_NACL)
if (WITH_ABI_BREAK)
- set(WITH_SYMBOL_VERSIONING ON)
+ set(WITH_SYMBOL_VERSIONING ON)
endif (WITH_ABI_BREAK)
+set(GLOBAL_CONF_DIR "/etc/ssh")
+if (WIN32)
+ # Use PROGRAMDATA on Windows
+ if (DEFINED ENV{PROGRAMDATA})
+ set(GLOBAL_CONF_DIR "$ENV{PROGRAMDATA}/ssh")
+ else ()
+ set(GLOBAL_CONF_DIR "C:/ProgramData/ssh")
+ endif ()
+ if (WITH_HERMETIC_USR)
+ set(USR_GLOBAL_CONF_DIR "/usr${GLOBAL_CONF_DIR}")
+ endif ()
+endif ()
+
if (NOT GLOBAL_BIND_CONFIG)
- set(GLOBAL_BIND_CONFIG "/etc/ssh/libssh_server_config")
+ set(GLOBAL_BIND_CONFIG "${GLOBAL_CONF_DIR}/libssh_server_config")
+
+ if (WITH_HERMETIC_USR)
+ set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
+ endif ()
endif (NOT GLOBAL_BIND_CONFIG)
if (NOT GLOBAL_CLIENT_CONFIG)
- set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config")
+ set(GLOBAL_CLIENT_CONFIG "${GLOBAL_CONF_DIR}/ssh_config")
+
+ if (WITH_HERMETIC_USR)
+ set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
+ endif ()
endif (NOT GLOBAL_CLIENT_CONFIG)
if (FUZZ_TESTING)
- set(WITH_INSECURE_NONE ON)
+ set(WITH_INSECURE_NONE ON)
endif (FUZZ_TESTING)
+
+if (WIN32)
+ set(WITH_EXEC 0)
+endif (WIN32)
diff --git a/INSTALL b/INSTALL
index 7ba53c50..a51914cc 100644
--- a/INSTALL
+++ b/INSTALL
@@ -7,11 +7,13 @@
In order to build libssh, you need to install several components:
- A C compiler
-- [CMake](https://www.cmake.org) >= 3.3.0
-- [openssl](https://www.openssl.org) >= 1.0.1
-or
-- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4
+- [CMake](https://www.cmake.org) >= 3.12.0
- [libz](https://www.zlib.net) >= 1.2
+- [openssl](https://www.openssl.org) >= 1.1.1
+or
+- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.5
+or
+- [Mbed TLS](https://www.trustedfirmware.org/projects/mbed-tls/)
optional:
- [cmocka](https://cmocka.org/) >= 1.1.0
@@ -36,14 +38,16 @@ First, you need to configure the compilation, using CMake. Go inside the
`build` dir. Create it if it doesn't exist.
GNU/Linux, MacOS X, MSYS/MinGW:
-
- cmake -DUNIT_TESTING=ON -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
+ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
make
On Windows you should choose a makefile generator with -G or use
cmake-gui.exe ..
+To enable building tests use -DUNIT_TESTING=ON. For this, the
+[cmocka](https://cmocka.org) dependency is required.
+
To enable additional client tests against a local OpenSSH server, add the
compile option -DCLIENT_TESTING=ON. These tests require an OpenSSH
server package and some wrapper libraries (see optional requirements) to
diff --git a/cmake/Modules/AddCMockaTest.cmake b/cmake/Modules/AddCMockaTest.cmake
index 4b0c2dad..f49961ba 100644
--- a/cmake/Modules/AddCMockaTest.cmake
+++ b/cmake/Modules/AddCMockaTest.cmake
@@ -116,5 +116,10 @@ function(ADD_CMOCKA_TEST _TARGET_NAME)
add_test(${_TARGET_NAME}
${TARGET_SYSTEM_EMULATOR} ${_TARGET_NAME}
)
+ if (WITH_COVERAGE)
+ ENABLE_LANGUAGE(CXX)
+ include(CodeCoverage)
+ append_coverage_compiler_flags_to_target(${_TARGET_NAME})
+ endif (WITH_COVERAGE)
endfunction (ADD_CMOCKA_TEST)
diff --git a/cmake/Modules/CodeCoverage.cmake b/cmake/Modules/CodeCoverage.cmake
index 3cf81b98..0fd70ae2 100644
--- a/cmake/Modules/CodeCoverage.cmake
+++ b/cmake/Modules/CodeCoverage.cmake
@@ -83,6 +83,10 @@
# - Change gcovr output from -o for --xml and --html output respectively.
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
#
+# 2022-09-28, Sebastian Mueller
+# - fix append_coverage_compiler_flags_to_target to correctly add flags
+# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent)
+#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
@@ -147,30 +151,34 @@ if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
+# Check supported compiler (Clang, GNU and Flang)
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
-list(GET LANGUAGES 0 LANG)
-
-if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
+foreach(LANG ${LANGUAGES})
+ if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
- message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
- endif()
-elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
- if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
- # Do nothing; exit conditional without error if true
- elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
- # Do nothing; exit conditional without error if true
- else()
- message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+ message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
-endif()
+ elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU"
+ AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang")
+ message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...")
+ endif()
+endforeach()
-set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
+set(COVERAGE_COMPILER_FLAGS "-g --coverage -fprofile-update=atomic"
CACHE INTERNAL "")
+
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
- check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
- if(HAVE_fprofile_abs_path)
- set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
+ check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
+ if(HAVE_cxx_fprofile_abs_path)
+ set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
+ endif()
+endif()
+if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
+ include(CheckCCompilerFlag)
+ check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
+ if(HAVE_c_fprofile_abs_path)
+ set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
@@ -202,7 +210,7 @@ mark_as_advanced(
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(NOT (CMAKE_BUILD_TYPE STREQUAL "Coverage" OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
+if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
@@ -228,7 +236,7 @@ endif()
# )
function(setup_target_for_coverage_lcov)
- set(options NO_DEMANGLE)
+ set(options NO_DEMANGLE SONARQUBE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
@@ -298,6 +306,18 @@ function(setup_target_for_coverage_lcov)
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
+ if(${Coverage_SONARQUBE})
+ # Generate SonarQube output
+ set(GCOVR_XML_CMD
+ ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
+ ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
+ )
+ set(GCOVR_XML_CMD_COMMAND
+ COMMAND ${GCOVR_XML_CMD}
+ )
+ set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml)
+ set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.")
+ endif()
if(CODE_COVERAGE_VERBOSE)
@@ -329,6 +349,12 @@ function(setup_target_for_coverage_lcov)
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
+
+ if(${Coverage_SONARQUBE})
+ message(STATUS "Command to generate SonarQube XML output: ")
+ string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
+ message(STATUS "${GCOVR_XML_CMD_SPACED}")
+ endif()
endif()
# Setup target
@@ -340,6 +366,7 @@ function(setup_target_for_coverage_lcov)
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
+ ${GCOVR_XML_CMD_COMMAND}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
@@ -347,6 +374,7 @@ function(setup_target_for_coverage_lcov)
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
+ ${GCOVR_XML_CMD_BYPRODUCTS}
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
@@ -358,6 +386,7 @@ function(setup_target_for_coverage_lcov)
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
+ ${GCOVR_XML_CMD_COMMENT}
)
# Show info where to find the report
@@ -621,7 +650,6 @@ function(setup_target_for_coverage_fastcov)
--process-gcno
--output ${Coverage_NAME}.json
--exclude ${FASTCOV_EXCLUDES}
- --exclude ${FASTCOV_EXCLUDES}
)
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
@@ -714,7 +742,9 @@ endfunction() # append_coverage_compiler_flags
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
- target_compile_options(${name}
- PRIVATE ${COVERAGE_COMPILER_FLAGS})
+ separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
+ target_compile_options(${name} PRIVATE ${_flag_list})
+ if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+ target_link_libraries(${name} PRIVATE gcov)
+ endif()
endfunction()
-
diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake
index c6c07ede..39378a10 100644
--- a/cmake/Modules/DefineCompilerFlags.cmake
+++ b/cmake/Modules/DefineCompilerFlags.cmake
@@ -46,16 +46,4 @@ if (UNIX AND NOT WIN32)
CACHE STRING "Flags used by the linker during the creation of shared libraries during UNDEFINEDSANITIZER builds.")
set(CMAKE_EXEC_LINKER_FLAGS_UNDEFINEDSANITIZER "-fsanitize=undefined"
CACHE STRING "Flags used by the linker during UNDEFINEDSANITIZER builds.")
-
- # Activate with: -DCMAKE_BUILD_TYPE=Coverage
- set(CMAKE_C_FLAGS_COVERAGE "-O0 -g -fprofile-arcs -ftest-coverage"
- CACHE STRING "Flags used by the C compiler during Coverage builds.")
- set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -g -fprofile-arcs -ftest-coverage"
- CACHE STRING "Flags used by the CXX compiler during Coverage builds.")
- set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage"
- CACHE STRING "Flags used by the linker during the creation of shared libraries during Coverage builds.")
- set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage"
- CACHE STRING "Flags used by the linker during the creation of shared libraries during Coverage builds.")
- set(CMAKE_EXEC_LINKER_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage"
- CACHE STRING "Flags used by the linker during Coverage builds.")
endif()
diff --git a/cmake/Modules/ExtractSymbols.cmake b/cmake/Modules/ExtractSymbols.cmake
index f7829334..95c850b6 100644
--- a/cmake/Modules/ExtractSymbols.cmake
+++ b/cmake/Modules/ExtractSymbols.cmake
@@ -50,15 +50,28 @@ file(READ ${HEADERS_LIST_FILE} HEADERS_LIST)
set(symbols)
foreach(header ${HEADERS_LIST})
+ file(READ ${header} header_content)
# Filter only lines containing the FILTER_PATTERN
- file(STRINGS ${header} contain_filter
- REGEX "^.*${FILTER_PATTERN}.*[(]"
+ # separated from the function name with one optional newline
+ string(REGEX MATCHALL
+ "${FILTER_PATTERN}[^(\n]*\n?[^(\n]*[(]"
+ contain_filter
+ "${header_content}"
+ )
+
+ # Remove the optional newline now
+ string(REGEX REPLACE
+ "(.+)\n?(.*)"
+ "\\1\\2"
+ oneline
+ "${contain_filter}"
)
# Remove function-like macros
- foreach(line ${contain_filter})
- if (NOT ${line} MATCHES ".*#[ ]*define")
+ # and anything with two underscores that sounds suspicious
+ foreach(line ${oneline})
+ if (NOT ${line} MATCHES ".*(#[ ]*define|__)")
list(APPEND not_macro ${line})
endif()
endforeach()
diff --git a/cmake/Modules/FindABIMap.cmake b/cmake/Modules/FindABIMap.cmake
index 5117b498..e7f725d2 100644
--- a/cmake/Modules/FindABIMap.cmake
+++ b/cmake/Modules/FindABIMap.cmake
@@ -220,13 +220,12 @@
# Search for python which is required
if (ABIMap_FIND_REQURIED)
- find_package(PythonInterp REQUIRED)
+ find_package(Python REQUIRED)
else()
- find_package(PythonInterp)
+ find_package(Python)
endif()
-
-if (PYTHONINTERP_FOUND)
+if (TARGET Python::Interpreter)
# Search for abimap tool used to generate the map files
find_program(ABIMAP_EXECUTABLE NAMES abimap DOC "path to the abimap executable")
mark_as_advanced(ABIMAP_EXECUTABLE)
diff --git a/cmake/Modules/FindGCrypt.cmake b/cmake/Modules/FindGCrypt.cmake
index b1f73d81..e28cb846 100644
--- a/cmake/Modules/FindGCrypt.cmake
+++ b/cmake/Modules/FindGCrypt.cmake
@@ -39,6 +39,15 @@ find_path(GCRYPT_INCLUDE_DIR
include
)
+find_path(GCRYPT_ERROR_INCLUDE_DIR
+ NAMES
+ gpg-error.h
+ HINTS
+ ${_GCRYPT_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES
+ include
+)
+
find_library(GCRYPT_LIBRARY
NAMES
gcrypt
@@ -56,8 +65,10 @@ find_library(GCRYPT_ERROR_LIBRARY
libgpg-error6-0
HINTS
${_GCRYPT_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES
+ lib
)
-set(GCRYPT_LIBRARIES ${GCRYPT_LIBRARY} ${GCRYPT_ERROR_LIBRARY})
+set(GCRYPT_LIBRARIES ${GCRYPT_ERROR_LIBRARY} ${GCRYPT_LIBRARY})
if (GCRYPT_INCLUDE_DIR)
file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" _gcrypt_version_str REGEX "^#define GCRYPT_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]")
@@ -83,5 +94,25 @@ else (GCRYPT_VERSION)
GCRYPT_LIBRARIES)
endif (GCRYPT_VERSION)
-# show the GCRYPT_INCLUDE_DIRS and GCRYPT_LIBRARIES variables only in the advanced view
-mark_as_advanced(GCRYPT_INCLUDE_DIR GCRYPT_LIBRARIES)
+# show the GCRYPT_INCLUDE_DIRS, GCRYPT_LIBRARIES and GCRYPT_ERROR_INCLUDE_DIR variables only in the advanced view
+mark_as_advanced(GCRYPT_INCLUDE_DIR GCRYPT_ERROR_INCLUDE_DIR GCRYPT_LIBRARIES)
+
+if(GCRYPT_FOUND)
+ if(NOT TARGET libgcrypt::libgcrypt)
+ add_library(libgcrypt::libgcrypt UNKNOWN IMPORTED)
+ set_target_properties(libgcrypt::libgcrypt PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${GCRYPT_INCLUDE_DIR}"
+ INTERFACE_LINK_LIBRARIES libgcrypt::libgcrypt
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${GCRYPT_LIBRARY}")
+ endif()
+
+ if(NOT TARGET libgpg-error::libgpg-error)
+ add_library(libgpg-error::libgpg-error UNKNOWN IMPORTED)
+ set_target_properties(libgpg-error::libgpg-error PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${GCRYPT_ERROR_INCLUDE_DIR}"
+ INTERFACE_LINK_LIBRARIES libgpg-error::libgpg-error
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${GCRYPT_ERROR_LIBRARY}")
+ endif()
+endif()
diff --git a/cmake/Modules/FindGSSAPI.cmake b/cmake/Modules/FindGSSAPI.cmake
index d227d8dd..630d7c16 100644
--- a/cmake/Modules/FindGSSAPI.cmake
+++ b/cmake/Modules/FindGSSAPI.cmake
@@ -11,6 +11,8 @@
# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
# GSSAPI_LIBRARIES - Link these to use GSSAPI
# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
+# GSSAPI_PC_REQUIRES - pkg-config module name if found, needed for
+# Requires.private for static linking
#
#=============================================================================
# Copyright (c) 2013 Andreas Schneider
@@ -24,12 +26,23 @@
#=============================================================================
#
+set(_mit_modname "mit-krb5-gssapi")
+set(_heimdal_modname "heimdal-gssapi")
+
+if(NOT _GSSAPI_ROOT_HINTS AND NOT _GSSAPI_ROOT_PATHS)
+ find_package(PkgConfig QUIET)
+ if (PKG_CONFIG_FOUND)
+ pkg_search_module(_GSSAPI ${_mit_modname} ${_heimdal_modname})
+ endif()
+endif()
+
find_path(GSSAPI_ROOT_DIR
NAMES
include/gssapi.h
include/gssapi/gssapi.h
HINTS
${_GSSAPI_ROOT_HINTS}
+ "${_GSSAPI_INCLUDEDIR}"
PATHS
${_GSSAPI_ROOT_PATHS}
)
@@ -317,9 +330,15 @@ endif (GSSAPI_FLAVOR_HEIMDAL)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
-if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
- set(GSSAPI_FOUND TRUE)
-endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
+if(GSSAPI_FOUND)
+ if(_GSSAPI_FOUND) # via pkg-config
+ if (GSSAPI_FLAVOR_MIT)
+ set(GSSAPI_PC_REQUIRES ${_mit_modname})
+ elseif (GSSAPI_FLAVOR_HEIMDAL)
+ set(GSSAPI_PC_REQUIRES ${_heimdal_modname})
+ endif()
+ endif()
+endif()
-# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
-mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
+# show the GSSAPI_INCLUDE_DIR and GSSAPI_LIBRARIES variables only in the advanced view
+mark_as_advanced(GSSAPI_INCLUDE_DIR GSSAPI_LIBRARIES)
diff --git a/cmake/Modules/FindMbedTLS.cmake b/cmake/Modules/FindMbedTLS.cmake
index baec8adc..7c5379c6 100644
--- a/cmake/Modules/FindMbedTLS.cmake
+++ b/cmake/Modules/FindMbedTLS.cmake
@@ -34,7 +34,7 @@ set(_MBEDTLS_ROOT_HINTS_AND_PATHS
find_path(MBEDTLS_INCLUDE_DIR
NAMES
- mbedtls/config.h
+ mbedtls/ssl.h
HINTS
${_MBEDTLS_ROOT_HINTS_AND_PATHS}
PATH_SUFFIXES
@@ -72,13 +72,23 @@ find_library(MBEDTLS_X509_LIBRARY
set(MBEDTLS_LIBRARIES ${MBEDTLS_SSL_LIBRARY} ${MBEDTLS_CRYPTO_LIBRARY}
${MBEDTLS_X509_LIBRARY})
+# mbedtls 2.8
if (MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h")
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" _mbedtls_version_str REGEX
"^#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"[0-9]+.[0-9]+.[0-9]+\"")
- string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+.[0-9]+.[0-9]+).*"
+ string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"
"\\1" MBEDTLS_VERSION "${_mbedtls_version_str}")
-endif ()
+endif()
+
+# mbedtls 3.6
+if (NOT MBEDTLS_VERSION AND MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")
+ file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h" _mbedtls_version_str REGEX
+ "^#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"[0-9]+.[0-9]+.[0-9]+\"")
+
+ string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"
+ "\\1" MBEDTLS_VERSION "${_mbedtls_version_str}")
+endif()
include(FindPackageHandleStandardArgs)
if (MBEDTLS_VERSION)
@@ -93,8 +103,8 @@ if (MBEDTLS_VERSION)
in the system variable MBEDTLS_ROOT_DIR"
)
else (MBEDTLS_VERSION)
- find_package_handle_standard_args(MBedTLS
- "Could NOT find mbedTLS, try to set the path to mbedLS root folder in
+ find_package_handle_standard_args(MbedTLS
+ "Could NOT find mbedTLS, try to set the path to mbedTLS root folder in
the system variable MBEDTLS_ROOT_DIR"
MBEDTLS_INCLUDE_DIR
MBEDTLS_LIBRARIES)
@@ -102,3 +112,32 @@ endif (MBEDTLS_VERSION)
# show the MBEDTLS_INCLUDE_DIRS and MBEDTLS_LIBRARIES variables only in the advanced view
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES)
+
+if(MBEDTLS_FOUND)
+ if(NOT TARGET MbedTLS::mbedcrypto)
+ add_library(MbedTLS::mbedcrypto UNKNOWN IMPORTED)
+ set_target_properties(MbedTLS::mbedcrypto PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
+ INTERFACE_LINK_LIBRARIES MbedTLS::mbedcrypto
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${MBEDTLS_CRYPTO_LIBRARY}")
+ endif()
+
+ if(NOT TARGET MbedTLS::mbedx509)
+ add_library(MbedTLS::mbedx509 UNKNOWN IMPORTED)
+ set_target_properties(MbedTLS::mbedx509 PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
+ INTERFACE_LINK_LIBRARIES MbedTLS::mbedx509
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${MBEDTLS_X509_LIBRARY}")
+ endif()
+
+ if(NOT TARGET MbedTLS::mbedtls)
+ add_library(MbedTLS::mbedtls UNKNOWN IMPORTED)
+ set_target_properties(MbedTLS::mbedtls PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
+ INTERFACE_LINK_LIBRARIES MbedTLS::mbedtls
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${MBEDTLS_LIBRARY}")
+ endif()
+endif()
diff --git a/cmake/Modules/Findlibfido2.cmake b/cmake/Modules/Findlibfido2.cmake
new file mode 100644
index 00000000..7111a919
--- /dev/null
+++ b/cmake/Modules/Findlibfido2.cmake
@@ -0,0 +1,63 @@
+# - Try to find libfido2
+# Once done this will define
+#
+# LIBFIDO2_ROOT_DIR - Set this variable to the root installation of libfido2
+#
+# Read-Only variables:
+# LIBFIDO2_FOUND - system has libfido2
+# LIBFIDO2_INCLUDE_DIR - the libfido2 include directory
+# LIBFIDO2_LIBRARIES - Link these to use libfido2
+#
+# The libfido2 library provides support for communicating
+# with FIDO2/U2F devices over USB/NFC.
+#
+# Copyright (c) 2025 Praneeth Sarode
+#
+# Redistribution and use is allowed according to the terms of the New
+# BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+#
+
+
+set(_LIBFIDO2_ROOT_HINTS
+ $ENV{LIBFIDO2_ROOT_DIR}
+ ${LIBFIDO2_ROOT_DIR}
+)
+
+set(_LIBFIDO2_ROOT_PATHS
+ "$ENV{PROGRAMFILES}/libfido2"
+)
+
+set(_LIBFIDO2_ROOT_HINTS_AND_PATHS
+ HINTS ${_LIBFIDO2_ROOT_HINTS}
+ PATHS ${_LIBFIDO2_ROOT_PATHS}
+)
+
+find_path(LIBFIDO2_INCLUDE_DIR
+ NAMES
+ fido.h
+ HINTS
+ ${_LIBFIDO2_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES
+ include
+)
+
+find_library(LIBFIDO2_LIBRARY
+ NAMES
+ fido2
+ HINTS
+ ${_LIBFIDO2_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES
+ lib
+ lib64
+)
+
+set(LIBFIDO2_LIBRARIES
+ ${LIBFIDO2_LIBRARY}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(libfido2 DEFAULT_MSG LIBFIDO2_LIBRARIES LIBFIDO2_INCLUDE_DIR)
+
+# show the LIBFIDO2_INCLUDE_DIR and LIBFIDO2_LIBRARIES variables only in the advanced view
+mark_as_advanced(LIBFIDO2_INCLUDE_DIR LIBFIDO2_LIBRARIES)
diff --git a/config.h.cmake b/config.h.cmake
index 5d0afdd7..14e1031c 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -8,10 +8,16 @@
#cmakedefine BINARYDIR "${BINARYDIR}"
#cmakedefine SOURCEDIR "${SOURCEDIR}"
+/* Global configuration directory */
+#cmakedefine USR_GLOBAL_CONF_DIR "${USR_GLOBAL_CONF_DIR}"
+#cmakedefine GLOBAL_CONF_DIR "${GLOBAL_CONF_DIR}"
+
/* Global bind configuration file path */
+#cmakedefine USR_GLOBAL_BIND_CONFIG "${USR_GLOBAL_BIND_CONFIG}"
#cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}"
/* Global client configuration file path */
+#cmakedefine USR_GLOBAL_CLIENT_CONFIG "${USR_GLOBAL_CLIENT_CONFIG}"
#cmakedefine GLOBAL_CLIENT_CONFIG "${GLOBAL_CLIENT_CONFIG}"
/************************** HEADER FILES *************************/
@@ -58,15 +64,15 @@
/* Define to 1 if you have the header file. */
#cmakedefine HAVE_STDINT_H 1
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_IFADDRS_H 1
+
/* Define to 1 if you have the header file. */
#cmakedefine HAVE_OPENSSL_AES_H 1
/* Define to 1 if you have the header file. */
#cmakedefine HAVE_WSPIAPI_H 1
-/* Define to 1 if you have the header file. */
-#cmakedefine HAVE_OPENSSL_BLOWFISH_H 1
-
/* Define to 1 if you have the header file. */
#cmakedefine HAVE_OPENSSL_DES_H 1
@@ -85,18 +91,24 @@
/* Define to 1 if you have elliptic curve cryptography in openssl */
#cmakedefine HAVE_OPENSSL_ECC 1
+/* Define to 1 if mbedTLS supports curve25519 */
+#cmakedefine HAVE_MBEDTLS_CURVE25519 1
+
/* Define to 1 if you have elliptic curve cryptography in gcrypt */
#cmakedefine HAVE_GCRYPT_ECC 1
/* Define to 1 if you have elliptic curve cryptography */
#cmakedefine HAVE_ECC 1
-/* Define to 1 if you have gl_flags as a glob_t sturct member */
+/* Define to 1 if you have gl_flags as a glob_t struct member */
#cmakedefine HAVE_GLOB_GL_FLAGS_MEMBER 1
/* Define to 1 if you have gcrypt with ChaCha20/Poly1305 support */
#cmakedefine HAVE_GCRYPT_CHACHA_POLY 1
+/* Define to 1 if you have gcrypt with curve25519 support */
+#cmakedefine HAVE_GCRYPT_CURVE25519
+
/*************************** FUNCTIONS ***************************/
/* Define to 1 if you have the `EVP_chacha20' function. */
@@ -171,6 +183,9 @@
/* Define to 1 if you have the `explicit_bzero' function. */
#cmakedefine HAVE_EXPLICIT_BZERO 1
+/* Define to 1 if you have the `memset_explicit' function. */
+#cmakedefine HAVE_MEMSET_EXPLICIT 1
+
/* Define to 1 if you have the `memset_s' function. */
#cmakedefine HAVE_MEMSET_S 1
@@ -180,6 +195,18 @@
/* Define to 1 if you have the `cmocka_set_test_filter' function. */
#cmakedefine HAVE_CMOCKA_SET_TEST_FILTER 1
+/* Define to 1 if we have support for blowfish */
+#cmakedefine HAVE_BLOWFISH 1
+
+/* Define to 1 if we have support for ML-KEM in libgcrypt */
+#cmakedefine HAVE_GCRYPT_MLKEM 1
+
+/* Define to 1 if we have support for ML-KEM in OpenSSL */
+#cmakedefine HAVE_OPENSSL_MLKEM 1
+
+/* Define to 1 if we have support for ML-KEM1024 in either backend */
+#cmakedefine HAVE_MLKEM1024 1
+
/*************************** LIBRARIES ***************************/
/* Define to 1 if you have the `crypto' library (-lcrypto). */
@@ -197,6 +224,10 @@
/* Define to 1 if you have the `cmocka' library (-lcmocka). */
#cmakedefine HAVE_CMOCKA 1
+/* Define to 1 if you have the `libfido2' library (-lfido2).
+ * This is required for interacting with FIDO2/U2F devices over USB-HID. */
+#cmakedefine HAVE_LIBFIDO2 1
+
/**************************** OPTIONS ****************************/
#cmakedefine HAVE_GCC_THREAD_LOCAL_STORAGE 1
@@ -231,9 +262,14 @@
/* Define to 1 if you want to enable DH group exchange algorithms */
#cmakedefine WITH_GEX 1
-/* Define to 1 if you want to enable none cipher and MAC */
+/* Define to 1 if you want to enable insecure none cipher and MAC */
#cmakedefine WITH_INSECURE_NONE 1
+/* Define to 1 if you want to allow libssh to execute arbitrary commands from
+ * configuration files or options (match exec, proxy commands and OpenSSH-based
+ * proxy-jumps). */
+#cmakedefine WITH_EXEC 1
+
/* Define to 1 if you want to enable blowfish cipher support */
#cmakedefine WITH_BLOWFISH_CIPHER 1
@@ -258,6 +294,9 @@
/* Define to 1 if we want to build a support for PKCS #11 provider. */
#cmakedefine WITH_PKCS11_PROVIDER 1
+/* Define to 1 if you want to enable FIDO2/U2F support */
+#cmakedefine WITH_FIDO2 1
+
/*************************** ENDIAN *****************************/
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index fcc15b90..0e7a9521 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -1,90 +1,247 @@
#
# Build the documentation
#
-if (${CMAKE_VERSION} VERSION_GREATER "3.8.99")
-
+# To build the documentation with a local doxygen-awesome-css directory:
+#
+# cmake -S . -B obj \
+# -DDOXYGEN_AWESOME_CSS_DIR=/path/to/doxygen-awesome-css
+# cmake --build obj --target docs
+#
+# The tarball can be downloaded from:
+# https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz
+#
find_package(Doxygen)
if (DOXYGEN_FOUND)
+ set(DOXYGEN_AWESOME_CSS_PROJECT
+ "https://github.com/jothepro/doxygen-awesome-css")
+ set(DOXYGEN_AWESOME_CSS_VERSION "2.4.1")
+ set(DOXYGEN_AWESOME_CSS_URL
+ "${DOXYGEN_AWESOME_CSS_PROJECT}/archive/refs/tags/v${DOXYGEN_AWESOME_CSS_VERSION}.tar.gz"
+ )
+
+ # Allow specifying a local doxygen-awesome-css directory (useful for
+ # packaging)
+ if (NOT DEFINED DOXYGEN_AWESOME_CSS_DIR)
+ # Custom target to download doxygen-awesome-css at build time
+ add_custom_target(
+ doxygen-awesome-css
+ COMMAND
+ ${CMAKE_COMMAND} -DURL=${DOXYGEN_AWESOME_CSS_URL}
+ -DDEST_DIR=${CMAKE_CURRENT_BINARY_DIR}
+ -DVERSION=${DOXYGEN_AWESOME_CSS_VERSION} -P
+ ${CMAKE_CURRENT_SOURCE_DIR}/fetch_doxygen_awesome.cmake
+ COMMENT "Fetching doxygen-awesome-css theme")
+
+ set(AWESOME_CSS_DIR
+ "${CMAKE_CURRENT_BINARY_DIR}/doxygen-awesome-css-${DOXYGEN_AWESOME_CSS_VERSION}"
+ )
+ else ()
+ message(
+ STATUS
+ "Using doxygen-awesome-css from ${DOXYGEN_AWESOME_CSS_DIR}")
+ set(AWESOME_CSS_DIR "${DOXYGEN_AWESOME_CSS_DIR}")
+ endif ()
+
+ # Project title shown in documentation
set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME})
+ # Project version number shown in documentation
set(DOXYGEN_PROJECT_NUMBER ${PROJECT_VERSION})
+ # Brief description shown below project name
set(DOXYGEN_PROJECT_BRIEF "The SSH library")
+ # Project favicon (browser tab icon)
+ set(DOXYGEN_PROJECT_ICON ${CMAKE_CURRENT_SOURCE_DIR}/favicon.png)
+ # Number of spaces used for indentation in code blocks
set(DOXYGEN_TAB_SIZE 4)
+ # Generate output optimized for C (vs C++)
set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES)
+ # Enable parsing of markdown in comments
set(DOXYGEN_MARKDOWN_SUPPORT YES)
- set(DOXYGEN_FULL_PATH_NAMES NO)
+ # Warn about undocumented members to improve documentation quality
+ set(DOXYGEN_WARN_IF_UNDOCUMENTED YES)
+ # Do not extract private class members
+ set(DOXYGEN_EXTRACT_PRIVATE NO)
+ if (WITH_INTERNAL_DOC)
+ # Include internal documentation
+ set(DOXYGEN_INTERNAL_DOCS YES)
+ else ()
+ # Do not include internal documentation
+ set(DOXYGEN_INTERNAL_DOCS NO)
+ endif( WITH_INTERNAL_DOC)
+ # Disable built-in clipboard (using doxygen-awesome extension instead)
+ set(DOXYGEN_HTML_COPY_CLIPBOARD NO)
+ # Disable page outline panel (using interactive TOC extension instead)
+ set(DOXYGEN_PAGE_OUTLINE_PANEL NO)
+
+ # Required configuration for doxygen-awesome-css theme Generate treeview
+ # sidebar for navigation
+ set(DOXYGEN_GENERATE_TREEVIEW YES)
+ # Enable default index pages
+ set(DOXYGEN_DISABLE_INDEX NO)
+ # Use top navigation bar instead of full sidebar (required for theme
+ # compatibility)
+ set(DOXYGEN_FULL_SIDEBAR NO)
+ # Use light color style (required for Doxygen >= 1.9.5)
+ set(DOXYGEN_HTML_COLORSTYLE LIGHT)
- set(DOXYGEN_PREDEFINED DOXYGEN
- WITH_SERVER
- WITH_SFTP
+ # Disable diagram generation (not relevant for C projects)
+ set(DOXYGEN_HAVE_DOT NO)
+ set(DOXYGEN_CLASS_DIAGRAMS NO)
+ set(DOXYGEN_CALL_GRAPH NO)
+ set(DOXYGEN_CALLER_GRAPH NO)
+
+ # Preprocessor defines to use when parsing code
+ set(DOXYGEN_PREDEFINED DOXYGEN WITH_SERVER WITH_SFTP
PRINTF_ATTRIBUTE\(x,y\))
- set(DOXYGEN_DOT_GRAPH_MAX_NODES 100)
- set(DOXYGEN_EXCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/that_style)
- set(DOXYGEN_HTML_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/that_style/header.html)
- set(DOXYGEN_HTML_EXTRA_STYLESHEET ${CMAKE_CURRENT_SOURCE_DIR}/that_style/that_style.css)
- set(DOXYGEN_HTML_EXTRA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_left.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_right.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_inter.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/sync_off.png
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/sync_on.png
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/splitbar_handle.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/doc.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/mag_glass.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderclosed.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderopen.svg
- ${CMAKE_CURRENT_SOURCE_DIR}/that_style/js/striped_bg.js)
- set(DOXYGEN_EXCLUDE_PATTERNS */src/external/* fe25519.h ge25519.h sc25519.h
- blf.h)
- set(DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS chacha20_poly1305_keysched,dh_ctx,dh_ctx,dh_keypair,error_struct,
- packet_struct,pem_get_password_struct,ssh_tokens_st,
- sftp_attributes_struct,sftp_client_message_struct,
- sftp_dir_struct,sftp_ext_struct,sftp_file_struct,sftp_message_struct,
- sftp_packet_struct,sftp_request_queue_struct,sftp_session_struct,
- sftp_status_message_struct,ssh_agent_state_struct,
- ssh_agent_struct,ssh_auth_auto_state_struct,ssh_auth_request,
- ssh_bind_config_keyword_table_s,ssh_bind_config_match_keyword_table_s,
- ssh_bind_struct,ssh_buffer_struct,ssh_channel_callbacks_struct,
- ssh_channel_read_termination_struct,ssh_channel_request,
- ssh_channel_request_open,ssh_channel_struct,ssh_cipher_struct,
- ssh_common_struct,ssh_config_keyword_table_s,
- ssh_config_match_keyword_table_s,ssh_connector_struct,
- ssh_counter_struct,ssh_crypto_struct,ssh_event_fd_wrapper,
- ssh_event_struct,ssh_global_request,ssh_gssapi_struct,ssh_hmac_struct,
- ssh_iterator,ssh_kbdint_struct,ssh_kex_struct,ssh_key_struct,
- ssh_knownhosts_entry,ssh_list,ssh_mac_ctx_struct,ssh_message_struct,
- ssh_packet_callbacks_struct,ssh_packet_header,ssh_poll_ctx_struct,
- ssh_poll_handle_struct,ssh_pollfd_struct,ssh_private_key_struct,
- ssh_public_key_struct,ssh_scp_struct,ssh_service_request,
- ssh_session_struct,ssh_signature_struct,ssh_socket_struct,
- ssh_string_struct,ssh_threads_callbacks_struct,ssh_timestamp,)
- set(DOXYGEN_EXCLUDE_SYMBOLS_MACRO SSH_FXP*,SSH_SOCKET*,SERVERBANNER,SOCKOPT_TYPE_ARG4,SSH_FILEXFER*,
- SSH_FXF*,SSH_S_*,SFTP_*,NSS_BUFLEN_PASSWD,CLOCK,MAX_LINE_SIZE,
- PKCS11_URI,KNOWNHOSTS_MAXTYPES,)
- set(DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS sftp_attributes,sftp_client_message,sftp_dir,sftp_ext,sftp_file,
- sftp_message,sftp_packet,sftp_request_queue,sftp_session,
- sftp_status_message,sftp_statvfs_t,poll_fn,ssh_callback_int,
- ssh_callback_data,ssh_callback_int_int,ssh_message_callback,
- ssh_channel_callback_int,ssh_channel_callback_data,ssh_callbacks,
- ssh_gssapi_select_oid_callback,ssh_gssapi_accept_sec_ctx_callback,
- ssh_gssapi_verify_mic_callback,ssh_server_callbacks,ssh_socket_callbacks,
- ssh_packet_callbacks,ssh_channel_callbacks,ssh_bind,ssh_bind_callbacks,)
- set(DOXYGEN_EXCLUDE_SYMBOLS ${DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS}
- ${DOXYGEN_EXCLUDE_SYMBOLS_MACRO}
- ${DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS})
+ # Exclude patterns for files we don't want to document
+ set(DOXYGEN_EXCLUDE_PATTERNS */src/external/* fe25519.h ge25519.h sc25519.h
+ blf.h)
+ # Exclude internal structures from documentation
+ set(DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS
+ chacha20_poly1305_keysched,
+ dh_ctx,
+ dh_ctx,
+ dh_keypair,
+ error_struct,
+ packet_struct,
+ pem_get_password_struct,
+ ssh_tokens_st,
+ sftp_attributes_struct,
+ sftp_client_message_struct,
+ sftp_dir_struct,
+ sftp_ext_struct,
+ sftp_file_struct,
+ sftp_message_struct,
+ sftp_packet_struct,
+ sftp_request_queue_struct,
+ sftp_session_struct,
+ sftp_status_message_struct,
+ ssh_agent_state_struct,
+ ssh_agent_struct,
+ ssh_auth_auto_state_struct,
+ ssh_auth_request,
+ ssh_bind_config_keyword_table_s,
+ ssh_bind_config_match_keyword_table_s,
+ ssh_bind_struct,
+ ssh_buffer_struct,
+ ssh_channel_callbacks_struct,
+ ssh_channel_read_termination_struct,
+ ssh_channel_request,
+ ssh_channel_request_open,
+ ssh_channel_struct,
+ ssh_cipher_struct,
+ ssh_common_struct,
+ ssh_config_keyword_table_s,
+ ssh_config_match_keyword_table_s,
+ ssh_connector_struct,
+ ssh_counter_struct,
+ ssh_crypto_struct,
+ ssh_event_fd_wrapper,
+ ssh_event_struct,
+ ssh_global_request,
+ ssh_gssapi_struct,
+ ssh_hmac_struct,
+ ssh_iterator,
+ ssh_kbdint_struct,
+ ssh_kex_struct,
+ ssh_key_struct,
+ ssh_knownhosts_entry,
+ ssh_list,
+ ssh_mac_ctx_struct,
+ ssh_message_struct,
+ ssh_packet_callbacks_struct,
+ ssh_packet_header,
+ ssh_poll_ctx_struct,
+ ssh_poll_handle_struct,
+ ssh_pollfd_struct,
+ ssh_private_key_struct,
+ ssh_public_key_struct,
+ ssh_scp_struct,
+ ssh_service_request,
+ ssh_session_struct,
+ ssh_signature_struct,
+ ssh_socket_struct,
+ ssh_string_struct,
+ ssh_threads_callbacks_struct,
+ ssh_timestamp)
+ set(DOXYGEN_EXCLUDE_SYMBOLS_MACRO
+ SSH_FXP*,
+ SSH_SOCKET*,
+ SERVERBANNER,
+ SOCKOPT_TYPE_ARG4,
+ SSH_FILEXFER*,
+ SSH_FXF*,
+ SSH_S_*,
+ SFTP_*,
+ NSS_BUFLEN_PASSWD,
+ CLOCK,
+ MAX_LINE_SIZE,
+ PKCS11_URI,
+ KNOWNHOSTS_MAXTYPES)
+ set(DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS
+ sftp_attributes,
+ sftp_client_message,
+ sftp_dir,
+ sftp_ext,
+ sftp_file,
+ sftp_message,
+ sftp_packet,
+ sftp_request_queue,
+ sftp_status_message,
+ sftp_statvfs_t,
+ poll_fn,
+ ssh_callback_int,
+ ssh_callback_data,
+ ssh_callback_int_int,
+ ssh_message_callback,
+ ssh_channel_callback_int,
+ ssh_channel_callback_data,
+ ssh_callbacks,
+ ssh_gssapi_select_oid_callback,
+ ssh_gssapi_accept_sec_ctx_callback,
+ ssh_gssapi_verify_mic_callback,
+ ssh_server_callbacks,
+ ssh_socket_callbacks,
+ ssh_packet_callbacks,
+ ssh_channel_callbacks,
+ ssh_bind,
+ ssh_bind_callbacks)
+ set(DOXYGEN_EXCLUDE_SYMBOLS
+ ${DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS} ${DOXYGEN_EXCLUDE_SYMBOLS_MACRO}
+ ${DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS})
+
+ # Custom layout file to rename "Topics" to "API Reference" and simplify
+ # navigation
+ set(DOXYGEN_LAYOUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/DoxygenLayout.xml)
+ # Custom HTML header with doxygen-awesome extension initialization
+ set(DOXYGEN_HTML_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/header.html)
+ # Modern CSS theme for documentation with custom libssh.org color scheme
+ set(DOXYGEN_HTML_EXTRA_STYLESHEET
+ ${AWESOME_CSS_DIR}/doxygen-awesome.css
+ ${CMAKE_CURRENT_SOURCE_DIR}/doxygen-custom.css)
+ # JavaScript extensions: dark mode toggle, copy button, paragraph links,
+ # interactive TOC
+ set(DOXYGEN_HTML_EXTRA_FILES
+ ${AWESOME_CSS_DIR}/doxygen-awesome-darkmode-toggle.js
+ ${AWESOME_CSS_DIR}/doxygen-awesome-fragment-copy-button.js
+ ${AWESOME_CSS_DIR}/doxygen-awesome-paragraph-link.js
+ ${AWESOME_CSS_DIR}/doxygen-awesome-interactive-toc.js)
- # This updates the Doxyfile if we do changes here
set(_doxyfile_template "${CMAKE_BINARY_DIR}/CMakeDoxyfile.in")
set(_target_doxyfile "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.docs")
configure_file("${_doxyfile_template}" "${_target_doxyfile}")
- doxygen_add_docs(docs
- ${CMAKE_SOURCE_DIR}/include/libssh
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_CURRENT_SOURCE_DIR})
+ doxygen_add_docs(docs ${CMAKE_SOURCE_DIR}/include/libssh
+ ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
- add_custom_target(docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh ${CMAKE_BINARY_DIR})
-endif() # DOXYGEN_FOUND
+ # Make docs depend on doxygen-awesome-css download (if not using local dir)
+ if (TARGET doxygen-awesome-css)
+ add_dependencies(docs doxygen-awesome-css)
+ endif ()
-endif() # CMAKE_VERSION
+ add_custom_target(
+ docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh
+ ${CMAKE_BINARY_DIR})
+endif (DOXYGEN_FOUND)
diff --git a/doc/DoxygenLayout.xml b/doc/DoxygenLayout.xml
new file mode 100644
index 00000000..14a90505
--- /dev/null
+++ b/doc/DoxygenLayout.xml
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/authentication.dox b/doc/authentication.dox
index 7d0ab81d..a0b2df84 100644
--- a/doc/authentication.dox
+++ b/doc/authentication.dox
@@ -105,7 +105,7 @@ Here is a small example of password authentication:
@code
int authenticate_password(ssh_session session)
{
- char *password;
+ char *password = NULL;
int rc;
password = getpass("Enter your password: ");
@@ -218,7 +218,7 @@ int authenticate_kbdint(ssh_session session)
rc = ssh_userauth_kbdint(session, NULL, NULL);
while (rc == SSH_AUTH_INFO)
{
- const char *name, *instruction;
+ const char *name = NULL, *instruction = NULL;
int nprompts, iprompt;
name = ssh_userauth_kbdint_getname(session);
@@ -231,7 +231,7 @@ int authenticate_kbdint(ssh_session session)
printf("%s\n", instruction);
for (iprompt = 0; iprompt < nprompts; iprompt++)
{
- const char *prompt;
+ const char *prompt = NULL;
char echo;
prompt = ssh_userauth_kbdint_getprompt(session, iprompt, &echo);
@@ -251,7 +251,7 @@ int authenticate_kbdint(ssh_session session)
}
else
{
- char *ptr;
+ char *ptr = NULL;
ptr = getpass(prompt);
if (ssh_userauth_kbdint_setanswer(session, iprompt, ptr) < 0)
@@ -354,7 +354,7 @@ The following example shows how to retrieve and dispose the issue banner:
int display_banner(ssh_session session)
{
int rc;
- char *banner;
+ char *banner = NULL;
/*
*** Does not work without calling ssh_userauth_none() first ***
diff --git a/doc/command.dox b/doc/command.dox
index 588151c6..dde32348 100644
--- a/doc/command.dox
+++ b/doc/command.dox
@@ -22,7 +22,7 @@ a SSH session that uses this channel:
@code
int show_remote_files(ssh_session session)
{
- ssh_channel channel;
+ ssh_channel channel = NULL;
int rc;
channel = ssh_channel_new(session);
@@ -91,4 +91,10 @@ that it used:
}
@endcode
+Warning: In a single channel, only ONE command can be executed!
+If you want to executed multiple commands, allocate separate channels for
+them or consider opening interactive shell.
+Attempting to run multiple consecutive commands in one channel will fail.
+
+
*/
diff --git a/doc/doxygen-custom.css b/doc/doxygen-custom.css
new file mode 100644
index 00000000..0c68d575
--- /dev/null
+++ b/doc/doxygen-custom.css
@@ -0,0 +1,127 @@
+/**
+ * Custom color scheme for libssh documentation
+ * Based on libssh.org color palette
+ */
+
+html {
+ /* Primary colors - using libssh.org orange accent */
+ --primary-color: #F78C40;
+ --primary-dark-color: #f57900;
+ --primary-light-color: #fab889;
+
+ /* Accent color - neutral gray */
+ --primary-lighter-color: #5A5A5A;
+
+ /* Page colors - clean white background */
+ --page-background-color: #ffffff;
+ --page-foreground-color: #333333;
+ --page-secondary-foreground-color: #666666;
+
+ /* Links - use the warm orange color */
+ --link-color: #F78C40;
+ --link-hover-color: #f0690a;
+
+ /* Code blocks and fragments - very light background */
+ --code-background: #f9f9f9;
+ --fragment-background: #f9f9f9;
+
+ /* Borders - subtle light grey */
+ --separator-color: #e0e0e0;
+ --border-light-color: #f0f0f0;
+
+ /* Side navigation - pure white */
+ --side-nav-background: #ffffff;
+
+ /* Menu colors - warm orange accent */
+ --menu-selected-background: #F78C40;
+
+ /* Tables and boxes - lighter */
+ --tablehead-background: #fbc7a2;
+ --tablehead-foreground: #333333;
+}
+
+/* Header styling with libssh brand colors */
+#titlearea {
+ background-color: #5A5A5A;
+ background-image: linear-gradient(to right, #5A5A5A, #6a6a6a);
+ border-bottom: 3px solid #F78C40;
+}
+
+#projectname {
+ color: #ffffff !important;
+}
+
+#projectbrief {
+ color: #fab889 !important;
+}
+
+/* Top navigation tabs */
+#top {
+ background: linear-gradient(to bottom, #5A5A5A 0%, #6a6a6a 100%);
+}
+
+.tabs, .tabs2, .tabs3 {
+ background-image: none;
+ background-color: transparent;
+}
+
+.tablist li {
+ background: rgba(255, 255, 255, 0.1);
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.tablist li:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.tablist li.current {
+ background: #F78C40;
+ border-bottom: 3px solid #f57900;
+}
+
+/* Tab text colors - comprehensive selectors */
+#nav-path ul li a,
+.tabs a,
+.tabs2 a,
+.tabs3 a,
+.tablist a,
+.tablist a:link,
+.tablist a:visited,
+.tablist li a,
+#main-nav a,
+.sm > li > a,
+.sm > li > a .sub-arrow {
+ color: #ffffff !important;
+ text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
+}
+
+/* Active/current tab text */
+#nav-path ul li.current a,
+.tabs .current a,
+.tabs2 .current a,
+.tabs3 .current a,
+.tablist .current a,
+.tablist .current a:link,
+.tablist .current a:visited,
+.tablist li.current a,
+#main-nav .current a,
+.sm .current a {
+ color: #333333 !important;
+ text-shadow: none;
+}
+
+/* Dropdown arrow - white color for top menu */
+.sm-dox a span.sub-arrow {
+ border-right-color: #ffffff !important;
+ border-bottom-color: #ffffff !important;
+}
+
+/* Dropdown menu text - must be dark on white background */
+/* Make this as specific as possible to override white color */
+.sm-dox > li > ul > li > a,
+.sm-dox li ul li a,
+.sm-dox ul li a,
+#main-menu ul li a {
+ color: #333333 !important;
+ text-shadow: none !important;
+}
diff --git a/doc/favicon.png b/doc/favicon.png
new file mode 100644
index 00000000..0b343c15
Binary files /dev/null and b/doc/favicon.png differ
diff --git a/doc/fetch_doxygen_awesome.cmake b/doc/fetch_doxygen_awesome.cmake
new file mode 100644
index 00000000..1fa89674
--- /dev/null
+++ b/doc/fetch_doxygen_awesome.cmake
@@ -0,0 +1,41 @@
+# Script to download doxygen-awesome-css at build time
+#
+# Usage:
+# cmake -P fetch_doxygen_awesome.cmake \
+# -DURL= \
+# -DDEST_DIR= \
+# -DVERSION=
+
+if(NOT DEFINED URL)
+ message(FATAL_ERROR "URL not specified")
+endif()
+if(NOT DEFINED DEST_DIR)
+ message(FATAL_ERROR "DEST_DIR not specified")
+endif()
+if(NOT DEFINED VERSION)
+ message(FATAL_ERROR "VERSION not specified")
+endif()
+
+set(EXTRACT_DIR "${DEST_DIR}/doxygen-awesome-css-${VERSION}")
+
+if(NOT EXISTS "${EXTRACT_DIR}/doxygen-awesome.css")
+ message(STATUS "Downloading doxygen-awesome-css ${VERSION}...")
+ set(TARBALL "${DEST_DIR}/doxygen-awesome-css.tar.gz")
+ file(DOWNLOAD
+ "${URL}"
+ "${TARBALL}"
+ STATUS download_status
+ SHOW_PROGRESS
+ )
+ list(GET download_status 0 status_code)
+ if(NOT status_code EQUAL 0)
+ list(GET download_status 1 error_msg)
+ message(FATAL_ERROR "Download failed: ${error_msg}")
+ endif()
+ message(STATUS "Extracting doxygen-awesome-css...")
+ file(ARCHIVE_EXTRACT
+ INPUT "${TARBALL}"
+ DESTINATION "${DEST_DIR}"
+ )
+ file(REMOVE "${TARBALL}")
+endif()
diff --git a/doc/fido2.dox b/doc/fido2.dox
new file mode 100644
index 00000000..98f1ca13
--- /dev/null
+++ b/doc/fido2.dox
@@ -0,0 +1,601 @@
+/**
+
+@page libssh_tutor_fido2 Chapter 11: FIDO2/U2F Keys Support
+
+@section fido2_intro Introduction
+
+The traditional SSH public key model stores the private key on disk
+and anyone who obtains that file (and possibly its passphrase) can impersonate
+the user. FIDO2 authenticators, such as USB security keys, are hardware tokens
+that generate or securely store private key material within a secure element
+and may require explicit user interaction such as a touch, PIN, or biometric
+verification for use. Hence, security keys are far safer from theft or
+exfiltration than traditional file-based SSH keys. libssh provides support
+for FIDO2/U2F security keys as hardware-backed SSH authentication credentials.
+
+This chapter explains the concepts, build prerequisites, the API, and
+usage patterns for enrolling (creating) and using security key-backed SSH
+keys, including resident (discoverable) credentials.
+
+@subsection fido2_resident_keys Resident Keys
+
+Two credential storage modes exist for security keys:
+
+ - Non-resident (default): A credential ID (key handle) and metadata are
+ stored on the client-side in a key file. This key handle must be
+ presented to the FIDO2/U2F device while signing. This is somewhat
+ similar to traditional SSH keys, except that the key handle is not the
+ private key itself, but used in combination with the device's master key
+ to derive the actual private key.
+
+ - Resident (discoverable): The credential (and metadata like user id) is
+ stored on the device. No local file is needed; the device can enumerate or
+ locate the credential internally when queried.
+
+Advantages of resident keys include portability (using the same device
+across hosts) and resilience (no loss if the local machine is destroyed).
+Although, they may be limited by the storage of the authenticator.
+
+@subsection fido2_presence_verification User Presence vs. User Verification
+
+FIDO2 distinguishes between:
+
+ - User Presence (UP): A simple physical interaction (touch) to confirm a
+ human is present.
+
+ - User Verification (UV): Verification of the user’s identity through
+ biometric authentication or a PIN.
+
+Requiring UV provides additional protection if the device is stolen
+and used without the PIN/biometric.
+
+libssh exposes flags controlling these requirements (see below).
+
+@subsection fido2_callbacks The Callback Abstraction
+
+Different environments may need to access security keys through different
+transport layers (e.g., USB-HID, NFC, Bluetooth, etc.). To accommodate
+this variability, libssh does not hard-code a single implementation.
+
+Instead, it defines a small callback interface (`ssh_sk_callbacks`) used for all
+security key operations. Any implementation of this callback interface can be used
+by higher-level PKI functions to perform enroll/sign/load_resident_keys
+operations without needing to know the transport specifics. Hence, users can
+define their own implementations for these callbacks to support different
+transport protocols or custom hardware. Refer @ref fido2_custom_callbacks
+for additional details.
+
+The callback interface is defined in `libssh/callbacks.h` and the behaviour
+and return values are specified by `libssh/sk_api.h`, which is the same
+interface defined by OpenSSH for its security key support. This means that
+any callback implementations (also called "middleware" in OpenSSH terminology)
+developed for OpenSSH can be adapted to libssh with minimal changes.
+
+The following operations are abstracted by the callback interface:
+
+ - api_version(): Report the version of the SK API that the callback implementation
+ is based on, so that libssh can check whether this implementation would be
+ compatible with the SK API version that it supports.
+ Refer @ref fido2_custom_callbacks_version for additional details.
+ - enroll(): Create (enroll) a new credential, returning public key, key
+ handle, attestation data.
+ - sign(): Produce a signature for supplied inputs using an existing key
+ handle.
+ - load_resident_keys(): Enumerate resident (discoverable) credentials stored
+ on the authenticator.
+
+libssh provides a default implementation of the `ssh_sk_callbacks` using
+the libfido2 library for the USB-HID transport protocol. Hence, by default,
+libssh can interact with any FIDO2/U2F device that supports USB-HID and is
+compatible with libfido2, without requiring any additional modifications.
+
+@subsection fido2_build Building with FIDO2 Support
+
+To enable FIDO2/U2F support, libssh must be built with the WITH_FIDO2
+build option as follows:
+
+@verbatim
+ cmake -DWITH_FIDO2=ON ..
+@endverbatim
+
+libssh will also build the default USB-HID `ssh_sk_callbacks`, if the
+libfido2 library and headers are installed on your system.
+
+@warning If built without libfido2, support for interacting with FIDO2/U2F
+devices over USB-HID will not be available.
+
+@subsection fido2_api_overview API Overview
+
+Security key operations are configured through the `ssh_pki_ctx`
+which allows to specify both general PKI options and FIDO2-specific
+options such as the sk_callbacks, challenge data, application string, flags, etc.
+
+The following sections describe the options that can be configured and how
+the `ssh_pki_ctx` is used in conjunction with `ssh_key` to perform
+enrollment, signing, and resident key loading operations.
+
+@subsection fido2_key_objects Security Key Objects & Metadata
+
+Security keys are surfaced as `ssh_key` objects of type
+`SSH_KEYTYPE_SK_ECDSA` and `SSH_KEYTYPE_SK_ED25519` (corresponding to the
+OpenSSH public key algorithm names `sk-ecdsa-sha2-nistp256@openssh.com` and
+`sk-ssh-ed25519@openssh.com`). In addition to standard key handling, libssh
+exposes the following helper functions to retrieve embedded SK metadata:
+
+ - ssh_key_get_sk_application(): Returns the relying party / application
+ (RP ID) string. The Relying Party ID (RP ID) is a string
+ that identifies the application or service requesting key enrollment. It
+ ensures that a credential is bound to a specific origin, preventing
+ phishing across sites. During registration, the authenticator associates
+ the credential with this RP ID so that it can later only be used for
+ authentication requests from the same relying party. For SSH keys, the
+ common format is "ssh:user@host".
+
+ - ssh_key_get_sk_user_id(): Returns a copy of the user ID associated with a key
+ which represents a unique identifier for the user within the relying
+ party (application) context. It is typically a string (such as an
+ email, or a random identifier) that helps distinguish credentials
+ belonging to different users for the same application.
+
+ Though the user ID can be binary data according to the FIDO2 spec, libssh only
+ supports NUL-terminated strings for enrolling new keys in order to remain compatible
+ with the OpenSSH's sk-api interface.
+
+ However, libssh does support loading existing resident keys with user IDs containing
+ arbitrary binary data. It does so by using an `ssh_string` to store the loaded key's
+ user_id, and an `ssh_string` can contain arbitrary binary data that can not be stored
+ in a traditional NUL-terminated string (like null bytes).
+
+ @note The user_id is NOT stored in the key file for non-resident keys. It is only
+ available for resident (discoverable) keys loaded from the authenticator via
+ ssh_sk_resident_keys_load(). For keys imported from files, this function returns
+ NULL.
+
+ - ssh_key_get_sk_flags(): Returns the flags associated with the key. The
+ following are the supported flags and they can be combined using
+ bitwise OR:
+ - SSH_SK_USER_PRESENCE_REQD : Require user presence (touch).
+ - SSH_SK_USER_VERIFICATION_REQD : Require user verification
+ (PIN/biometric).
+ - SSH_SK_RESIDENT_KEY : Request a resident discoverable credential.
+ - SSH_SK_FORCE_OPERATION : Force resident (discoverable) credential
+ creation even if one with same application and user_id already
+ exists.
+
+These functions perform no additional communication with the
+authenticator, this metadata is captured during enrollment/loading and
+cached in the `ssh_key`.
+
+@subsection fido2_options Setting Security Key Context Options
+
+Options are set via ssh_pki_ctx_options_set().
+
+Representative security key options:
+ - SSH_PKI_OPTION_SK_APPLICATION (const char *): Required relying party ID
+ If not set, a default value of "ssh:" is used.
+ - SSH_PKI_OPTION_SK_FLAGS (uint8_t *): Flags described above. If not set,
+ defaults to SSH_SK_USER_PRESENCE_REQD. This is because OpenSSH `sshd`
+ requires user presence for security key authentication by default.
+ - SSH_PKI_OPTION_SK_USER_ID (const char *): Represents a unique identifier
+ for the user within the relying party (application) context.
+ It is typically a string (such as an email, or a random identifier) that
+ helps distinguish credentials belonging to different users for the same
+ application. If not set, defaults to 64 zeros.
+ - SSH_PKI_OPTION_SK_CHALLENGE (ssh_buffer): Custom challenge; if omitted a
+ random 32-byte challenge is generated.
+ - SSH_PKI_OPTION_SK_CALLBACKS (ssh_sk_callbacks): Replace the default
+ callbacks with custom callbacks.
+
+PIN callback: Use ssh_pki_ctx_set_sk_pin_callback() to register a function
+matching `ssh_auth_callback` to prompt for and supply a PIN. The callback may
+be called multiple times to ask for the pin depending on the authenticator policy.
+
+Callback options: Callback implementations may accept additional configuration
+name/value options such as the path to the fido device. These options can be provided via
+`ssh_pki_ctx_sk_callbacks_option_set()`. Refer @ref fido2_custom_callbacks_options
+for additional details.
+
+The built-in callback implementation provided by libssh supports additional options,
+with their names defined in `libssh.h` prefixed with `SSH_SK_OPTION_NAME_*`, such as:
+
+SSH_SK_OPTION_NAME_DEVICE_PATH: Used for specifying a device path.
+If the device path is not specified and multiple devices are connected, then
+depending upon the operation and the flags set, the callback implementation may
+automatically select a suitable device, or the user may be prompted to touch the
+device they want to use.
+
+SSH_SK_OPTION_NAME_USER_ID: Used for setting the user ID.
+Note that the user ID can also be set using the ssh_pki_ctx_options_set() API.
+
+@subsection fido2_enrollment Enrollment Example
+
+An enrollment operation creates a new credential on the authenticator and
+returns an ssh_key object representing it. The application and user_id
+fields are required for creating the credential. The other options are
+optional. A successful enrollment returns the public key, key handle, and
+metadata which are stored in the ssh_key object, and may optionally return
+attestation data which is used for verifying the authenticator model and
+firmware version.
+
+Below is a simple example enrolling an Ed25519 security key (non-resident)
+requiring user presence only:
+
+@code
+#include
+#include
+
+static int pin_cb(const char *prompt,
+ char *buf,
+ size_t len,
+ int echo,
+ int verify,
+ void *userdata)
+{
+ (void)prompt;
+ (void)echo;
+ (void)verify;
+ (void)userdata;
+
+ /* In a real application, the user would be prompted to enter the PIN */
+ const char *pin = "4242";
+ size_t l = strlen(pin);
+ if (l + 1 > len) {
+ return SSH_ERROR;
+ }
+
+ memcpy(buf, pin, l + 1);
+ return SSH_OK;
+}
+
+int enroll_sk_key()
+{
+ const char *app = "ssh:user@host";
+ const char *user_id = "alice";
+ uint8_t flags = SSH_SK_USER_PRESENCE_REQD | SSH_SK_USER_VERIFICATION_REQD;
+ const char *device_path = "/dev/hidraw6"; /* Optional device path */
+
+ ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
+ ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_APPLICATION, app);
+ ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_USER_ID, user_id);
+ ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_FLAGS, &flags);
+
+ ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL);
+
+ ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
+ SSH_SK_OPTION_NAME_DEVICE_PATH,
+ device_path,
+ true);
+
+ ssh_key enrolled = NULL;
+ int rc = ssh_pki_generate_key(SSH_KEYTYPE_SK_ED25519,
+ pki_ctx,
+ &enrolled); /* produces sk-ed25519 key */
+
+ /* Save enrolled key using ssh_pki_export_privkey_file, retrieve attestation
+ * buffer etc. */
+
+ /* Free context and key when done */
+}
+@endcode
+
+After a successful enrollment, you can retrieve the attestation buffer
+(if provided by the authenticator) from the PKI context:
+
+@code
+ssh_buffer att_buf = NULL;
+rc = ssh_pki_ctx_get_sk_attestation_buffer(pki_ctx, &att_buf);
+if (rc == SSH_OK && att_buf != NULL) {
+ /* att_buf now contains the serialized attestation
+ * ("ssh-sk-attest-v01"). You can inspect, save, or
+ * parse the buffer as needed
+ */
+ ssh_buffer_free(att_buf);
+}
+@endcode
+
+Notes:
+- The attestation buffer is only populated if the enrollment operation
+ succeeds and the authenticator provides attestation data.
+- `ssh_pki_ctx_get_sk_attestation_buffer()` returns a copy of the attestation
+ buffer; the caller must free it with `ssh_buffer_free()`.
+
+@subsection fido2_signing Authenticating with a Stored Security Key Public Key
+
+To authenticate using a security key, the application typically loads the
+previously enrolled sk-* private key, establishes an SSH connection, and
+calls `ssh_userauth_publickey()`. libssh automatically recognizes security
+key types and transparently handles the required hardware-backed
+authentication steps such as prompting for a touch or PIN using the
+configured security key callbacks.
+
+Example:
+@code
+#include
+#include
+
+int auth_with_sk_file(const char *host,
+ const char *user,
+ const char *privkey_path)
+{
+ ssh_session session = NULL;
+ ssh_key privkey = NULL;
+ int rc = SSH_ERROR;
+
+ session = ssh_new();
+ ssh_options_set(session, SSH_OPTIONS_HOST, host);
+ ssh_options_set(session, SSH_OPTIONS_USER, user);
+ ssh_connect(session);
+
+ ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &privkey);
+
+ ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
+ /* Optionally set PIN callback, device path, etc. */
+ /* ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL); */
+
+ ssh_options_set(session, SSH_OPTIONS_PKI_CONTEXT, pki_ctx);
+
+ rc = ssh_userauth_publickey(session, user, privkey);
+ if (rc == SSH_AUTH_SUCCESS) {
+ printf("Authenticated with security key.\n");
+ rc = SSH_OK;
+ } else {
+ fprintf(stderr,
+ "Authentication failed rc=%d err=%s\n",
+ rc,
+ ssh_get_error(session));
+ rc = SSH_ERROR;
+ }
+
+ /* Free resources */
+}
+@endcode
+
+@subsection fido2_resident Resident Key Enumeration
+
+Resident keys stored on the device can be discovered and loaded with
+ssh_sk_resident_keys_load() which takes a PKI context (configured with
+a PIN callback) and returns each key as an ssh_key and the number of keys loaded.
+
+Example:
+
+@code
+#include
+#include
+#include
+
+static int pin_cb(const char *prompt,
+ char *buf,
+ size_t len,
+ int echo,
+ int verify,
+ void *userdata)
+{
+ (void)prompt;
+ (void)echo;
+ (void)verify;
+ (void)userdata;
+ const char *pin = "4242";
+ size_t l = strlen(pin);
+
+ if (l + 1 > len) {
+ return SSH_ERROR;
+ }
+
+ memcpy(buf, pin, l + 1);
+ return SSH_OK;
+}
+
+int auth_with_resident(const char *host,
+ const char *user,
+ const char *application,
+ const char *user_id)
+{
+ ssh_pki_ctx pki_ctx = NULL;
+ size_t num_found = 0;
+ ssh_key *keys = NULL;
+ ssh_key final_key = NULL;
+ int rc = SSH_ERROR;
+
+ ssh_string cur_application = NULL;
+ ssh_string cur_user_id = NULL;
+ ssh_string expected_application = NULL;
+ ssh_string expected_user_id = NULL;
+
+ pki_ctx = ssh_pki_ctx_new();
+ ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL);
+
+ expected_application = ssh_string_from_char(application);
+ expected_user_id = ssh_string_from_char(user_id);
+
+ rc = ssh_sk_resident_keys_load(pki_ctx, &keys, &num_found);
+ for (size_t i = 0; i < num_found; i++) {
+ cur_application = ssh_key_get_sk_application(keys[i]);
+ cur_user_id = ssh_key_get_sk_user_id(keys[i]);
+
+ if (ssh_string_cmp(cur_application, expected_application) == 0 &&
+ ssh_string_cmp(cur_user_id, expected_user_id) == 0) {
+ SSH_STRING_FREE(cur_application);
+ SSH_STRING_FREE(cur_user_id);
+ final_key = keys[i];
+ break;
+ }
+
+ SSH_STRING_FREE(cur_application);
+ SSH_STRING_FREE(cur_user_id);
+ }
+
+ SSH_STRING_FREE(expected_application);
+ SSH_STRING_FREE(expected_user_id);
+
+ /* Continue with authentication using the ssh_key with
+ * ssh_userauth_publickey as usual, and free resources when done. */
+}
+@endcode
+
+@subsection fido2_sshsig Signing using the sshsig API
+
+Security keys can also be used for general-purpose signing of arbitrary data
+(without SSH authentication) using the existing `sshsig_sign()` and `sshsig_verify()`
+functions. These functions work seamlessly with security key types
+(`SSH_KEYTYPE_SK_ECDSA` and `SSH_KEYTYPE_SK_ED25519`) and will automatically
+invoke the configured security key callbacks to perform hardware-backed signing
+operations.
+
+@subsection fido2_custom_callbacks Implementing Custom Callback Implementations
+
+Users may need to implement custom callback implementations to support
+different transport protocols (e.g., NFC, Bluetooth) beyond the default USB-HID
+support. This section describes how to implement and integrate custom callback
+implementations.
+
+To implement custom callbacks, you must include the following headers:
+
+@code
+#include /* For ssh_sk_callbacks_struct */
+#include /* For SK API constants and data structures */
+@endcode
+
+The `libssh/sk_api.h` header provides the complete interface specification including
+request/response structures, flags, and version macros.
+
+@subsubsection fido2_custom_callbacks_version API Version Compatibility
+
+libssh validates callback implementations by checking the API version returned by
+the `api_version()` callback. To ensure compatibility, libssh compares the major
+version (upper 16 bits) of the returned value with `LIBSSH_SK_API_VERSION_MAJOR`.
+If they don't match, libssh will reject the callback implementation.
+This ensures that the callbacks' SK API matches the major version expected by libssh,
+while allowing minor version differences.
+
+@subsubsection fido2_custom_callbacks_implementation Implementation Example
+
+Here's a minimal example of defining and using custom callbacks:
+
+@code
+#include
+#include
+#include
+
+/* Your custom API version callback */
+static uint32_t my_sk_api_version(void)
+{
+ /* Match the major version, set your own minor version */
+ return SSH_SK_VERSION_MAJOR | 0x0001;
+}
+
+/* Your custom enroll callback */
+static int my_sk_enroll(uint32_t alg,
+ const uint8_t *challenge,
+ size_t challenge_len,
+ const char *application,
+ uint8_t flags,
+ const char *pin,
+ struct sk_option **options,
+ struct sk_enroll_response **enroll_response)
+{
+ /* Parse options array to extract custom parameters */
+ if (options != NULL) {
+ for (size_t i = 0; options[i] != NULL; i++) {
+ if (strcmp(options[i]->name, "my_custom_option") == 0) {
+ /* Use options[i]->value */
+ }
+ }
+ }
+
+ /* Implement your enroll logic here */
+ /* ... */
+
+ return SSH_SK_ERR_GENERAL; /* Return appropriate error code */
+}
+
+/* Implement other required callbacks: sign, load_resident_keys */
+/* ... */
+
+/* Define your callback structure */
+static struct ssh_sk_callbacks_struct my_sk_callbacks = {
+ .size = sizeof(struct ssh_sk_callbacks_struct),
+ .api_version = my_sk_api_version,
+ .enroll = my_sk_enroll,
+ .sign = my_sk_sign, /* Your implementation */
+ .load_resident_keys = my_sk_load_resident_keys, /* Your implementation */
+};
+
+/* Usage example */
+void use_custom_callbacks(void)
+{
+ ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
+
+ /* Set your custom callbacks */
+ ssh_pki_ctx_options_set(pki_ctx,
+ SSH_PKI_OPTION_SK_CALLBACKS,
+ &my_sk_callbacks);
+
+ /* Pass custom options to your callbacks */
+ ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
+ "my_custom_option",
+ "my_custom_value",
+ false);
+
+ /* Use the context for enrollment, signing, etc. */
+}
+@endcode
+
+@subsubsection fido2_custom_callbacks_options Passing Custom Options
+
+The `ssh_pki_ctx_sk_callbacks_option_set()` function allows you to pass
+implementation-specific options as name/value string pairs:
+
+@code
+ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
+ "option_name",
+ "option_value",
+ required);
+@endcode
+
+Parameters:
+- `option_name`: The name of the option (e.g., "device_path", "my_custom_param")
+- `option_value`: The string value for this option
+- `required`: If true, this option must be processed by the callback implementation
+ and cannot be ignored. If false, the option is advisory and can be skipped if the
+ callback implementation does not support it.
+
+These options are passed to your callbacks in the `struct sk_option **options`
+parameter as a NULL-terminated array. Each `sk_option` has the following fields:
+- `name`: The option name (char *)
+- `value`: The option value (char *)
+- `required`: Whether the option must be processed (uint8_t, non-zero = required)
+
+@subsubsection fido2_custom_callbacks_openssh OpenSSH Middleware Compatibility
+
+Since libssh uses the same SK API as OpenSSH, middleware implementations developed
+for OpenSSH can be adapted with minimal changes.
+To adapt an OpenSSH middleware for libssh, create a wrapper that populates
+`ssh_sk_callbacks_struct` with pointers to the middleware's functions.
+
+@subsection fido2_testing Testing and Environment Variables
+
+Unit tests covering USB-HID enroll/sign/load_resident_keys operations can be found
+in the `tests/unittests/torture_sk_usbhid.c` file. To run these tests you
+must have libfido2 installed and the WITH_FIDO2=ON build option set.
+Additionally, you must ensure the following:
+
+ - An actual FIDO2 device must be connected to the test machine.
+ - The TORTURE_SK_USBHID environment variable must be set.
+ - The environment variable TORTURE_SK_PIN= must be set.
+
+If these are not set, the tests are skipped.
+
+The higher level PKI integration tests can be found in
+`tests/unittests/torture_pki_sk.c` and the tests related to the sshsig API
+can be found in `tests/unittests/torture_pki_sshsig.c`.
+These use the callback implementation provided by OpenSSH's sk-dummy.so,
+which simulates an authenticator without requiring any hardware. Hence, these tests
+can be run in the CI environment.
+However, these tests can also be configured to use the default USB-HID callbacks
+by setting the same environment variables as described above.
+
+The following devices were tested during development:
+
+- Yubico Security Key NFC - USB-A
+
+*/
diff --git a/doc/forwarding.dox b/doc/forwarding.dox
index 2b202b4d..3ca3aa8a 100644
--- a/doc/forwarding.dox
+++ b/doc/forwarding.dox
@@ -100,7 +100,7 @@ used to retrieve google's home page from the remote SSH server.
@code
int direct_forwarding(ssh_session session)
{
- ssh_channel forwarding_channel;
+ ssh_channel forwarding_channel = NULL;
int rc = SSH_ERROR;
char *http_get = "GET / HTTP/1.1\nHost: www.google.com\n\n";
int nbytes, nwritten;
@@ -161,7 +161,7 @@ local libssh application, which handles them:
int web_server(ssh_session session)
{
int rc;
- ssh_channel channel;
+ ssh_channel channel = NULL;
char buffer[256];
int nbytes, nwritten;
int port = 0;
diff --git a/doc/guided_tour.dox b/doc/guided_tour.dox
index 904a739e..e98b5e0d 100644
--- a/doc/guided_tour.dox
+++ b/doc/guided_tour.dox
@@ -79,7 +79,7 @@ Here is a small example of how to use it:
int main()
{
- ssh_session my_ssh_session;
+ ssh_session my_ssh_session = NULL;
int verbosity = SSH_LOG_PROTOCOL;
int port = 22;
@@ -126,7 +126,7 @@ Here's an example:
int main()
{
- ssh_session my_ssh_session;
+ ssh_session my_ssh_session = NULL;
int rc;
my_ssh_session = ssh_new();
@@ -190,8 +190,7 @@ int verify_knownhost(ssh_session session)
ssh_key srv_pubkey = NULL;
size_t hlen;
char buf[10];
- char *hexa;
- char *p;
+ char *p = NULL;
int cmp;
int rc;
@@ -201,7 +200,7 @@ int verify_knownhost(ssh_session session)
}
rc = ssh_get_publickey_hash(srv_pubkey,
- SSH_PUBLICKEY_HASH_SHA1,
+ SSH_PUBLICKEY_HASH_SHA256,
&hash,
&hlen);
ssh_key_free(srv_pubkey);
@@ -217,7 +216,7 @@ int verify_knownhost(ssh_session session)
break;
case SSH_KNOWN_HOSTS_CHANGED:
fprintf(stderr, "Host key for server changed: it is now:\n");
- ssh_print_hexa("Public key hash", hash, hlen);
+ ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
fprintf(stderr, "For security reasons, connection will be stopped\n");
ssh_clean_pubkey_hash(&hash);
@@ -238,10 +237,9 @@ int verify_knownhost(ssh_session session)
/* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */
case SSH_KNOWN_HOSTS_UNKNOWN:
- hexa = ssh_get_hexa(hash, hlen);
fprintf(stderr,"The server is unknown. Do you trust the host key?\n");
- fprintf(stderr, "Public key hash: %s\n", hexa);
- ssh_string_free_char(hexa);
+ fprintf(stderr, "Public key hash: ");
+ ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
ssh_clean_pubkey_hash(&hash);
p = fgets(buf, sizeof(buf), stdin);
if (p == NULL) {
@@ -317,9 +315,9 @@ The example below shows an authentication with password:
int main()
{
- ssh_session my_ssh_session;
+ ssh_session my_ssh_session = NULL;
int rc;
- char *password;
+ char *password = NULL;
// Open session and set options
my_ssh_session = ssh_new();
@@ -380,7 +378,7 @@ The example below shows how to execute a remote command:
@code
int show_remote_processes(ssh_session session)
{
- ssh_channel channel;
+ ssh_channel channel = NULL;
int rc;
char buffer[256];
int nbytes;
diff --git a/doc/header.html b/doc/header.html
new file mode 100644
index 00000000..6e43e819
--- /dev/null
+++ b/doc/header.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+$projectname: $title
+$title
+
+
+
+
+
+
+
+
+
+
+
+
+$treeview
+$search
+$mathjax
+$darkmode
+
+$extrastylesheet
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
$projectname $projectnumber
+
+
$projectbrief
+
+
+
+
+
+
$projectbrief
+
+
+
+
+
+
+
$searchbox
+
+
+
+
+
+
+
$searchbox
+
+
+
+
+
+
+
diff --git a/doc/introduction.dox b/doc/introduction.dox
index f2f3d3dd..4415c1bb 100644
--- a/doc/introduction.dox
+++ b/doc/introduction.dox
@@ -14,8 +14,8 @@ libssh is a Free Software / Open Source project. The libssh library
is distributed under LGPL license. The libssh project has nothing to do with
"libssh2", which is a completely different and independent project.
-libssh can run on top of either libgcrypt or libcrypto,
-two general-purpose cryptographic libraries.
+libssh can run on top of either libcrypto, mbedtls or libgcrypt (deprecated)
+general-purpose cryptographic libraries.
This tutorial concentrates for its main part on the "client" side of libssh.
To learn how to accept incoming SSH connections (how to write a SSH server),
@@ -44,6 +44,12 @@ Table of contents:
@subpage libssh_tutor_threads
+@subpage libssh_tutor_pkcs11
+
+@subpage libssh_tutor_sftp_aio
+
+@subpage libssh_tutor_fido2
+
@subpage libssh_tutor_todo
*/
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
index 04197603..a0ed6776 100644
--- a/doc/mainpage.dox
+++ b/doc/mainpage.dox
@@ -19,12 +19,13 @@ the interesting functions as you go.
The libssh library provides:
- - Key Exchange Methods: curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1
+ - Key Exchange Methods: sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com, mlkem768x25519-sha256, mlkem768nistp256-sha256, mlkem1024nistp384-sha384, curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1
+ - GSSAPI Key Exchange Methods: gss-group14-sha256-*, gss-group16-sha512-*, gss-nistp256-sha256-*, gss-curve25519-sha256-*
- Public Key Algorithms: ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa, rsa-sha2-512, rsa-sha2-256
- Ciphers: aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc
- Compression Schemes: zlib, zlib@openssh.com, none
- MAC hashes: hmac-sha1, hmac-sha2-256, hmac-sha2-512, hmac-md5
- - Authentication: none, password, public-key, keyboard-interactive, gssapi-with-mic
+ - Authentication: none, password, public-key, keyboard-interactive, gssapi-with-mic, gssapi-keyex
- Channels: shell, exec (incl. SCP wrapper), direct-tcpip, subsystem, auth-agent-req@openssh.com
- Global Requests: tcpip-forward, forwarded-tcpip
- Channel Requests: x11, pty, exit-status, signal, exit-signal, keepalive@openssh.com, auth-agent-req@openssh.com
@@ -33,7 +34,7 @@ The libssh library provides:
- Thread-safe: Just don't share sessions
- Non-blocking: it can be used both blocking and non-blocking
- Your sockets: the app hands over the socket, or uses libssh sockets
- - OpenSSL or gcrypt: builds with either
+ - OpenSSL, MBedTLS or gcrypt (deprecated): builds with either
@section main-additional-features Additional Features
@@ -170,21 +171,15 @@ The following RFC documents described SSH-2 protocol as an Internet standard.
The Secure Shell (SSH) Session Channel Break Extension
- RFC 4344,
The Secure Shell (SSH) Transport Layer Encryption Modes
- - RFC 4345,
- Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
It was later modified and expanded by the following RFCs.
- RFC 4419,
Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer
Protocol
- - RFC 4432,
- RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol
- (not implemented in libssh)
- RFC 4462,
Generic Security Service Application Program Interface (GSS-API)
Authentication and Key Exchange for the Secure Shell (SSH) Protocol
- (only the authentication implemented in libssh)
- RFC 4716,
The Secure Shell (SSH) Public Key File Format
(not implemented in libssh)
@@ -203,7 +198,6 @@ It was later modified and expanded by the following RFCs.
(not implemented in libssh)
- RFC 8160,
IUTF8 Terminal Mode in Secure Shell (SSH)
- (not handled in libssh)
- RFC 8270,
Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits
- RFC 8308,
@@ -222,6 +216,14 @@ There are also drafts that are being currently developed and followed.
- draft-miller-ssh-agent-08
SSH Agent Protocol
+ - draft-ietf-sshm-mlkem-hybrid-kex-09
+ PQ/T Hybrid Key Exchange with ML-KEM in SSH
+ - draft-ietf-sshm-ntruprime-ssh-06
+ Secure Shell (SSH) Key Exchange Method Using Hybrid Streamlined NTRU Prime sntrup761 and X25519 with SHA-512: sntrup761x25519-sha512
+ - draft-ietf-sshm-chacha20-poly1305-02
+ Secure Shell (SSH) authenticated encryption cipher: chacha20-poly1305
+ - draft-ietf-sshm-strict-kex-01
+ SSH Strict KEX extension
Interesting cryptography documents:
@@ -246,8 +248,6 @@ them like the statvfs calls in SFTP or the ssh-agent.
OpenSSH's deviations and extensions
-
OpenSSH's pubkey certificate authentication
- -
- chacha20-poly1305@openssh.com authenticated encryption mode
-
OpenSSH private key format (openssh-key-v1)
diff --git a/doc/pkcs11.dox b/doc/pkcs11.dox
index c2732a81..b358432c 100644
--- a/doc/pkcs11.dox
+++ b/doc/pkcs11.dox
@@ -21,6 +21,9 @@ To build and use libssh with PKCS #11 support:
3. Install and configure engine_pkcs11 (https://github.com/OpenSC/libp11).
4. Plug in a working smart card or configure softhsm (https://www.opendnssec.org/softhsm).
+@warning The support for Engines was deprecated in OpenSSL 3.0 so this approach
+is deprecated in libssh 0.11.x.
+
# Pre-requisites (OpenSSL 3.0.8+)
The OpenSSL 3.0 is deprecating usage of low-level engines in favor of high-level
@@ -78,10 +81,6 @@ We recommend the users to provide a specific PKCS #11 URI so that it matches onl
If the engine discovers multiple slots that could potentially contain the private keys referenced
by the provided PKCS #11 URI, the engine will not try to authenticate.
-For testing, the SoftHSM PKCS#11 library is used. But it has some issues with
-OpenSSL initialization/cleanup when used with OpenSSL 3.0 so we are using it
-indirectly through a p11-kit remoting as described in the following article:
-
-https://p11-glue.github.io/p11-glue/p11-kit/manual/remoting.html
+For testing, the SoftHSM PKCS#11 library is used.
*/
diff --git a/doc/sftp.dox b/doc/sftp.dox
index 1f99cfdf..4c176a4b 100644
--- a/doc/sftp.dox
+++ b/doc/sftp.dox
@@ -139,7 +139,7 @@ Unlike its equivalent in the SCP subsystem, this function does NOT change the
current directory to the newly created subdirectory.
-@subsection sftp_write Copying a file to the remote computer
+@subsection sftp_write Writing to a file on the remote computer
You handle the contents of a remote file just like you would do with a
local file: you open the file in a given mode, move the file pointer in it,
@@ -203,16 +203,14 @@ int sftp_helloworld(ssh_session session, sftp_session sftp)
@subsection sftp_read Reading a file from the remote computer
-The nice thing with reading a file over the network through SFTP is that it
-can be done both in a synchronous way or an asynchronous way. If you read the file
-asynchronously, your program can do something else while it waits for the
-results to come.
-
-Synchronous read is done with sftp_read().
+A synchronous read from a remote file is done using sftp_read(). This
+section describes how to download a remote file using sftp_read(). The
+next section will discuss more about synchronous/asynchronous read/write
+operations using libssh sftp API.
Files are normally transferred in chunks. A good chunk size is 16 KB. The following
example transfers the remote file "/etc/profile" in 16 KB chunks. For each chunk we
-request, sftp_read blocks till the data has been received:
+request, sftp_read() blocks till the data has been received:
@code
// Good chunk size
@@ -273,87 +271,39 @@ int sftp_read_sync(ssh_session session, sftp_session sftp)
}
@endcode
-Asynchronous read is done in two steps, first sftp_async_read_begin(), which
-returns a "request handle", and then sftp_async_read(), which uses that request handle.
-If the file has been opened in nonblocking mode, then sftp_async_read()
-might return SSH_AGAIN, which means that the request hasn't completed yet
-and that the function should be called again later on. Otherwise,
-sftp_async_read() waits for the data to come. To open a file in nonblocking mode,
-call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode.
+@subsection sftp_aio Performing an asynchronous read/write on a file on the remote computer
-The example below reads a very big file in asynchronous, nonblocking, mode. Each
-time the data is not ready yet, a counter is incremented.
+sftp_read() performs a "synchronous" read operation on a remote file.
+This means that sftp_read() will first request the server to read some
+data from the remote file and then would wait until the server response
+containing data to read (or an error) arrives at the client side.
-@code
-// Good chunk size
-#define MAX_XFER_BUF_SIZE 16384
+sftp_write() performs a "synchronous" write operation on a remote file.
+This means that sftp_write() will first request the server to write some
+data to the remote file and then would wait until the server response
+containing information about the status of the write operation arrives at the
+client side.
-int sftp_read_async(ssh_session session, sftp_session sftp)
-{
- int access_type;
- sftp_file file;
- char buffer[MAX_XFER_BUF_SIZE];
- int async_request;
- int nbytes;
- long counter;
- int rc;
+If your client program wants to do something other than waiting for the
+response after requesting a read/write, the synchronous sftp_read() and
+sftp_write() can't be used. In such a case the "asynchronous" sftp aio API
+should be used.
- access_type = O_RDONLY;
- file = sftp_open(sftp, "some_very_big_file",
- access_type, 0);
- if (file == NULL) {
- fprintf(stderr, "Can't open file for reading: %s\n",
- ssh_get_error(session));
- return SSH_ERROR;
- }
- sftp_file_set_nonblocking(file);
-
- async_request = sftp_async_read_begin(file, sizeof(buffer));
- counter = 0L;
- usleep(10000);
- if (async_request >= 0) {
- nbytes = sftp_async_read(file, buffer, sizeof(buffer),
- async_request);
- } else {
- nbytes = -1;
- }
+Please go through @ref libssh_tutor_sftp_aio for a detailed description
+of the sftp aio API.
- while (nbytes > 0 || nbytes == SSH_AGAIN) {
- if (nbytes > 0) {
- write(1, buffer, nbytes);
- async_request = sftp_async_read_begin(file, sizeof(buffer));
- } else {
- counter++;
- }
- usleep(10000);
+The sftp aio API provides two categories of functions :
+ - sftp_aio_begin_*() : For requesting a read/write from the server.
+ - sftp_aio_wait_*() : For waiting for the response of a previously
+ issued read/write request from the server.
- if (async_request >= 0) {
- nbytes = sftp_async_read(file, buffer, sizeof(buffer),
- async_request);
- } else {
- nbytes = -1;
- }
- }
-
- if (nbytes < 0) {
- fprintf(stderr, "Error while reading file: %s\n",
- ssh_get_error(session));
- sftp_close(file);
- return SSH_ERROR;
- }
-
- printf("The counter has reached value: %ld\n", counter);
-
- rc = sftp_close(file);
- if (rc != SSH_OK) {
- fprintf(stderr, "Can't close the read file: %s\n",
- ssh_get_error(session));
- return rc;
- }
+Hence, the client program can call sftp_aio_begin_*() to request a read/write
+and then can perform any number of operations (other than waiting) before
+calling sftp_aio_wait_*() for waiting for the response of the previously
+issued request.
- return SSH_OK;
-}
-@endcode
+We call read/write operations performed in the manner described above as
+"asynchronous" read/write operations on a remote file.
@subsection sftp_ls Listing the contents of a directory
diff --git a/doc/sftp_aio.dox b/doc/sftp_aio.dox
new file mode 100644
index 00000000..9c26f5e1
--- /dev/null
+++ b/doc/sftp_aio.dox
@@ -0,0 +1,705 @@
+/**
+
+@page libssh_tutor_sftp_aio Chapter 10: The SFTP asynchronous I/O
+
+@section sftp_aio_api The SFTP asynchronous I/O
+
+NOTE : Please read @ref libssh_tutor_sftp before reading this page. The
+synchronous sftp_read() and sftp_write() have been described there.
+
+SFTP AIO stands for "SFTP Asynchronous Input/Output". This API contains
+functions which perform async read/write operations on remote files.
+
+File transfers performed using the asynchronous sftp aio API can be
+significantly faster than the file transfers performed using the synchronous
+sftp read/write API (see sftp_read() and sftp_write()).
+
+The sftp aio API functions are divided into two categories :
+ - sftp_aio_begin_*() [see sftp_aio_begin_read(), sftp_aio_begin_write()]:
+ These functions send a request for an i/o operation to the server and
+ provide the caller an sftp aio handle corresponding to the sent request.
+
+ - sftp_aio_wait_*() [see sftp_aio_wait_read(), sftp_aio_wait_write()]:
+ These functions wait for the server response corresponding to a previously
+ issued request. Which request ? the request corresponding to the sftp aio
+ handle supplied by the caller to these functions.
+
+Conceptually, you can think of the sftp aio handle as a request identifier.
+
+Technically, the sftp_aio_begin_*() functions dynamically allocate memory to
+store information about the i/o request they send and provide the caller a
+handle to this memory, we call this handle an sftp aio handle.
+
+sftp_aio_wait_*() functions use the information stored in that memory (handled
+by the caller supplied sftp aio handle) to identify a request, and then they
+wait for that request's response. These functions also release the memory
+handled by the caller supplied sftp aio handle (except when they return
+SSH_AGAIN).
+
+sftp_aio_free() can also be used to release the memory handled by an sftp aio
+handle but unlike the sftp_aio_wait_*() functions, it doesn't wait for a
+response. This should be used to release the memory corresponding to an sftp
+aio handle when some failure occurs. An example has been provided at the
+end of this page to show the usage of sftp_aio_free().
+
+To begin with, this tutorial will provide basic examples that describe the
+usage of sftp aio API to perform a single read/write operation.
+
+The later sections describe the usage of the sftp aio API to obtain faster file
+transfers as compared to the transfers performed using the synchronous sftp
+read/write API.
+
+On encountering an error, the sftp aio API functions set the sftp and ssh
+errors just like any other libssh sftp API function. These errors can be
+obtained using sftp_get_error(), ssh_get_error() and ssh_get_error_code().
+The code examples provided on this page ignore error handling for the sake of
+brevity.
+
+@subsection sftp_aio_read Using the sftp aio API for reading (a basic example)
+
+For performing an async read operation on a sftp file (see sftp_open()),
+the first step is to call sftp_aio_begin_read() to send a read request to the
+server. The caller is provided an sftp aio handle corresponding to the sent
+read request.
+
+The second step is to pass a pointer to this aio handle to
+sftp_aio_wait_read(), this function waits for the server response which
+indicates the success/failure of the read request. On success, the response
+indicates EOF or contains the data read from the sftp file.
+
+The following code example shows how a read operation can be performed
+on an sftp file using the sftp aio API.
+
+@code
+ssize_t read_chunk(sftp_file file, void *buf, size_t to_read)
+{
+ ssize_t bytes_requested, bytes_read;
+
+ // Variable to store an sftp aio handle
+ sftp_aio aio = NULL;
+
+ // Send a read request to the sftp server
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ // Here its possible that (bytes_requested < to_read) as specified in
+ // the function documentation of sftp_aio_begin_read()
+
+ // Wait for the response of the read request corresponding to the
+ // sftp aio handle stored in the aio variable.
+ bytes_read = sftp_aio_wait_read(&aio, buf, to_read);
+ if (bytes_read == SSH_ERROR) {
+ // handle error
+ }
+
+ return bytes_read;
+}
+@endcode
+
+@subsection sftp_aio_write Using the sftp aio API for writing (a basic example)
+
+For performing an async write operation on a sftp file (see sftp_open()),
+the first step is to call sftp_aio_begin_write() to send a write request to
+the server. The caller is provided an sftp aio handle corresponding to the
+sent write request.
+
+The second step is to pass a pointer to this aio handle to
+sftp_aio_wait_write(), this function waits for the server response which
+indicates the success/failure of the write request.
+
+The following code example shows how a write operation can be performed on an
+sftp file using the sftp aio API.
+
+@code
+ssize_t write_chunk(sftp_file file, void *buf, size_t to_write)
+{
+ ssize_t bytes_requested, bytes_written;
+
+ // Variable to store an sftp aio handle
+ sftp_aio aio = NULL;
+
+ // Send a write request to the sftp server
+ bytes_requested = sftp_aio_begin_write(file, buf, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ // Here its possible that (bytes_requested < to_write) as specified in
+ // the function documentation of sftp_aio_begin_write()
+
+ // Wait for the response of the write request corresponding to
+ // the sftp aio handle stored in the aio variable.
+ bytes_written = sftp_aio_wait_write(&aio);
+ if (bytes_written == SSH_ERROR) {
+ // handle error
+ }
+
+ return bytes_written;
+}
+@endcode
+
+@subsection sftp_aio_actual_use Using the sftp aio API to speed up a transfer
+
+The above examples were provided to introduce the sftp aio API.
+This is not how the sftp aio API is intended to be used, because the
+above usage offers no advantage over the synchronous sftp read/write API
+which does the same thing i.e issue a request and then immediately wait for
+its response.
+
+The facility that the sftp aio API provides is that the user can do
+anything between issuing a request and getting the corresponding response.
+Any number of operations can be performed after calling sftp_aio_begin_*()
+[which issues a request] and before calling sftp_aio_wait_*() [which waits
+for a response]
+
+The code can leverage this feature by calling sftp_aio_begin_*() multiple times
+to issue multiple requests before calling sftp_aio_wait_*() to wait for the
+response of an earlier issued request. This approach will keep a certain number
+of requests outstanding at the client side.
+
+After issuing those requests, while the client code does something else (for
+example waiting for an outstanding request's response, processing an obtained
+response, issuing another request or any other operation the client wants
+to perform), at the same time :
+
+ - Some of those outstanding requests may be travelling over the
+ network towards the server.
+
+ - Some of the outstanding requests may have reached the server and may
+ be queued for processing at the server side.
+
+ - Some of the outstanding requests may have been processed and the
+ corresponding responses may be travelling over the network towards the
+ client.
+
+ - Some of the responses corresponding to the outstanding requests may
+ have already reached the client side.
+
+Clearly in this case, operations that the client performs and operations
+involved in transfer/processing of a outstanding request can occur in
+parallel. Also, operations involved in transfer/processing of two or more
+outstanding requests may also occur in parallel (for example when one request
+travels to the server, another request's response may be incoming towards the
+client). Such kind of parallelism makes the overall transfer faster as compared
+to a transfer performed using the synchronous sftp read/write API.
+
+When the synchronous sftp read/write API is used to perform a transfer,
+a strict sequence is followed:
+
+ - The client issues a single read/write request.
+ - Then waits for its response.
+ - On obtaining the response, the client processes it.
+ - After the processing ends, the client issues the next read/write request.
+
+A file transfer performed in this manner would be slower than the case where
+multiple read/write requests are kept outstanding at the client side. Because
+here at any given time, operations related to transfer/processing of only one
+request/response pair occurs. This is in contrast to the multiple outstanding
+requests scenario where operations related to transfer/processing of multiple
+request/response pairs may occur at the same time.
+
+Although it's true that keeping multiple requests outstanding can speed up a
+transfer, those outstanding requests come at a cost of increased memory
+consumption both at the client side and the server side. Hence care must be
+taken to use a reasonable limit for the number of requests kept outstanding.
+
+The further sections provide code examples to show how uploads/downloads
+can be performed using the sftp aio API and the concept of outstanding requests
+discussed in this section. In those code examples, error handling has been
+ignored and at some places pseudo code has been used for the sake of brevity.
+
+The complete code for performing uploads/downloads using the sftp aio API,
+can be found at https://gitlab.com/libssh/libssh-mirror/-/tree/master.
+
+ - libssh benchmarks for uploads performed using the sftp aio API [See
+ tests/benchmarks/bench_sftp.c]
+ - libssh benchmarks for downloads performed using the sftp aio API. [See
+ tests/benchmarks/bench_sftp.c]
+ - libssh sftp ft API code for performing a local to remote transfer (upload).
+ [See src/sftp_ft.c]
+ - libssh sftp ft API code for performing a remote to local transfer
+ (download). [See src/sftp_ft.c]
+
+@subsection sftp_aio_cap Capping applied by the sftp aio API
+
+Before the code examples for uploads and downloads, its important
+to know about the capping applied by the sftp aio API.
+
+sftp_aio_begin_read() caps the number of bytes the caller can request
+to read from the remote file. That cap is the value of the max_read_length
+field of the sftp_limits_t returned by sftp_limits(). Say that cap is LIM
+and the caller passes x as the number of bytes to read to
+sftp_aio_begin_read(), then (assuming no error occurs) :
+
+ - if x <= LIM, then sftp_aio_begin_read() will request the server
+ to read x bytes from the remote file, and will return x.
+
+ - if x > LIM, then sftp_aio_begin_read() will request the server
+ to read LIM bytes from the remote file and will return LIM.
+
+Hence to request server to read x bytes (> LIM), the caller would have
+to call sftp_aio_begin_read() multiple times, typically in a loop and
+break out of the loop when the summation of return values of the multiple
+sftp_aio_begin_read() calls becomes equal to x.
+
+For the sake of simplicity, the code example for download in the upcoming
+section would always ask sftp_aio_begin_read() to read x <= LIM bytes,
+so that its return value is guaranteed to be x, unless an error occurs.
+
+Similarly, sftp_aio_begin_write() caps the number of bytes the caller
+can request to write to the remote file. That cap is the value of
+max_write_length field of the sftp_limits_t returned by sftp_limits().
+Say that cap is LIM and the caller passes x as the number of bytes to
+write to sftp_aio_begin_write(), then (assuming no error occurs) :
+
+ - if x <= LIM, then sftp_aio_begin_write() will request the server
+ to write x bytes to the remote file, and will return x.
+
+ - if x > LIM, then sftp_aio_begin_write() will request the server
+ to write LIM bytes to the remote file and will return LIM.
+
+Hence to request server to write x bytes (> LIM), the caller would have
+to call sftp_aio_begin_write() multiple times, typically in a loop and
+break out of the loop when the summation of return values of the multiple
+sftp_aio_begin_write() calls becomes equal to x.
+
+For the sake of simplicity, the code example for upload in the upcoming
+section would always ask sftp_aio_begin_write() to write x <= LIM bytes,
+so that its return value is guaranteed to be x, unless an error occurs.
+
+@subsection sftp_aio_download_example Performing a download using the sftp aio API
+
+Terminologies used in the following code snippets :
+
+ - sftp : The sftp_session opened using sftp_new() and initialised using
+ sftp_init()
+
+ - file : The sftp file handle of the remote file to download data
+ from. (See sftp_open())
+
+ - file_size : the size of the sftp file to download. This size can be obtained
+ by statting the remote file to download (e.g by using sftp_stat())
+
+ - We will need to maintain a queue which will be used to store the sftp aio
+ handles corresponding to the outstanding requests.
+
+First, we issue the read requests while ensuring that their count
+doesn't exceed a particular limit decided by us, and the number of bytes
+requested don't exceed the size of the file to download.
+
+@code
+sftp_aio aio = NULL;
+
+// Chunk size to use for the transfer
+size_t chunk_size;
+
+// For the limits structure that would be used
+// by the code to set the chunk size
+sftp_limits_t lim = NULL;
+
+// Max number of requests to keep outstanding at a time
+size_t in_flight_requests = 5;
+
+// Number of bytes for which requests have been sent
+size_t total_bytes_requested = 0;
+
+// Number of bytes which have been downloaded
+size_t bytes_downloaded = 0;
+
+// Buffer to use for the download
+char *buffer = NULL;
+
+// Helper variables
+size_t to_read;
+ssize_t bytes_requested;
+
+// Get the sftp limits
+lim = sftp_limits(sftp);
+if (lim == NULL) {
+ // handle error
+}
+
+// Set the chunk size for download = the max limit for reading
+// The reason for this has been given in the "Capping applied by
+// the sftp aio API" section (Its to make the code simpler)
+//
+// Assigning a size_t type variable a uint64_t type value here,
+// theoretically could cause an overflow, but practically
+// max_read_length would never exceed SIZE_MAX so its okay.
+chunk_size = lim->max_read_length;
+
+buffer = malloc(chunk_size);
+if (buffer == NULL) {
+ // handle error
+}
+
+... // Code to open the remote file (to download) using sftp_open().
+... // Code to stat the remote file's file size.
+... // Code to open the local file in which downloaded data is to be stored.
+... // Code to initialize the queue which will be used to store sftp aio
+ // handles.
+
+for (i = 0;
+ i < in_flight_requests && total_bytes_requested < file_size;
+ ++i) {
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ // Issue a read request
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_read) {
+ // Should not happen for this code, as the to_read is <=
+ // max limit for reading (chunk size), so there is no reason
+ // for sftp_aio_begin_read() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+@endcode
+
+At this point, at max in_flight_requests number of requests may be
+outstanding. Now we wait for the response corresponding to the earliest
+issued outstanding request.
+
+On getting that response, we issue another read request if there are
+still some bytes in the sftp file (to download) for which we haven't sent the
+read request. (This happens when total_bytes_requested < file_size)
+
+This issuing of another read request (under a condition) is done to
+keep the number of outstanding requests equal to the value of the
+in_flight_requests variable.
+
+This process has to be repeated for every remaining outstanding request.
+
+@code
+while (the queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ // Wait for the response of the request corresponding to the aio
+ bytes_read = sftp_aio_wait_read(&aio, buffer, chunk_size);
+ if (bytes_read == SSH_ERROR) {
+ //handle error
+ }
+
+ bytes_downloaded += bytes_read;
+ if (bytes_read != chunk_size && bytes_downloaded != file_size) {
+ // A short read encountered on the remote file before reaching EOF,
+ // short read before reaching EOF should never happen for the sftp aio
+ // API which respects the max limit for reading. This probably
+ // indicates a bad server.
+ }
+
+ // Pseudo code
+ WRITE bytes_read bytes from the buffer into the local file
+ in which downloaded data is to be stored ;
+
+ if (total_bytes_requested == file_size) {
+ // no need to issue more read requests
+ continue;
+ }
+
+ // else issue a read request
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_read) {
+ // Should not happen for this code, as the to_read is <=
+ // max limit for reading (chunk size), so there is no reason
+ // for sftp_aio_begin_read() to return a lesser value.
+ }
+
+ total_bytes_requested += bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+free(buffer);
+sftp_limits_free(lim);
+
+... // Code to destroy the queue which was used to store the sftp aio
+ // handles.
+@endcode
+
+After exiting the while (the queue is not empty) loop, the download
+would've been complete (assuming no error occurs).
+
+@subsection sftp_aio_upload_example Performing an upload using the sftp aio API
+
+Terminologies used in the following code snippets :
+
+ - sftp : The sftp_session opened using sftp_new() and initialised using
+ sftp_init()
+
+ - file : The sftp file handle of the remote file in which uploaded data
+ is to be stored. (See sftp_open())
+
+ - file_size : The size of the local file to upload. This size can be
+ obtained by statting the local file to upload (e.g by using stat())
+
+ - We will need maintain a queue which will be used to store the sftp aio
+ handles corresponding to the outstanding requests.
+
+First, we issue the write requests while ensuring that their count
+doesn't exceed a particular limit decided by us, and the number of bytes
+requested to write don't exceed the size of the file to upload.
+
+@code
+sftp_aio aio = NULL;
+
+// The chunk size to use for the transfer
+size_t chunk_size;
+
+// For the limits structure that would be used by
+// the code to set the chunk size
+sftp_limits_t lim = NULL;
+
+// Max number of requests to keep outstanding at a time
+size_t in_flight_requests = 5;
+
+// Total number of bytes for which write requests have been sent
+size_t total_bytes_requested = 0;
+
+// Buffer to use for the upload
+char *buffer = NULL;
+
+// Helper variables
+size_t to_write;
+ssize_t bytes_requested;
+
+// Get the sftp limits
+lim = sftp_limits(sftp);
+if (lim == NULL) {
+ // handle error
+}
+
+// Set the chunk size for upload = the max limit for writing.
+// The reason for this has been given in the "Capping applied by
+// the sftp aio API" section (Its to make the code simpler)
+//
+// Assigning a size_t type variable a uint64_t type value here,
+// theoretically could cause an overflow, but practically
+// max_write_length would never exceed SIZE_MAX so its okay.
+chunk_size = lim->max_write_length;
+
+buffer = malloc(chunk_size);
+if (buffer == NULL) {
+ // handle error
+}
+
+... // Code to open the local file (to upload) [e.g using open(), fopen()].
+... // Code to stat the local file's file size [e.g using stat()].
+... // Code to open the remote file in which uploaded data will be stored [see
+ // sftp_open()].
+... // Code to initialize the queue which will be used to store sftp aio
+ // handles.
+
+for (i = 0;
+ i < in_flight_requests && total_bytes_requested < file_size;
+ ++i) {
+ to_write = file_size - total_bytes_requested;
+ if (to_write > chunk_size) {
+ to_write = chunk_size;
+ }
+
+ // Pseudo code
+ READ to_write bytes from the local file (to upload) into the buffer;
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_write) {
+ // Should not happen for this code, as the to_write is <=
+ // max limit for writing (chunk size), so there is no reason
+ // for sftp_aio_begin_write() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+@endcode
+
+At this point, at max in_flight_requests number of requests may be
+outstanding. Now we wait for the response corresponding to the earliest
+issued outstanding request.
+
+On getting that response, we issue another write request if there are
+still some bytes in the local file (to upload) for which we haven't sent
+the write request. (This happens when total_bytes_requested < file_size)
+
+This issuing of another write request (under a condition) is done to
+keep the number of outstanding requests equal to the value of the
+in_flight_requests variable.
+
+This process has to be repeated for every remaining outstanding request.
+
+@code
+while (the queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ // Wait for the response of the request corresponding to the aio
+ bytes_written = sftp_aio_wait_write(&aio);
+ if (bytes_written == SSH_ERROR) {
+ // handle error
+ }
+
+ // sftp_aio_wait_write() won't report a short write, so no need
+ // to check for a short write here.
+
+ if (total_bytes_requested == file_size) {
+ // no need to issue more write requests
+ continue;
+ }
+
+ // else issue a write request
+ to_write = file_size - total_bytes_requested;
+ if (to_write > chunk_size) {
+ to_write = chunk_size;
+ }
+
+ // Pseudo code
+ READ to_write bytes from the local file (to upload) into a buffer;
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_write) {
+ // Should not happen for this code, as the to_write is <=
+ // max limit for writing (chunk size), so there is no reason
+ // for sftp_aio_begin_write() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+free(buffer);
+
+... // Code to destroy the queue which was used to store the sftp aio
+ // handles.
+@endcode
+
+After exiting the while (the queue is not empty) loop, the upload
+would've been complete (assuming no error occurs).
+
+@subsection sftp_aio_free Example showing the usage of sftp_aio_free()
+
+The purpose of sftp_aio_free() was discussed at the beginning of this page,
+the following code example shows how it can be used during cleanup.
+
+@code
+void print_sftp_error(sftp_session sftp)
+{
+ if (sftp == NULL) {
+ return;
+ }
+
+ fprintf(stderr, "sftp error : %d\n", sftp_get_error(sftp));
+ fprintf(stderr, "ssh error : %s\n", ssh_get_error(sftp->session));
+}
+
+// Returns 0 on success, -1 on error
+int write_strings(sftp_file file)
+{
+ const char * strings[] = {
+ "This is the first string",
+ "This is the second string",
+ "This is the third string",
+ "This is the fourth string"
+ };
+
+ size_t string_count = sizeof(strings) / sizeof(strings[0]);
+ size_t i;
+
+ sftp_session sftp = NULL;
+ sftp_aio aio = NULL;
+
+ int rc;
+
+ if (file == NULL) {
+ return -1;
+ }
+
+ ... // Code to initialize the queue which will be used to store sftp aio
+ // handles
+
+ sftp = file->sftp;
+ for (i = 0; i < string_count; ++i) {
+ rc = sftp_aio_begin_write(file,
+ strings[i],
+ strlen(strings[i]),
+ &aio);
+ if (rc == SSH_ERROR) {
+ print_sftp_error(sftp);
+ goto err;
+ }
+
+ // Pseudo code
+ ENQUEUE aio in the queue of sftp aio handles
+ }
+
+ for (i = 0; i < string_count; ++i) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ rc = sftp_aio_wait_write(&aio);
+ if (rc == SSH_ERROR) {
+ print_sftp_error(sftp);
+ goto err;
+ }
+ }
+
+
+ ... // Code to destroy the queue in which sftp aio handles were
+ // stored
+
+ return 0;
+
+err:
+
+ while (queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ sftp_aio_free(aio);
+ }
+
+ ... // Code to destroy the queue in which sftp aio handles were
+ // stored.
+
+ return -1;
+}
+
+@endcode
+
+*/
diff --git a/doc/shell.dox b/doc/shell.dox
index d770f27a..35fc5c9a 100644
--- a/doc/shell.dox
+++ b/doc/shell.dox
@@ -26,7 +26,7 @@ The code sample below achieves these tasks:
@code
int shell_session(ssh_session session)
{
- ssh_channel channel;
+ ssh_channel channel = NULL;
int rc;
channel = ssh_channel_new(session);
@@ -65,8 +65,17 @@ to as a "pty", for "pseudo-teletype". The remote processes won't see the
difference with a real text-oriented terminal.
If needed, you request the pty with the function ssh_channel_request_pty().
-Then you define its dimensions (number of rows and columns)
-with ssh_channel_change_pty_size().
+If you want define its dimensions (number of rows and columns),
+call ssh_channel_request_pty_size() instead. It's also possible to change
+the dimensions after creating the pty with ssh_channel_change_pty_size().
+
+These two functions configure the pty using the same terminal modes that
+stdin has. If stdin isn't a TTY, they use default modes that configure
+the pty with in canonical mode and e.g. preserving CR and LF characters.
+If you want to change the terminal modes used by the pty (e.g. to change
+CRLF handling), use ssh_channel_request_pty_size_modes(). This function
+accepts an additional "modes" buffer that is expected to contain encoded
+terminal modes according to RFC 4254 section 8.
Be your session interactive or not, the next step is to request a
shell with ssh_channel_request_shell().
diff --git a/doc/that_style/LICENSE b/doc/that_style/LICENSE
deleted file mode 100644
index eac42e70..00000000
--- a/doc/that_style/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 Jan-Lukas Wynen
-
-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.
diff --git a/doc/that_style/README.md b/doc/that_style/README.md
deleted file mode 100644
index 75727209..00000000
--- a/doc/that_style/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# that style
-A plain, more modern HTML style for Doxygen
-
-## Requirements
-- Doxygen (tested with version 1.8.13)
-- *optional*: a sass/scss compiler if you want to modify the style
-
-## Simple usage
-Tell Doxygen about the files for that style as shown in [doxyfile.conf](doxyfile.conf). You might need to adjust the
-paths depending on where you installed that style.
-When you run Doxygen, all files are copied into to generated HTML folder. So you don't need to keep the originals around
-unless you want to re-generate the documentation.
-
-## Advanced
-that style uses a custom javascript to hack some nice stripes into some tables. It has to be loaded from HTML. Hence you need
-to use the provided custom header. Since its default content may change when Doxygen is updated, there might be syntax error in
-the generated HTML. If this is the case, you can remove the custom header (adjust your doxyfile.conf). This has no
-disadvantages other than removing the stripes.
-
-[that_style.css](that_style.css) was generated from the scss files in the folder [sass](sass). If you want to change the style,
-use those files in order to have better control. For instance, you can easily change most colors by modifying the variables
-in the beginning of [that_style.scss](sass/that_style.scss).
diff --git a/doc/that_style/header.html b/doc/that_style/header.html
deleted file mode 100644
index 3da46393..00000000
--- a/doc/that_style/header.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-$projectname: $title
-$title
-
-
-
-$treeview
-$search
-$mathjax
-
-
-$extrastylesheet
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
$projectname
- $projectnumber
-
-
$projectbrief
-
-
-
-
-
-
$projectbrief
-
-
-
-
-
-
$searchbox
-
-
-
-
-
-
-
-
diff --git a/doc/that_style/img/doc.svg b/doc/that_style/img/doc.svg
deleted file mode 100644
index 68e1ba01..00000000
--- a/doc/that_style/img/doc.svg
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/folderclosed.svg b/doc/that_style/img/folderclosed.svg
deleted file mode 100644
index e53ec906..00000000
--- a/doc/that_style/img/folderclosed.svg
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/folderopen.svg b/doc/that_style/img/folderopen.svg
deleted file mode 100644
index 1ab7b78a..00000000
--- a/doc/that_style/img/folderopen.svg
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/mag_glass.svg b/doc/that_style/img/mag_glass.svg
deleted file mode 100644
index e21a0048..00000000
--- a/doc/that_style/img/mag_glass.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/nav_edge_inter.svg b/doc/that_style/img/nav_edge_inter.svg
deleted file mode 100644
index f04f10fd..00000000
--- a/doc/that_style/img/nav_edge_inter.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/nav_edge_left.svg b/doc/that_style/img/nav_edge_left.svg
deleted file mode 100644
index ca1adf4c..00000000
--- a/doc/that_style/img/nav_edge_left.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/nav_edge_right.svg b/doc/that_style/img/nav_edge_right.svg
deleted file mode 100644
index bb338153..00000000
--- a/doc/that_style/img/nav_edge_right.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/splitbar_handle.svg b/doc/that_style/img/splitbar_handle.svg
deleted file mode 100644
index e0dc90da..00000000
--- a/doc/that_style/img/splitbar_handle.svg
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
diff --git a/doc/that_style/img/sync_off.png b/doc/that_style/img/sync_off.png
deleted file mode 100644
index 9d286d9c..00000000
Binary files a/doc/that_style/img/sync_off.png and /dev/null differ
diff --git a/doc/that_style/img/sync_on.png b/doc/that_style/img/sync_on.png
deleted file mode 100644
index b8434e80..00000000
Binary files a/doc/that_style/img/sync_on.png and /dev/null differ
diff --git a/doc/that_style/js/striped_bg.js b/doc/that_style/js/striped_bg.js
deleted file mode 100644
index 97ae0a8e..00000000
--- a/doc/that_style/js/striped_bg.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Adds extra CSS classes "even" and "odd" to .memberdecls to allow
-// striped backgrounds.
-function MemberDeclsStriper () {
- var counter = 0;
-
- this.stripe = function() {
- $(".memberdecls tbody").children().each(function(i) {
-
- // reset counter at every heading -> always start with even
- if ($(this).is(".heading")) {
- counter = 0;
- }
-
- // add extra classes
- if (counter % 2 == 1) {
- $(this).addClass("odd");
- }
- else {
- $(this).addClass("even");
- }
-
- // advance counter at every separator
- // this is the only way to reliably detect which table rows belong together
- if ($(this).is('[class^="separator"]')) {
- counter++;
- }
- });
- }
-}
-
-// execute the function
-$(document).ready(new MemberDeclsStriper().stripe);
diff --git a/doc/that_style/that_style.css b/doc/that_style/that_style.css
deleted file mode 100644
index 846353ca..00000000
--- a/doc/that_style/that_style.css
+++ /dev/null
@@ -1,1431 +0,0 @@
-@charset "UTF-8";
-/*
- * My own little style
- */
-body, table, div, p, dl {
- font: 400 14px/22px Roboto,sans-serif; }
-
-h1.groupheader {
- font-size: 150%; }
-
-.title {
- font: 400 14px/28px Roboto,sans-serif;
- font-size: 150%;
- font-weight: bold;
- margin: 10px 2px; }
-
-h2.groupheader {
- border-bottom: 1px solid #555555;
- color: black;
- font-size: 200%;
- font-weight: bold;
- margin-top: 1.75em;
- padding-top: 1em;
- padding-bottom: 4px;
- width: 100%; }
-
-tr.heading h2 {
- border-bottom: 1px solid #a5a5a5;
- font-size: 150%;
- margin-top: 6px;
- margin-bottom: 6px;
- padding-top: 3px;
- padding-bottom: 7px; }
-
-h2.groupheader a {
- margin-left: 1%; }
-
-h3.groupheader {
- font-size: 100%; }
-
-h1, h2, h3, h4, h5, h6 {
- -webkit-transition: none;
- -moz-transition: none;
- -o-transition: none;
- transition: none;
- margin-right: 15px; }
- h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
- text-shadow: none;
- color: #5f082b; }
-
-dt {
- font-weight: bold; }
-
-div.multicol {
- -moz-column-gap: 1em;
- -webkit-column-gap: 1em;
- -moz-column-count: 3;
- -webkit-column-count: 3; }
-
-p.startli, p.startdd {
- margin-top: 2px; }
-
-p.starttd {
- margin-top: 0px; }
-
-p.endli {
- margin-bottom: 0px; }
-
-p.enddd {
- margin-bottom: 4px; }
-
-p.endtd {
- margin-bottom: 2px; }
-
-#top {
- border: none;
- position: relative;
- z-index: 100;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 0 8px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 0 8px rgba(0, 0, 0, 0.3);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 0 8px rgba(0, 0, 0, 0.3);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 0 8px rgba(0, 0, 0, 0.3); }
-
-caption {
- font-weight: bold; }
-
-span.legend {
- font-size: 70%;
- text-align: center; }
-
-h3.version {
- font-size: 90%;
- text-align: center; }
-
-div.qindex, div.navtab {
- background-color: #ffffff;
- border: none;
- text-align: center; }
-
-div.qindex, div.navpath {
- width: 100%;
- line-height: 140%; }
-
-div.navtab {
- margin-right: 15px; }
-
-a, a:visited {
- color: #00549f;
- font-weight: normal;
- text-decoration: none; }
-
-.contents a:visited {
- color: #4665A2; }
-
-a:hover {
- text-decoration: underline; }
-
-a.qindex {
- font-weight: bold;
- text-transform: uppercase; }
-
-a.qindexHL {
- font-weight: bold;
- background-color: #9CAFD4;
- color: #ffffff;
- border: 1px double #869DCA; }
-
-.contents a.qindexHL:visited {
- color: #ffffff; }
-
-a.el, a.el:visited {
- font-weight: normal;
- color: #00549f; }
-
-a.elRef, a.elRef:visited {
- font-family: monospace;
- color: #006bc8; }
-
-/*
- * The main menu at the top
- */
-#main-menu {
- background-image: none;
- background: #414141;
- padding: 0; }
-
-.sm-dox > li:not(:last-child) > a {
- background-image: none;
- text-shadow: none;
- color: white;
- font-weight: normal;
- letter-spacing: 1px;
- font-size: 11pt;
- text-transform: uppercase; }
-
-.sm-dox > li:not(:last-child) > a:hover,
-.sm-dox > li:not(:last-child) > a.highlighted {
- background-color: #5f082b; }
-
-.sm-dox a span.sub-arrow {
- border-color: white transparent transparent; }
-
-.sm-dox ul {
- border: none;
- -moz-border-radius: 0 !important;
- -webkit-border-radius: 0 !important;
- border-radius: 0 !important;
- padding: 0;
- background: #414141;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
- .sm-dox ul a {
- background: inherit;
- color: white;
- font-weight: normal;
- letter-spacing: 1px;
- font-size: 11pt; }
- .sm-dox ul a:hover {
- background: #5f082b;
- color: white;
- font-weight: normal;
- letter-spacing: 1px;
- font-size: 11pt; }
- .sm-dox ul a.highlighted {
- background: #5f082b;
- color: white;
- font-weight: normal;
- letter-spacing: 1px;
- font-size: 11pt; }
- .sm-dox ul a span.sub-arrow {
- /* this sets the color of the arrow */
- border-color: white transparent transparent; }
-
-dl.el {
- margin-left: -1cm; }
-
-div.ah, span.ah {
- background: none;
- color: black;
- margin-bottom: 3px;
- margin-top: 3px;
- padding: 0.2em;
- border: none;
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- border-radius: 0;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none;
- font: 14pt monospace;
- font-weight: bold;
- text-transform: uppercase; }
-
-div.classindex ul {
- list-style: none;
- padding-left: 0; }
-
-div.classindex span.ai {
- display: inline-block; }
-
-div.groupHeader {
- margin-left: 16px;
- margin-top: 12px;
- font-weight: bold; }
-
-div.groupText {
- margin-left: 16px;
- font-style: italic; }
-
-body {
- background-color: #ffffff;
- color: black;
- margin: 0; }
-
-#doc-content {
- background-color: #111;
- color: #ffffff; }
-
-div.contents {
- color: black;
- background-color: #ffffff;
- padding: 0;
- margin: 5pt; }
- div.contents hr {
- display: none;
- visibility: hidden; }
-
-div.header + div.contents {
- padding: 1ex;
- margin: 0 5pt 5pt 5pt; }
-
-div.textblock {
- padding: 1ex 1ex 0 1ex; }
-
-div.textblock + ul {
- padding-bottom: 1%; }
-
-img.footer {
- border: 0px;
- vertical-align: middle; }
-
-/*
- Basic styling for fragments shared by all themes.
-*/
-div.fragment {
- padding: 4px;
- margin: 4px 8px 4px 2px;
- color: #bebebe;
- background-color: #323232;
- border: 3px solid #e8e8e8;
- border-radius: 2px;
- overflow-y: hidden;
- overflow-x: auto;
- position: relative; }
-
-div.line {
- font-family: monospace, fixed;
- font-size: 13px;
- min-height: 13px;
- line-height: 1.0;
- text-indent: -53px;
- margin: 0px;
- padding: 1px 0 1px 53px;
- white-space: pre;
- -webkit-transition: background-color;
- -moz-transition: background-color;
- -o-transition: background-color;
- transition: background-color;
- -webkit-duration: 0s;
- -moz-duration: 0s;
- -o-duration: 0s;
- duration: 0s; }
- div.line:hover {
- background-color: #1a1a1a; }
- div.line::after {
- content: "\000A";
- white-space: pre; }
-
-span.lineno {
- padding-right: 4px;
- text-align: right;
- color: black;
- height: 100px;
- white-space: pre;
- border-right: 3px solid #1d7567;
- background-color: #323232; }
-
-span.lineno a, span.lineno a:visited {
- background-color: inherit;
- color: #1e595a; }
-
-span.lineno a:hover {
- background-color: #C8C8C8;
- text-decoration: none; }
-
-.lineno {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none; }
-
-div.fragment {
- color: #bebebe;
- background-color: #323232; }
-
-div.fragment::before {
- background-color: #1a1a1a;
- border-right: 1px solid #3e3e3e; }
-
-div.line:hover {
- background-color: #1a1a1a; }
-
-span.lineno {
- color: #969696;
- background-color: #323232;
- border-right: 1px solid #3e3e3e; }
-
-span.lineno a, span.lineno a:visited {
- background-color: inherit;
- color: #dcdcdc; }
-
-span.lineno a:hover {
- background-color: #323232; }
-
-a.code, a.code:visited {
- color: #6cc7eb; }
-
-a.codeRef, a.codeRef:visited {
- color: #3d95e6; }
-
-span.keyword {
- color: #98f77a;
- font-weight: bold; }
-
-span.keywordtype {
- color: #ffa0a0; }
-
-span.keywordflow {
- color: #98f77a;
- font-weight: bold; }
-
-span.comment {
- color: #999;
- font-style: oblique; }
-
-span.preprocessor {
- color: #cd5c57; }
-
-span.stringliteral {
- color: #64b041; }
-
-span.charliteral {
- color: #64b041; }
-
-blockquote {
- background-color: #F7F8FB;
- border-left: 2px solid #9CAFD4;
- margin: 0 24px 0 4px;
- padding: 0 12px 0 16px; }
-
-/*
- * The search box
- */
-.sm-dox > li:last-child {
- margin-right: 10pt; }
-
-#MSearchBox {
- border: 2px inset black;
- display: table;
- width: 350px;
- height: 26px;
- background: white;
- margin-top: 5px; }
- #MSearchBox .left {
- background-image: none;
- display: table-cell;
- width: 100%;
- height: inherit;
- left: 0; }
- #MSearchBox .right {
- background-image: none;
- width: 0;
- display: none;
- visibility: hidden; }
-
-nav > #MSearchBox {
- border: 2px solid #666666;
- margin: 5px 10pt 0 0;
- height: 22px; }
-
-#MSearchSelect, .left #MSearchSelect {
- left: 0;
- background-image: url("mag_glass.svg");
- width: 22px;
- height: 22px;
- padding: 22px 22px 0 0;
- margin: 0 4px 0 4px;
- box-sizing: border-box; }
-
-#MSearchField {
- background-image: none;
- display: table-cell;
- margin: 0;
- margin-left: 30px;
- width: calc(100% - 34px);
- height: 22px;
- font: 11pt sans-serif; }
-
-#MSearchSelectWindow {
- background-color: #414141;
- padding: 0;
- border: solid 1px black;
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- border-radius: 0;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-a.SelectItem {
- color: white;
- padding: 3px 4px;
- font: 10pt sans-serif;
- letter-spacing: 1px; }
- a.SelectItem:hover {
- background-color: #5f082b;
- color: white; }
- a.SelectItem:focus, a.SelectItem:active {
- color: white; }
-
-#MSearchResultsWindow {
- background-color: white;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-table.memberdecls {
- width: 100%;
- border-spacing: 0px;
- padding: 0px;
- margin-top: 7px;
- background-color: #ffffff;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-.memberdecls tbody {
- background-color: #ffffff; }
-
-.memberdecls .odd {
- background: #f6f6f6; }
-
-/* all but last separator show a line */
-.memberdecls tr[class^="separator"]:not(:last-child) .memSeparator {
- border-bottom: 1px solid #c5c5c5;
- line-height: 1px;
- margin: 0;
- padding: 0; }
-
-.memberdecls tr[class^="separator"]:last-child .memSeparator {
- border-bottom: none;
- line-height: 0;
- margin: 0;
- padding: 0; }
-
-table.fieldtable {
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- border-radius: 0;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none; }
-
-.memberdecls td, .fieldtable tr {
- background-color: inherit; }
-
-.fieldtable th {
- display: none;
- height: 0;
- visibility: hidden; }
-
-td.fieldname {
- color: #820a32;
- font-family: monospace;
- font-weight: bold; }
-
-th.markdownTableHeadLeft, th.markdownTableHeadRight,
-th.markdownTableHeadCenter, th.markdownTableHeadNone {
- background-color: #414141;
- color: white; }
-
-.memItemLeft, .memItemRight,
-.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
- font-family: monospace;
- background-color: #ffffff;
- border: none;
- margin: 4px;
- padding: 1px 0 0 8px; }
-
-.mdescLeft, .mdescRight {
- background-color: #ffffff;
- border: none;
- margin: 4px;
- padding: 1px 0 0 8px; }
-
-.mdescLeft, .mdescRight {
- padding: 0px 8px 4px 8px;
- color: #555; }
-
-.memItemLeft, .memTemplItemLeft {
- white-space: nowrap; }
-
-.memItemRight {
- width: 100%; }
-
-.memTemplParams {
- color: black;
- white-space: nowrap;
- font-size: 100%; }
-
-/* Styles for detailed member documentation */
-.memtitle {
- padding: 8px;
- border: none;
- margin-bottom: -1px;
- background-image: none;
- background-color: #f6f6f6;
- line-height: 1.25;
- font-weight: bold;
- color: black;
- float: left;
- z-index: 0;
- position: relative;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-.permalink {
- font-size: 100%;
- display: inline-block;
- vertical-align: middle; }
-
-/* replace content of permalinks */
-.permalink a {
- visibility: hidden; }
-
-.permalink a:after {
- content: "§";
- visibility: visible;
- display: block;
- position: absolute;
- color: black;
- top: 20%; }
-
-.permalink a:visited {
- color: black; }
-
-.memtemplate {
- font-size: 100%;
- color: black;
- font-family: monospace;
- font-weight: normal;
- margin-left: 9px; }
-
-.memnav {
- background-color: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
- margin: 2px;
- margin-right: 15px;
- padding: 2px; }
-
-.mempage {
- width: 100%; }
-
-.memitem {
- font-family: monospace;
- padding: 0;
- margin-bottom: 10px;
- margin-right: 5px;
- -webkit-transition: none;
- -moz-transition: none;
- -ms-transition: none;
- -o-transition: none;
- transition: none;
- display: table !important;
- width: 100%;
- background-color: #f6f6f6;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-.memitem.glow {
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none; }
-
-.memname {
- font-family: monospace;
- font-weight: 400;
- margin-left: 6px; }
-
-.memname td {
- vertical-align: bottom; }
-
-.memproto, dl.reflist dt {
- border: none;
- padding: 6px 0px 6px 0px;
- color: black;
- font-weight: bold;
- text-shadow: none;
- background-color: #f6f6f6;
- position: relative;
- z-index: 1;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none; }
-
-.overload {
- font-family: "courier new",courier,monospace;
- font-size: 65%; }
-
-.memdoc, dl.reflist dd {
- border: none;
- border-left: 4px solid #5f082b;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- -webkit-border-bottom-left-radius: 0px;
- -webkit-border-bottom-right-radius: 0px;
- -moz-border-bottom-left-radius: 0px;
- -moz-border-bottom-right-radius: 0px;
- padding: 2px 1% 2px 1%;
- margin: 1%;
- background-color: #ffffff;
- background-image: none;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none;
- /* allow movement of elements inside */
- display: flex;
- flex-direction: column; }
-
-/* overrides for docs on individual pages */
-.memtitle:nth-child(2) {
- width: 0;
- height: 0;
- display: none;
- visibility: hidden; }
-
-.memitem:nth-child(3) {
- margin: 0;
- margin-top: 0.5%;
- background-color: #ffffff;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none; }
-
-.memitem:nth-child(3) .memproto {
- padding: 10px;
- background-color: #ffffff;
- margin-bottom: 10px; }
-
-.memitem:nth-child(3) .memproto::after {
- content: "";
- width: 99%;
- height: 1px;
- position: absolute;
- bottom: -10px;
- left: 0.5%;
- background: #666; }
-
-.memitem:nth-child(3) .memdoc {
- border: none;
- padding: 0; }
-
-.memitem:nth-child(3) table.memname {
- background-color: #f6f6f6;
- border-collapse: collapse;
- border-spacing: initial;
- border: 1px solid #aaa; }
-
-.memitem:nth-child(3) table.memname tr:not(:last-child) {
- border-bottom: 1px dashed #aaa; }
-
-dl.reflist dt {
- padding: 5px;
- z-index: 0;
- /* cover the top shadow of dd */
- position: relative;
- -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.35), 0 0 8px rgba(0, 0, 0, 0.2); }
-
-/* cover up the shadow at the bottom */
-dl.reflist dt::after {
- content: " ";
- width: 100%;
- display: block;
- height: 8px;
- position: absolute;
- background-color: #f6f6f6;
- left: 0;
- bottom: -8px; }
-
-dl.reflist dd {
- border-left: 4px solid #5f082b;
- padding: 2px 1% 2px 1%;
- margin: 8px 8px 24px 8px;
- outline: 8px solid #f6f6f6;
- -moz-box-shadow: 0 0 4px 8px rgba(0, 0, 0, 0.35), 0 0 8px 8px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 4px 8px rgba(0, 0, 0, 0.35), 0 0 8px 8px rgba(0, 0, 0, 0.2);
- -o-box-shadow: 0 0 4px 8px rgba(0, 0, 0, 0.35), 0 0 8px 8px rgba(0, 0, 0, 0.2);
- box-shadow: 0 0 4px 8px rgba(0, 0, 0, 0.35), 0 0 8px 8px rgba(0, 0, 0, 0.2); }
-
-dl.reflist dd p::before {
- font-size: 85%;
- content: "\25B6\00A0\00A0";
- display: inline-block;
- width: 12pt; }
-
-dl.reflist dd p {
- margin-top: 4px;
- margin-bottom: 4px; }
-
-.paramkey {
- text-align: right; }
-
-.paramtype {
- font-family: monospace;
- white-space: nowrap;
- color: #002546; }
-
-.paramname {
- color: black;
- font-family: monospace;
- white-space: nowrap; }
-
-.paramname em {
- color: #820a32;
- font-style: normal; }
-
-.paramname code {
- color: #404040;
- line-height: 14px; }
-
-.params, .retval, .exception, .tparams {
- margin-left: 0;
- padding-left: 0;
- margin-bottom: -0.25em; }
-
-.params dt, .tparams dt {
- margin-bottom: 0.5em; }
-
-.params .paramname, .tparams .paramname, .retval .paramname, .exception .paramname {
- color: #820a32;
- font-family: monospace;
- font-weight: bold;
- vertical-align: top; }
-
-.params .paramtype, .tparams .paramtype {
- font-family: monospace;
- font-style: italic;
- vertical-align: top; }
-
-.params .paramdir, .tparams .paramdir {
- font-family: "courier new",courier,monospace;
- vertical-align: top; }
-
-/* line over parameters docs */
-.params, .tparams {
- border-collapse: collapse; }
-
-.params tr, .tparams tr {
- -moz-box-shadow: 0 -2px 0 -1px #606060;
- -webkit-box-shadow: 0 -2px 0 -1px #606060;
- -o-box-shadow: 0 -2px 0 -1px #606060;
- box-shadow: 0 -2px 0 -1px #606060; }
-
-.params .paramname, .tparams .paramname {
- border-top: 2px solid #5f082b;
- padding-right: 5pt; }
-
-.params td, .tparams td {
- padding-bottom: 1em; }
-
-table.mlabels {
- border-spacing: 0px; }
-
-td.mlabels-left {
- width: 100%;
- padding: 0px; }
-
-td.mlabels-right {
- vertical-align: bottom;
- padding: 0px;
- white-space: nowrap; }
-
-span.mlabels {
- margin-left: 8px; }
-
-span.mlabel {
- background-color: #444444;
- border: none;
- border-radius: 3px;
- text-shadow: none;
- color: white;
- margin-right: 4px;
- padding: 3px 5px;
- font-size: 8pt;
- white-space: nowrap;
- vertical-align: middle; }
-
-.memdoc .definition {
- position: relative;
- padding-top: 0.5em;
- /* move definition line to bottom of memdoc */
- order: 3; }
- .memdoc .definition::before {
- content: "";
- width: 33%;
- height: 1px;
- border-top: 1px solid black;
- position: absolute;
- top: 0; }
-
-table.directory {
- border-top: 1px solid #c5c5c5;
- border-bottom: 1px solid #c5c5c5;
- border-collapse: collapse;
- width: 100%;
- font: 400 14px Roboto,sans-serif; }
- table.directory tr {
- background-color: white !important; }
- table.directory tr.even {
- background-color: #f6f6f6 !important; }
- table.directory tr:hover {
- background-color: #e6e6e6 !important; }
- table.directory td.entry {
- padding: 1.5pt 3pt 1.5pt 3pt;
- white-space: normal; }
-
-div.directory {
- border: none; }
- div.directory table.directory tr {
- line-height: 17pt; }
- div.directory table.directory td {
- margin: 0;
- padding: 0.5pt 6pt 0.5pt 0;
- vertical-align: middle; }
- div.directory table.directory td.entry {
- white-space: nowrap; }
- div.directory table.directory td.desc {
- width: 100%;
- padding-left: 6pt;
- border-left: 1px solid rgba(0, 0, 0, 0.05); }
-
-.directory .levels {
- white-space: nowrap;
- width: 100%;
- text-align: right;
- font-size: 9pt; }
- .directory .levels span {
- cursor: pointer;
- padding-left: 2px;
- padding-right: 2px;
- color: #00549f; }
- .directory .levels span:hover {
- text-decoration: underline; }
-
-/*
- * The tree view on the left
- */
-.arrow {
- color: black;
- cursor: pointer;
- font-size: 80%;
- display: inline-block;
- width: 16px;
- height: 22px;
- margin-left: 4px;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none; }
- .arrow:hover {
- color: black; }
-
-#selected .arrow {
- color: white; }
- #selected .arrow:hover {
- color: #d2d2d2; }
-
-#nav-tree {
- background-image: none;
- background-color: white; }
- #nav-tree .item {
- margin: 0; }
- #nav-tree .item:hover {
- background-color: #d2d2d2; }
- #nav-tree .selected {
- background-image: none;
- background-color: #5f082b;
- color: white;
- text-shadow: none; }
- #nav-tree .selected:hover {
- background-image: none;
- background-color: #5f082b;
- color: white;
- text-shadow: none; }
- #nav-tree a {
- color: black; }
-
-.ui-resizable-e {
- background: #808080 url("splitbar_handle.svg") no-repeat center;
- border-right: solid 1px #c0c0c0;
- border-left: solid 1px black; }
- .ui-resizable-e:hover {
- background-color: #606060; }
-
-.icon {
- font-family: monospace;
- font-weight: bold;
- font-size: 12px;
- height: 15px;
- width: 15px;
- display: inline-block;
- background-color: #444444;
- color: white;
- text-align: center;
- border-radius: 3px;
- margin: 0;
- padding-top: 1px;
- text-indent: -1px; }
-
-.icona {
- width: 0;
- height: 0;
- display: none;
- visibility: hidden; }
-
-.iconfopen {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image: url("folderopen.svg");
- background-position: 0;
- background-repeat: no-repeat;
- vertical-align: top;
- display: inline-block; }
-
-.iconfclosed {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image: url("folderclosed.svg");
- background-position: 0;
- background-repeat: no-repeat;
- vertical-align: top;
- display: inline-block; }
-
-.icondoc {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image: url("doc.svg");
- background-position: 0;
- background-repeat: no-repeat;
- vertical-align: top;
- display: inline-block; }
-
-div.dynheader {
- margin-top: 8px;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none; }
-
-address {
- font-style: normal;
- color: #2A3D61; }
-
-table.doxtable caption {
- caption-side: top; }
-
-table.doxtable {
- border-collapse: collapse;
- margin-top: 4px;
- margin-bottom: 4px; }
-
-table.doxtable td, table.doxtable th {
- border: 1px solid #444444;
- padding: 3px 7px 2px; }
-
-table.doxtable th {
- background-color: #444444;
- color: #FFFFFF;
- font-size: 110%;
- padding-bottom: 4px;
- padding-top: 5px; }
-
-/*
- * The line at the bottom
- */
-.navpath {
- /* intermediate navelems */
- /* first navelem */
- /* last navelem */ }
- .navpath ul {
- font-size: 11px;
- background-image: none;
- height: 30px;
- line-height: 30px;
- color: black;
- border: none;
- border-top: 1px solid #808080;
- overflow: hidden;
- margin: 0px;
- padding: 0px; }
- .navpath li:not(:first-child) {
- list-style-type: none;
- float: left;
- padding-left: 18px;
- padding-right: 10px;
- color: black;
- background-color: white;
- background-image: url("nav_edge_inter.svg");
- background-repeat: no-repeat;
- background-position: left -1px;
- background-size: auto 100%; }
- .navpath li:first-child {
- list-style-type: none;
- float: left;
- padding-left: 15px;
- padding-right: 10px;
- color: black;
- background-color: white;
- background-image: none; }
- .navpath li:nth-last-child(2) {
- list-style-type: none;
- float: left;
- padding-left: 10px;
- padding-right: 15px;
- color: white;
- background-color: #5f082b;
- background-image: url("nav_edge_right.svg");
- background-repeat: no-repeat;
- background-position: right -1px;
- background-size: auto 100%; }
- .navpath li:nth-last-child(2):not(:first-child) {
- list-style-type: none;
- float: left;
- padding-left: 15px;
- padding-right: 15px;
- color: white;
- background-color: #5f082b;
- background-image: url("nav_edge_left.svg"), url("nav_edge_right.svg");
- background-repeat: no-repeat;
- background-position: -1px -1px, right -1px;
- background-size: auto 100%; }
- .navpath li.navelem a, .navpath .navpath li.navelem b {
- height: 32px;
- display: block;
- text-decoration: none;
- outline: none;
- color: inherit;
- font-family: Roboto,sans-serif;
- text-shadow: none;
- text-decoration: none;
- font-weight: normal; }
- .navpath li.navelem a:hover {
- color: inherit;
- text-decoration: underline; }
- .navpath li.footer {
- list-style-type: none;
- float: right;
- padding-left: 0;
- padding-right: 10px;
- background-color: #d5d5d5;
- background-image: none;
- color: black;
- font-size: 8pt; }
- .navpath li.footer:before {
- content: "";
- width: 13px;
- height: 30px;
- display: inline-block;
- float: left;
- background-image: url("nav_edge_right.svg");
- background-repeat: no-repeat;
- background-position: right 0;
- background-size: auto 100%;
- /* flip the element horizontally */
- -moz-transform: scaleX(-1);
- -o-transform: scaleX(-1);
- -webkit-transform: scaleX(-1);
- transform: scaleX(-1);
- filter: FlipH;
- -ms-filter: "FlipH"; }
-
-div.summary {
- -webkit-order: 2;
- order: 2;
- float: right;
- font-size: 8pt;
- padding-right: 5px;
- width: 50%;
- text-align: right;
- margin-right: 0.5%; }
-
-div.summary a {
- white-space: nowrap; }
-
-table.classindex {
- margin: 10px;
- white-space: nowrap;
- margin-left: 1%;
- margin-right: 1%;
- width: 98%;
- border: none;
- border-top: 1px solid black;
- border-bottom: 1px solid black;
- border-spacing: 0.5em;
- padding: 0; }
-
-div.ingroups {
- font-size: 8pt;
- width: 50%;
- text-align: left; }
-
-div.ingroups a {
- white-space: nowrap; }
-
-div.header {
- display: -webkit-flex;
- display: flex;
- justify-content: space-between;
- background-image: none;
- background-color: #ffffff;
- color: black;
- margin: 5pt 5pt 0 5pt;
- padding: 0 1ex 0 1ex;
- align-items: center;
- justify-content: center;
- border-bottom: none;
- position: relative; }
-
-div.header::after {
- content: "";
- height: 2px;
- width: 99%;
- position: absolute;
- bottom: -5px;
- left: 0.5%;
- background: #666; }
-
-div.headertitle {
- -webkit-order: 1;
- order: 1;
- margin-right: auto;
- text-align: center; }
-
-dl {
- padding: 0; }
-
-dl.section {
- margin-left: 0px;
- padding-left: 0px; }
-
-dl.section > dt {
- font-weight: bold;
- font-family: sans-serif; }
-
-dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant,
-dl.deprecated, dl.todo, dl.test, dl.bug {
- margin-left: -7px;
- padding-left: 3px; }
-
-dl.note {
- padding-left: 7px;
- border: none; }
-
-dl.warning {
- background-color: #ffe6ea;
- border: 1px solid #ff0728;
- border-left: 4px solid #ff0728;
- padding-top: 4px;
- padding-bottom: 3px; }
-
-dl.attention {
- border-left: 4px solid #ff0728; }
-
-dl.pre, dl.post, dl.invariant {
- background-color: #f0ffe6;
- border: 1px solid #5eb82a;
- border-left: 4px solid #5eb82a; }
-
-dl.deprecated {
- background-color: #f6f6f6;
- border: 1px solid black; }
-
-dl.todo {
- border-left: 4px solid #e8d500; }
-
-dl.test {
- border-left: 4px solid #00549f; }
-
-dl.bug {
- background-color: #f6f6f6;
- border: 1px solid #cc071e;
- border-left: 4px solid #cc071e; }
-
-dl.section dd {
- margin-bottom: 6px; }
-
-.memdoc dl dt a.el {
- font-weight: bold;
- color: black; }
-
-#projectlogo {
- text-align: center;
- vertical-align: bottom;
- border-collapse: separate; }
-
-#projectlogo img {
- border: 0px none; }
-
-#projectalign {
- vertical-align: middle; }
-
-#projectname {
- font: 300% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 2px 0px; }
-
-#projectbrief {
- font: 120% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 0px; }
-
-#projectnumber {
- font: 50% Roboto,sans-serif;
- margin: 0px;
- padding: 0px; }
-
-#titlearea {
- padding: 0px;
- margin: 0px;
- width: 100%;
- border-bottom: none; }
- #titlearea:nth-last-child(2) {
- border-bottom: 2px solid #444444; }
-
-.image {
- text-align: center; }
-
-.dotgraph, .mscgraph, .diagraph {
- text-align: center; }
-
-.caption {
- font-weight: bold; }
-
-div.zoom {
- border: 1px solid #90A5CE; }
-
-dl.citelist {
- margin-bottom: 5ex; }
- dl.citelist dt {
- color: black;
- float: left;
- font-weight: bold;
- padding: 5px 0;
- margin: 2px 10pt 2px 0; }
- dl.citelist dd {
- margin: 2px 0;
- padding: 5px 0; }
- dl.citelist .startdd {
- margin-top: 0; }
-
-div.toc {
- background-color: transparent;
- border: 1px solid #414141;
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
- border-radius: 0;
- float: right;
- height: auto;
- margin: 0 8px 10px 10px;
- padding: 10px 15px 5px 25px;
- width: auto; }
- div.toc li {
- background: transparent;
- font: 10pt Roboto,DejaVu Sans,sans-serif;
- padding-left: 0;
- padding-top: 0.5ex; }
- div.toc li .level1 {
- margin-left: 10pt; }
- div.toc li .level2 {
- margin-left: 10pt; }
- div.toc li .level3 {
- margin-left: 10pt; }
- div.toc li .level4 {
- margin-left: 10pt; }
- div.toc h3 {
- font: bold 12px/1.2 Roboto,DejaVu Sans,sans-serif;
- color: black;
- border-bottom: none;
- margin: 0;
- letter-spacing: 1px; }
- div.toc ul {
- list-style: disc;
- border: none;
- padding: 0; }
-
-.inherit_header {
- font-weight: bold;
- color: gray;
- cursor: pointer;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none; }
-
-.inherit_header td {
- padding: 6px 0px 2px 5px; }
-
-.inherit {
- display: none; }
-
-/* tooltip related style info */
-.ttc {
- position: absolute;
- display: none; }
-
-#powerTip {
- cursor: default;
- white-space: nowrap;
- background-color: #ffffff;
- border: 1px solid #323232;
- border-radius: 0;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -o-box-shadow: none;
- box-shadow: none;
- display: none;
- font-size: smaller;
- max-width: 80%;
- opacity: 0.9;
- padding: 1ex 1em 1em 1em;
- position: absolute;
- z-index: 2147483647; }
- #powerTip div.ttdoc {
- color: grey;
- font-style: italic; }
- #powerTip div.ttname a {
- font-weight: bold; }
- #powerTip div.ttname {
- font-weight: bold; }
- #powerTip div.ttdeci {
- color: #006318; }
- #powerTip div {
- margin: 0px;
- padding: 0px;
- font: 12px/16px Roboto,sans-serif; }
- #powerTip:before, #powerTip:after {
- content: "";
- position: absolute;
- margin: 0px; }
-
-@media print {
- #top {
- display: none; }
- #side-nav {
- display: none; }
- #nav-path {
- display: none; }
- body {
- overflow: visible; }
- h1, h2, h3, h4, h5, h6 {
- page-break-after: avoid; }
- .summary {
- display: none; }
- .memitem {
- page-break-inside: avoid; }
- #doc-content {
- margin-left: 0 !important;
- height: auto !important;
- width: auto !important;
- overflow: inherit;
- display: inline;
- background-color: white; } }
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 64458cfc..4fb842b0 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -30,9 +30,11 @@ if (UNIX AND NOT WIN32)
target_compile_options(samplesftp PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
target_link_libraries(samplesftp ssh::ssh)
- add_executable(sample_sftpserver sample_sftpserver.c ${examples_SRCS})
- target_compile_options(sample_sftpserver PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
- target_link_libraries(sample_sftpserver ssh::ssh ${ARGP_LIBRARIES})
+ if (WITH_SERVER)
+ add_executable(sample_sftpserver sample_sftpserver.c ${examples_SRCS})
+ target_compile_options(sample_sftpserver PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
+ target_link_libraries(sample_sftpserver ssh::ssh ${ARGP_LIBRARIES})
+ endif (WITH_SERVER)
endif (WITH_SFTP)
add_executable(ssh-client ssh_client.c ${examples_SRCS})
diff --git a/examples/authentication.c b/examples/authentication.c
index 7c47c8bd..f6a5dbd5 100644
--- a/examples/authentication.c
+++ b/examples/authentication.c
@@ -30,8 +30,8 @@ int authenticate_kbdint(ssh_session session, const char *password)
err = ssh_userauth_kbdint(session, NULL, NULL);
while (err == SSH_AUTH_INFO) {
- const char *instruction;
- const char *name;
+ const char *instruction = NULL;
+ const char *name = NULL;
char buffer[128];
int i, n;
@@ -48,8 +48,8 @@ int authenticate_kbdint(ssh_session session, const char *password)
}
for (i = 0; i < n; i++) {
- const char *answer;
- const char *prompt;
+ const char *answer = NULL;
+ const char *prompt = NULL;
char echo;
prompt = ssh_userauth_kbdint_getprompt(session, i, &echo);
@@ -58,7 +58,7 @@ int authenticate_kbdint(ssh_session session, const char *password)
}
if (echo) {
- char *p;
+ char *p = NULL;
printf("%s", prompt);
@@ -66,7 +66,6 @@ int authenticate_kbdint(ssh_session session, const char *password)
return SSH_AUTH_ERROR;
}
- buffer[sizeof(buffer) - 1] = '\0';
if ((p = strchr(buffer, '\n'))) {
*p = '\0';
}
@@ -75,7 +74,7 @@ int authenticate_kbdint(ssh_session session, const char *password)
return SSH_AUTH_ERROR;
}
- memset(buffer, 0, strlen(buffer));
+ memset(buffer, 0, sizeof(buffer));
} else {
if (password && strstr(prompt, "Password:")) {
answer = password;
@@ -143,11 +142,11 @@ int authenticate_console(ssh_session session)
int rc;
int method;
char password[128] = {0};
- char *banner;
+ char *banner = NULL;
// Try to authenticate
rc = ssh_userauth_none(session, NULL);
- if (rc == SSH_AUTH_ERROR) {
+ if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
error(session);
return rc;
}
@@ -156,7 +155,7 @@ int authenticate_console(ssh_session session)
while (rc != SSH_AUTH_SUCCESS) {
if (method & SSH_AUTH_METHOD_GSSAPI_MIC){
rc = ssh_userauth_gssapi(session);
- if(rc == SSH_AUTH_ERROR) {
+ if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
error(session);
return rc;
} else if (rc == SSH_AUTH_SUCCESS) {
@@ -166,7 +165,7 @@ int authenticate_console(ssh_session session)
// Try to authenticate with public key first
if (method & SSH_AUTH_METHOD_PUBLICKEY) {
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
- if (rc == SSH_AUTH_ERROR) {
+ if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
error(session);
return rc;
} else if (rc == SSH_AUTH_SUCCESS) {
@@ -206,7 +205,7 @@ int authenticate_console(ssh_session session)
// Try to authenticate with keyboard interactive";
if (method & SSH_AUTH_METHOD_INTERACTIVE) {
rc = authenticate_kbdint(session, NULL);
- if (rc == SSH_AUTH_ERROR) {
+ if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
error(session);
return rc;
} else if (rc == SSH_AUTH_SUCCESS) {
@@ -221,7 +220,7 @@ int authenticate_console(ssh_session session)
// Try to authenticate with password
if (method & SSH_AUTH_METHOD_PASSWORD) {
rc = ssh_userauth_password(session, NULL, password);
- if (rc == SSH_AUTH_ERROR) {
+ if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
error(session);
return rc;
} else if (rc == SSH_AUTH_SUCCESS) {
diff --git a/examples/connect_ssh.c b/examples/connect_ssh.c
index c9e4ef6e..b07f824d 100644
--- a/examples/connect_ssh.c
+++ b/examples/connect_ssh.c
@@ -21,47 +21,57 @@ clients must be made or how a client should react.
#include "examples_common.h"
#include
-ssh_session connect_ssh(const char *host, const char *user,int verbosity){
- ssh_session session;
- int auth=0;
+ssh_session connect_ssh(const char *host, const char *port, const char *user, int verbosity)
+{
+ ssh_session session = NULL;
+ int auth = 0;
- session=ssh_new();
- if (session == NULL) {
- return NULL;
- }
+ session = ssh_new();
+ if (session == NULL) {
+ return NULL;
+ }
- if(user != NULL){
- if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
- ssh_free(session);
- return NULL;
+ if (user != NULL) {
+ if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
+ ssh_free(session);
+ return NULL;
+ }
}
- }
- if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
- ssh_free(session);
- return NULL;
- }
- ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
- if(ssh_connect(session)){
- fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
- ssh_disconnect(session);
- ssh_free(session);
- return NULL;
- }
- if(verify_knownhost(session)<0){
+ if (port != NULL) {
+ if (ssh_options_set(session, SSH_OPTIONS_PORT_STR, port) < 0) {
+ ssh_free(session);
+ return NULL;
+ }
+ }
+
+ if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
+ ssh_free(session);
+ return NULL;
+ }
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ if (ssh_connect(session)) {
+ fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
+ ssh_disconnect(session);
+ ssh_free(session);
+ return NULL;
+ }
+ if (verify_knownhost(session) < 0) {
+ ssh_disconnect(session);
+ ssh_free(session);
+ return NULL;
+ }
+ auth = authenticate_console(session);
+ if (auth == SSH_AUTH_SUCCESS) {
+ return session;
+ } else if (auth == SSH_AUTH_DENIED) {
+ fprintf(stderr, "Authentication failed\n");
+ } else {
+ fprintf(stderr,
+ "Error while authenticating : %s\n",
+ ssh_get_error(session));
+ }
ssh_disconnect(session);
ssh_free(session);
return NULL;
- }
- auth=authenticate_console(session);
- if(auth==SSH_AUTH_SUCCESS){
- return session;
- } else if(auth==SSH_AUTH_DENIED){
- fprintf(stderr,"Authentication failed\n");
- } else {
- fprintf(stderr,"Error while authenticating : %s\n",ssh_get_error(session));
- }
- ssh_disconnect(session);
- ssh_free(session);
- return NULL;
}
diff --git a/examples/examples_common.h b/examples/examples_common.h
index d3f4e1aa..6f5a1b12 100644
--- a/examples/examples_common.h
+++ b/examples/examples_common.h
@@ -16,11 +16,11 @@ clients must be made or how a client should react.
#include
/** Zero a structure */
-#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#define ZERO_STRUCT(x) memset(&(x), 0, sizeof(x))
int authenticate_console(ssh_session session);
int authenticate_kbdint(ssh_session session, const char *password);
int verify_knownhost(ssh_session session);
-ssh_session connect_ssh(const char *hostname, const char *user, int verbosity);
+ssh_session connect_ssh(const char *hostname, const char *port, const char *user, int verbosity);
#endif /* EXAMPLES_COMMON_H_ */
diff --git a/examples/exec.c b/examples/exec.c
index 77d3be47..9c1d8dbb 100644
--- a/examples/exec.c
+++ b/examples/exec.c
@@ -5,13 +5,13 @@
#include "examples_common.h"
int main(void) {
- ssh_session session;
- ssh_channel channel;
+ ssh_session session = NULL;
+ ssh_channel channel = NULL;
char buffer[256];
int rbytes, wbytes, total = 0;
int rc;
- session = connect_ssh("localhost", NULL, 0);
+ session = connect_ssh("localhost", NULL, NULL, 0);
if (session == NULL) {
ssh_finalize();
return 1;
diff --git a/examples/keygen.c b/examples/keygen.c
index 2ab00113..99f8c98c 100644
--- a/examples/keygen.c
+++ b/examples/keygen.c
@@ -27,14 +27,14 @@ int main(void)
rv = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &key);
if (rv != SSH_OK) {
fprintf(stderr, "Failed to generate private key");
- return -1;
+ return -1;
}
- /* Write it to a file testkey in the current dirrectory */
+ /* Write it to a file testkey in the current directory */
rv = ssh_pki_export_privkey_file(key, NULL, NULL, NULL, "testkey");
if (rv != SSH_OK) {
fprintf(stderr, "Failed to write private key file");
- return -1;
+ return -1;
}
return 0;
diff --git a/examples/keygen2.c b/examples/keygen2.c
index bbe15e0e..73d70211 100644
--- a/examples/keygen2.c
+++ b/examples/keygen2.c
@@ -38,6 +38,7 @@ struct arguments_st {
unsigned long bits;
char *file;
char *passphrase;
+ char *format;
int action_list;
};
@@ -96,6 +97,16 @@ static struct argp_option options[] = {
.doc = "List the Fingerprint of the given key\n",
.group = 0
},
+ {
+ .name = "format",
+ .key = 'm',
+ .arg = "FORMAT",
+ .flags = 0,
+ .doc = "Write the file in specific format. The supported values are "
+ "'PEM'and 'OpenSSH' file format. By default Ed25519 "
+ "keys are exported in OpenSSH format and others in PEM.\n",
+ .group = 0
+ },
{
/* End of the options */
0
@@ -168,6 +179,9 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
case 'l':
arguments->action_list = 1;
break;
+ case 'm':
+ arguments->format = strdup(arg);
+ break;
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
/* Too many arguments. */
@@ -296,6 +310,7 @@ list_fingerprint(char *file)
rc = ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen);
if (rc != SSH_OK) {
fprintf(stderr, "Failed to get key fingerprint\n");
+ ssh_key_free(key);
return;
}
ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
@@ -335,7 +350,12 @@ int main(int argc, char *argv[])
goto end;
}
- if (arguments.action_list && arguments.file) {
+ if (arguments.file == NULL) {
+ fprintf(stderr, "Error: Missing argument file\n");
+ goto end;
+ }
+
+ if (arguments.action_list) {
list_fingerprint(arguments.file);
goto end;
}
@@ -382,8 +402,36 @@ int main(int argc, char *argv[])
}
/* Write the private key */
- rc = ssh_pki_export_privkey_file(key, arguments.passphrase, NULL, NULL,
- arguments.file);
+ if (arguments.format != NULL) {
+ if (strcasecmp(arguments.format, "PEM") == 0) {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_PEM);
+ } else if (strcasecmp(arguments.format, "OpenSSH") == 0) {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_OPENSSH);
+ } else {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_DEFAULT);
+ }
+ } else {
+ rc = ssh_pki_export_privkey_file(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file);
+ }
if (rc != SSH_OK) {
fprintf(stderr, "Error: Failed to write private key file");
goto end;
diff --git a/examples/knownhosts.c b/examples/knownhosts.c
index 0726bfa8..2857a085 100644
--- a/examples/knownhosts.c
+++ b/examples/knownhosts.c
@@ -38,7 +38,7 @@ int verify_knownhost(ssh_session session)
char buf[10];
unsigned char *hash = NULL;
size_t hlen;
- ssh_key srv_pubkey;
+ ssh_key srv_pubkey = NULL;
int rc;
rc = ssh_get_server_publickey(session, &srv_pubkey);
diff --git a/examples/libssh_scp.c b/examples/libssh_scp.c
index adb82ef0..2b1a7627 100644
--- a/examples/libssh_scp.c
+++ b/examples/libssh_scp.c
@@ -26,10 +26,11 @@ program.
#define BUF_SIZE 16384
#endif
-static char **sources;
+static char **sources = NULL;
static int nsources;
-static char *destination;
+static char *destination = NULL;
static int verbosity = 0;
+static char *port = NULL;
struct location {
int is_ssh;
@@ -49,9 +50,10 @@ enum {
static void usage(const char *argv0) {
fprintf(stderr, "Usage : %s [options] [[user@]host1:]file1 ... \n"
" [[user@]host2:]destination\n"
- "sample scp client - libssh-%s\n",
- // "Options :\n",
- // " -r : use RSA to verify host public key\n",
+ "sample scp client - libssh-%s\n"
+ "Options :\n"
+ " -P : use port to connect to remote host\n"
+ " -v : increase verbosity of libssh. Can be used multiple times\n",
argv0,
ssh_version(0));
exit(0);
@@ -60,11 +62,14 @@ static void usage(const char *argv0) {
static int opts(int argc, char **argv) {
int i;
- while((i = getopt(argc, argv, "v")) != -1) {
+ while((i = getopt(argc, argv, "P:v")) != -1) {
switch(i) {
case 'v':
verbosity++;
break;
+ case 'P':
+ port = optarg;
+ break;
default:
fprintf(stderr, "unknown option %c\n", optopt);
usage(argv[0]);
@@ -114,9 +119,14 @@ static void location_free(struct location *loc)
}
}
-static struct location *parse_location(char *loc) {
- struct location *location;
- char *ptr;
+static struct location *parse_location(char *loc)
+{
+ struct location *location = NULL;
+ char *ptr = NULL;
+
+ if (loc == NULL) {
+ return NULL;
+ }
location = malloc(sizeof(struct location));
if (location == NULL) {
@@ -178,7 +188,7 @@ static void close_location(struct location *loc) {
static int open_location(struct location *loc, int flag) {
if (loc->is_ssh && flag == WRITE) {
- loc->session = connect_ssh(loc->host, loc->user, verbosity);
+ loc->session = connect_ssh(loc->host, port, loc->user, verbosity);
if (!loc->session) {
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
return -1;
@@ -204,7 +214,7 @@ static int open_location(struct location *loc, int flag) {
}
return 0;
} else if (loc->is_ssh && flag == READ) {
- loc->session = connect_ssh(loc->host, loc->user, verbosity);
+ loc->session = connect_ssh(loc->host, port, loc->user, verbosity);
if (!loc->session) {
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
return -1;
diff --git a/examples/proxy.c b/examples/proxy.c
index 49c39e4c..ab69b18e 100644
--- a/examples/proxy.c
+++ b/examples/proxy.c
@@ -35,8 +35,8 @@ clients must be made or how a client should react.
static int authenticated=0;
static int tries = 0;
static int error = 0;
-static ssh_channel chan=NULL;
-static char *username;
+static ssh_channel chan = NULL;
+static char *username = NULL;
static ssh_gssapi_creds client_creds = NULL;
static int auth_password(ssh_session session, const char *user,
@@ -204,11 +204,12 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
-int main(int argc, char **argv){
- ssh_session session;
- ssh_bind sshbind;
- ssh_event mainloop;
- ssh_session client_session;
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
+ ssh_bind sshbind = NULL;
+ ssh_event mainloop = NULL;
+ ssh_session client_session = NULL;
struct ssh_server_callbacks_struct cb = {
.userdata = NULL,
@@ -219,7 +220,7 @@ int main(int argc, char **argv){
char buf[BUF_SIZE];
char host[128]="";
- char *ptr;
+ char *ptr = NULL;
int i,r, rc;
sshbind=ssh_bind_new();
@@ -336,4 +337,3 @@ int main(int argc, char **argv){
ssh_finalize();
return 0;
}
-
diff --git a/examples/sample_sftpserver.c b/examples/sample_sftpserver.c
index b96f4ef7..ced99318 100644
--- a/examples/sample_sftpserver.c
+++ b/examples/sample_sftpserver.c
@@ -148,6 +148,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
ssh_bind sshbind = state->input;
static int no_default_keys = 0;
static int rsa_already_set = 0, ecdsa_already_set = 0;
+ static int verbosity = 0;
switch (key)
{
@@ -176,8 +177,10 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
break;
case 'v':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
- "3");
+ verbosity++;
+ ssh_bind_options_set(sshbind,
+ SSH_BIND_OPTIONS_LOG_VERBOSITY,
+ &verbosity);
break;
case ARGP_KEY_ARG:
if (state->arg_num >= 1)
@@ -213,10 +216,7 @@ static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
/* A userdata struct for channel. */
-struct channel_data_struct
-{
- /* Event which is used to poll the above descriptors. */
- ssh_event event;
+struct channel_data_struct {
sftp_session sftp;
};
@@ -378,18 +378,11 @@ static void handle_session(ssh_event event, ssh_session session)
do {
/* Poll the main event which takes care of the session, the channel and
* even our child process's stdout/stderr (once it's started). */
- if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
+ if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
ssh_channel_close(sdata.channel);
}
-
- /* If child process's stdout/stderr has been registered with the event,
- * or the child process hasn't started yet, continue. */
- if (cdata.event != NULL) {
- continue;
- }
- /* FIXME The server keeps hanging in the poll above when the client
- * closes the channel */
- } while (ssh_channel_is_open(sdata.channel));
+ } while (ssh_channel_is_open(sdata.channel) &&
+ !ssh_channel_is_eof(sdata.channel));
ssh_channel_send_eof(sdata.channel);
ssh_channel_close(sdata.channel);
diff --git a/examples/samplesshd-cb.c b/examples/samplesshd-cb.c
index 6b272102..31e035ff 100644
--- a/examples/samplesshd-cb.c
+++ b/examples/samplesshd-cb.c
@@ -244,10 +244,11 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
-int main(int argc, char **argv){
- ssh_session session;
- ssh_bind sshbind;
- ssh_event mainloop;
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
+ ssh_bind sshbind = NULL;
+ ssh_event mainloop = NULL;
struct ssh_server_callbacks_struct cb = {
.userdata = NULL,
.auth_none_function = auth_none,
@@ -339,4 +340,3 @@ int main(int argc, char **argv){
ssh_finalize();
return 0;
}
-
diff --git a/examples/samplesshd-kbdint.c b/examples/samplesshd-kbdint.c
index d382abac..919eb338 100644
--- a/examples/samplesshd-kbdint.c
+++ b/examples/samplesshd-kbdint.c
@@ -174,8 +174,8 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
-static const char *name;
-static const char *instruction;
+static const char *name = NULL;
+static const char *instruction = NULL;
static const char *prompts[2];
static char echo[] = { 1, 0 };
@@ -279,11 +279,12 @@ static int authenticate(ssh_session session) {
return 0;
}
-int main(int argc, char **argv){
- ssh_session session;
- ssh_bind sshbind;
- ssh_message message;
- ssh_channel chan=0;
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
+ ssh_bind sshbind = NULL;
+ ssh_message message = NULL;
+ ssh_channel chan = NULL;
char buf[BUF_SIZE];
int auth=0;
int shell=0;
@@ -411,4 +412,3 @@ int main(int argc, char **argv){
ssh_finalize();
return 0;
}
-
diff --git a/examples/scp_download.c b/examples/scp_download.c
index e6c1e796..24355e29 100644
--- a/examples/scp_download.c
+++ b/examples/scp_download.c
@@ -32,151 +32,163 @@ static const char *createcommand =
"cd /tmp/libssh_tests && date > a && date > b && mkdir c && date > d";
static char *host = NULL;
-static void usage(const char *argv0){
- fprintf(stderr,"Usage : %s [options] host\n"
- "sample tiny scp downloader client - libssh-%s\n"
- "This program will create files in /tmp and try to fetch them\n",
-// "Options :\n",
-// " -r : use RSA to verify host public key\n",
- argv0,
- ssh_version(0));
- exit(0);
+static void usage(const char *argv0)
+{
+ fprintf(stderr,
+ "Usage : %s [options] host\n"
+ "sample tiny scp downloader client - libssh-%s\n"
+ "This program will create files in /tmp and try to fetch them\n",
+ argv0,
+ ssh_version(0));
+ exit(0);
}
-static int opts(int argc, char **argv){
- int i;
- while((i=getopt(argc,argv,"v"))!=-1){
- switch(i){
- case 'v':
- verbosity++;
- break;
- default:
- fprintf(stderr,"unknown option %c\n",optopt);
- usage(argv[0]);
- return -1;
+static int opts(int argc, char **argv)
+{
+ int i;
+
+ while ((i = getopt(argc, argv, "v")) != -1) {
+ switch (i) {
+ case 'v':
+ verbosity++;
+ break;
+ default:
+ fprintf(stderr, "unknown option %c\n", optopt);
+ usage(argv[0]);
+ return -1;
+ }
}
- }
- host = argv[optind];
- if(host == NULL)
- usage(argv[0]);
- return 0;
+ host = argv[optind];
+ if (host == NULL)
+ usage(argv[0]);
+ return 0;
}
-static void create_files(ssh_session session){
- ssh_channel channel=ssh_channel_new(session);
- char buffer[1];
- int rc;
+static void create_files(ssh_session session)
+{
+ ssh_channel channel = ssh_channel_new(session);
+ char buffer[1];
+ int rc;
- if(channel == NULL){
- fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session));
- exit(EXIT_FAILURE);
- }
- if(ssh_channel_open_session(channel) != SSH_OK){
- fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session));
- ssh_channel_free(channel);
- exit(EXIT_FAILURE);
- }
- if(ssh_channel_request_exec(channel,createcommand) != SSH_OK){
- fprintf(stderr,"Error executing command: %s\n",ssh_get_error(session));
- ssh_channel_close(channel);
- ssh_channel_free(channel);
- exit(EXIT_FAILURE);
- }
- while(!ssh_channel_is_eof(channel)){
- rc = ssh_channel_read(channel,buffer,1,1);
- if (rc != 1) {
- fprintf(stderr, "Error reading from channel\n");
- ssh_channel_close(channel);
- ssh_channel_free(channel);
- return;
- }
-
- rc = write(1, buffer, 1);
- if (rc < 0) {
- fprintf(stderr, "Error writing to buffer\n");
- ssh_channel_close(channel);
- ssh_channel_free(channel);
- return;
- }
- }
- ssh_channel_close(channel);
- ssh_channel_free(channel);
+ if (channel == NULL) {
+ fprintf(stderr, "Error creating channel: %s\n", ssh_get_error(session));
+ exit(EXIT_FAILURE);
+ }
+ if (ssh_channel_open_session(channel) != SSH_OK) {
+ fprintf(stderr, "Error creating channel: %s\n", ssh_get_error(session));
+ ssh_channel_free(channel);
+ exit(EXIT_FAILURE);
+ }
+ if (ssh_channel_request_exec(channel, createcommand) != SSH_OK) {
+ fprintf(stderr,
+ "Error executing command: %s\n",
+ ssh_get_error(session));
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ exit(EXIT_FAILURE);
+ }
+ while (!ssh_channel_is_eof(channel)) {
+ rc = ssh_channel_read(channel, buffer, 1, 1);
+ if (rc != 1) {
+ fprintf(stderr, "Error reading from channel\n");
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return;
+ }
+
+ rc = write(1, buffer, 1);
+ if (rc < 0) {
+ fprintf(stderr, "Error writing to buffer\n");
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ return;
+ }
+ }
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
}
-
-static int fetch_files(ssh_session session){
- int size;
- char buffer[BUF_SIZE];
- int mode;
- char *filename;
- int r;
- ssh_scp scp=ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, "/tmp/libssh_tests/*");
- if(ssh_scp_init(scp) != SSH_OK){
- fprintf(stderr,"error initializing scp: %s\n",ssh_get_error(session));
- ssh_scp_free(scp);
- return -1;
- }
- printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n");
- do {
-
- r=ssh_scp_pull_request(scp);
- switch(r){
- case SSH_SCP_REQUEST_NEWFILE:
- size=ssh_scp_request_get_size(scp);
- filename=strdup(ssh_scp_request_get_filename(scp));
- mode=ssh_scp_request_get_permissions(scp);
- printf("downloading file %s, size %d, perms 0%o\n",filename,size,mode);
- free(filename);
- ssh_scp_accept_request(scp);
- r=ssh_scp_read(scp,buffer,sizeof(buffer));
- if(r==SSH_ERROR){
- fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(session));
- ssh_scp_close(scp);
- ssh_scp_free(scp);
- return -1;
- }
- printf("done\n");
- break;
- case SSH_ERROR:
- fprintf(stderr,"Error: %s\n",ssh_get_error(session));
- ssh_scp_close(scp);
- ssh_scp_free(scp);
- return -1;
- case SSH_SCP_REQUEST_WARNING:
- fprintf(stderr,"Warning: %s\n",ssh_scp_request_get_warning(scp));
- break;
- case SSH_SCP_REQUEST_NEWDIR:
- filename=strdup(ssh_scp_request_get_filename(scp));
- mode=ssh_scp_request_get_permissions(scp);
- printf("downloading directory %s, perms 0%o\n",filename,mode);
- free(filename);
- ssh_scp_accept_request(scp);
- break;
- case SSH_SCP_REQUEST_ENDDIR:
- printf("End of directory\n");
- break;
- case SSH_SCP_REQUEST_EOF:
- printf("End of requests\n");
- goto end;
- }
- } while (1);
- end:
- ssh_scp_close(scp);
- ssh_scp_free(scp);
- return 0;
+static int fetch_files(ssh_session session)
+{
+ int size;
+ char buffer[BUF_SIZE];
+ int mode;
+ char *filename = NULL;
+ int r;
+ ssh_scp scp = ssh_scp_new(session,
+ SSH_SCP_READ | SSH_SCP_RECURSIVE,
+ "/tmp/libssh_tests/*");
+ if (ssh_scp_init(scp) != SSH_OK) {
+ fprintf(stderr, "error initializing scp: %s\n", ssh_get_error(session));
+ ssh_scp_free(scp);
+ return -1;
+ }
+ printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n");
+ do {
+ r = ssh_scp_pull_request(scp);
+ switch (r) {
+ case SSH_SCP_REQUEST_NEWFILE:
+ size = ssh_scp_request_get_size(scp);
+ filename = strdup(ssh_scp_request_get_filename(scp));
+ mode = ssh_scp_request_get_permissions(scp);
+ printf("downloading file %s, size %d, perms 0%o\n",
+ filename,
+ size,
+ mode);
+ free(filename);
+ ssh_scp_accept_request(scp);
+ r = ssh_scp_read(scp, buffer, sizeof(buffer));
+ if (r == SSH_ERROR) {
+ fprintf(stderr,
+ "Error reading scp: %s\n",
+ ssh_get_error(session));
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+ return -1;
+ }
+ printf("done\n");
+ break;
+ case SSH_ERROR:
+ fprintf(stderr, "Error: %s\n", ssh_get_error(session));
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+ return -1;
+ case SSH_SCP_REQUEST_WARNING:
+ fprintf(stderr, "Warning: %s\n", ssh_scp_request_get_warning(scp));
+ break;
+ case SSH_SCP_REQUEST_NEWDIR:
+ filename = strdup(ssh_scp_request_get_filename(scp));
+ mode = ssh_scp_request_get_permissions(scp);
+ printf("downloading directory %s, perms 0%o\n", filename, mode);
+ free(filename);
+ ssh_scp_accept_request(scp);
+ break;
+ case SSH_SCP_REQUEST_ENDDIR:
+ printf("End of directory\n");
+ break;
+ case SSH_SCP_REQUEST_EOF:
+ printf("End of requests\n");
+ goto end;
+ }
+ } while (1);
+end:
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+ return 0;
}
-int main(int argc, char **argv){
- ssh_session session;
- if(opts(argc,argv)<0)
- return EXIT_FAILURE;
- session=connect_ssh(host,NULL,verbosity);
- if(session == NULL)
- return EXIT_FAILURE;
- create_files(session);
- fetch_files(session);
- ssh_disconnect(session);
- ssh_free(session);
- ssh_finalize();
- return 0;
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
+ if (opts(argc, argv) < 0)
+ return EXIT_FAILURE;
+ session = connect_ssh(host, NULL, NULL, verbosity);
+ if (session == NULL)
+ return EXIT_FAILURE;
+ create_files(session);
+ fetch_files(session);
+ ssh_disconnect(session);
+ ssh_free(session);
+ ssh_finalize();
+ return 0;
}
diff --git a/examples/senddata.c b/examples/senddata.c
index 21181fb9..5be65af3 100644
--- a/examples/senddata.c
+++ b/examples/senddata.c
@@ -5,60 +5,60 @@
#define LIMIT 0x100000000UL
-int main(void) {
- ssh_session session;
- ssh_channel channel;
- char buffer[1024*1024];
- int rc;
- uint64_t total=0;
- uint64_t lastshown=4096;
- session = connect_ssh("localhost", NULL, 0);
- if (session == NULL) {
- return 1;
- }
+int main(void)
+{
+ ssh_session session = NULL;
+ ssh_channel channel = NULL;
+ char buffer[1024 * 1024] = {0};
+ int rc;
+ uint64_t total = 0;
+ uint64_t lastshown = 4096;
+ session = connect_ssh("localhost", NULL, NULL, 0);
+ if (session == NULL) {
+ return 1;
+ }
- channel = ssh_channel_new(session);
- if (channel == NULL) {
- ssh_disconnect(session);
- return 1;
- }
+ channel = ssh_channel_new(session);
+ if (channel == NULL) {
+ ssh_disconnect(session);
+ return 1;
+ }
- rc = ssh_channel_open_session(channel);
- if (rc < 0) {
- ssh_channel_close(channel);
- ssh_disconnect(session);
- return 1;
- }
+ rc = ssh_channel_open_session(channel);
+ if (rc < 0) {
+ ssh_channel_close(channel);
+ ssh_disconnect(session);
+ return 1;
+ }
- rc = ssh_channel_request_exec(channel, "cat > /dev/null");
- if (rc < 0) {
- ssh_channel_close(channel);
- ssh_disconnect(session);
- return 1;
- }
+ rc = ssh_channel_request_exec(channel, "cat > /dev/null");
+ if (rc < 0) {
+ ssh_channel_close(channel);
+ ssh_disconnect(session);
+ return 1;
+ }
+ while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) {
+ total += rc;
+ if (total / 2 >= lastshown) {
+ printf("written %llx\n", (long long unsigned int)total);
+ lastshown = total;
+ }
+ if (total > LIMIT)
+ break;
+ }
- while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) {
- total += rc;
- if(total/2 >= lastshown){
- printf("written %llx\n", (long long unsigned int) total);
- lastshown=total;
+ if (rc < 0) {
+ printf("error : %s\n", ssh_get_error(session));
+ ssh_channel_close(channel);
+ ssh_disconnect(session);
+ return 1;
}
- if(total > LIMIT)
- break;
- }
-
- if (rc < 0) {
- printf("error : %s\n",ssh_get_error(session));
- ssh_channel_close(channel);
- ssh_disconnect(session);
- return 1;
- }
- ssh_channel_send_eof(channel);
- ssh_channel_close(channel);
+ ssh_channel_send_eof(channel);
+ ssh_channel_close(channel);
- ssh_disconnect(session);
+ ssh_disconnect(session);
- return 0;
+ return 0;
}
diff --git a/examples/ssh_X11_client.c b/examples/ssh_X11_client.c
index b3fa0e2d..195b259b 100644
--- a/examples/ssh_X11_client.c
+++ b/examples/ssh_X11_client.c
@@ -70,6 +70,7 @@
#include
#include
+#include
#include
#include
#include
@@ -231,7 +232,7 @@ static void _logging_callback(int priority, const char *function,
milliseconds = _current_timestamp();
- fprintf(fp, "[%s.%jd, %d] %s: %s\n", buf, milliseconds, priority,
+ fprintf(fp, "[%s.%" PRId64 ", %d] %s: %s\n", buf, milliseconds, priority,
function, buffer);
fclose(fp);
}
@@ -343,10 +344,11 @@ static void delete_item(ssh_channel channel)
static node_t *search_item(ssh_channel channel)
{
- node_t *current = node;
+ node_t *current = NULL;
pthread_mutex_lock(&mutex);
+ current = node;
while (current != NULL) {
if (current->channel == channel) {
pthread_mutex_unlock(&mutex);
diff --git a/examples/ssh_client.c b/examples/ssh_client.c
index cc249183..1b516830 100644
--- a/examples/ssh_client.c
+++ b/examples/ssh_client.c
@@ -39,8 +39,6 @@
#include
#include
-#include
-
#include "examples_common.h"
#define MAXCMD 10
@@ -53,7 +51,7 @@ static struct termios terminal;
static char *pcap_file = NULL;
-static char *proxycommand;
+static char *proxycommand = NULL;
static int auth_callback(const char *prompt,
char *buf,
@@ -88,22 +86,24 @@ static void add_cmd(char *cmd)
static void usage(void)
{
- fprintf(stderr,
- "Usage : ssh [options] [login@]hostname\n"
- "sample client - libssh-%s\n"
- "Options :\n"
- " -l user : log in as user\n"
- " -p port : connect to port\n"
- " -r : use RSA to verify host public key\n"
- " -F file : parse configuration file instead of default one\n"
+ fprintf(
+ stderr,
+ "Usage : ssh [options] [login@]hostname\n"
+ "sample client - libssh-%s\n"
+ "Options :\n"
+ " -l user : log in as user\n"
+ " -p port : connect to port\n"
+ " -o option : set configuration option (e.g., -o Compression=yes)\n"
+ " -r : use RSA to verify host public key\n"
+ " -F file : parse configuration file instead of default one\n"
#ifdef WITH_PCAP
- " -P file : create a pcap debugging file\n"
+ " -P file : create a pcap debugging file\n"
#endif
#ifndef _WIN32
- " -T proxycommand : command to execute as a socket proxy\n"
+ " -T proxycommand : command to execute as a socket proxy\n"
#endif
- "\n",
- ssh_version(0));
+ "\n",
+ ssh_version(0));
exit(0);
}
@@ -112,8 +112,8 @@ static int opts(int argc, char **argv)
{
int i;
- while((i = getopt(argc,argv,"T:P:F:")) != -1) {
- switch(i){
+ while ((i = getopt(argc, argv, "T:P:F:")) != -1) {
+ switch (i) {
case 'P':
pcap_file = optarg;
break;
@@ -159,16 +159,14 @@ static void cfmakeraw(struct termios *termios_p)
static void do_cleanup(int i)
{
- /* unused variable */
- (void) i;
+ (void)i;
- tcsetattr(0, TCSANOW, &terminal);
+ tcsetattr(0, TCSANOW, &terminal);
}
static void do_exit(int i)
{
- /* unused variable */
- (void) i;
+ (void)i;
do_cleanup(0);
exit(0);
@@ -179,7 +177,7 @@ static int signal_delayed = 0;
#ifdef SIGWINCH
static void sigwindowchanged(int i)
{
- (void) i;
+ (void)i;
signal_delayed = 1;
}
#endif
@@ -213,18 +211,18 @@ static void select_loop(ssh_session session,ssh_channel channel)
/* stdin */
connector_in = ssh_connector_new(session);
ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT);
- ssh_connector_set_in_fd(connector_in, 0);
+ ssh_connector_set_in_fd(connector_in, STDIN_FILENO);
ssh_event_add_connector(event, connector_in);
/* stdout */
connector_out = ssh_connector_new(session);
- ssh_connector_set_out_fd(connector_out, 1);
+ ssh_connector_set_out_fd(connector_out, STDOUT_FILENO);
ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT);
ssh_event_add_connector(event, connector_out);
/* stderr */
connector_err = ssh_connector_new(session);
- ssh_connector_set_out_fd(connector_err, 2);
+ ssh_connector_set_out_fd(connector_err, STDERR_FILENO);
ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR);
ssh_event_add_connector(event, connector_err);
@@ -251,9 +249,9 @@ static void select_loop(ssh_session session,ssh_channel channel)
static void shell(ssh_session session)
{
- ssh_channel channel;
+ ssh_channel channel = NULL;
struct termios terminal_local;
- int interactive=isatty(0);
+ int interactive = isatty(0);
channel = ssh_channel_new(session);
if (channel == NULL) {
@@ -339,7 +337,7 @@ static void batch_shell(ssh_session session)
static int client(ssh_session session)
{
int auth = 0;
- char *banner;
+ char *banner = NULL;
int state;
if (user) {
@@ -357,10 +355,8 @@ static int client(ssh_session session)
}
/* Parse configuration file if specified: The command-line options will
* overwrite items loaded from configuration file */
- if (config_file != NULL) {
- ssh_options_parse_config(session, config_file);
- } else {
- ssh_options_parse_config(session, NULL);
+ if (ssh_options_parse_config(session, config_file) < 0) {
+ return -1;
}
if (ssh_connect(session)) {
@@ -423,7 +419,7 @@ static void cleanup_pcap(void)
int main(int argc, char **argv)
{
- ssh_session session;
+ ssh_session session = NULL;
ssh_init();
session = ssh_new();
diff --git a/examples/ssh_server.c b/examples/ssh_server.c
index ef444458..fb1541d9 100644
--- a/examples/ssh_server.c
+++ b/examples/ssh_server.c
@@ -45,31 +45,10 @@ The goal is to show the API in action.
#define BUF_SIZE 1048576
#endif
-#ifndef KEYS_FOLDER
-#ifdef _WIN32
-#define KEYS_FOLDER
-#else
-#define KEYS_FOLDER "/etc/ssh/"
-#endif
-#endif
-
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
+#define AUTH_KEYS_MAX_LINE_SIZE 2048
-static void set_default_keys(ssh_bind sshbind,
- int rsa_already_set,
- int ecdsa_already_set) {
- if (!rsa_already_set) {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
- KEYS_FOLDER "ssh_host_rsa_key");
- }
- if (!ecdsa_already_set) {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
- KEYS_FOLDER "ssh_host_ecdsa_key");
- }
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
- KEYS_FOLDER "ssh_host_ed25519_key");
-}
#define DEF_STR_SIZE 1024
char authorizedkeys[DEF_STR_SIZE] = {0};
char username[128] = "myuser";
@@ -144,14 +123,6 @@ static struct argp_option options[] = {
.doc = "Set expected password.",
.group = 0
},
- {
- .name = "no-default-keys",
- .key = 'n',
- .arg = NULL,
- .flags = 0,
- .doc = "Do not set default key locations.",
- .group = 0
- },
{
.name = "verbose",
.key = 'v',
@@ -164,70 +135,53 @@ static struct argp_option options[] = {
};
/* Parse a single option. */
-static error_t parse_opt (int key, char *arg, struct argp_state *state) {
+static error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
/* Get the input argument from argp_parse, which we
* know is a pointer to our arguments structure. */
ssh_bind sshbind = state->input;
- static int no_default_keys = 0;
- static int rsa_already_set = 0, ecdsa_already_set = 0;
switch (key) {
- case 'n':
- no_default_keys = 1;
- break;
- case 'p':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
- break;
- case 'k':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
- /* We can't track the types of keys being added with this
- option, so let's ensure we keep the keys we're adding
- by just not setting the default keys */
- no_default_keys = 1;
- break;
- case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
- rsa_already_set = 1;
- break;
- case 'e':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
- ecdsa_already_set = 1;
- break;
- case 'a':
- strncpy(authorizedkeys, arg, DEF_STR_SIZE-1);
- break;
- case 'u':
- strncpy(username, arg, sizeof(username) - 1);
- break;
- case 'P':
- strncpy(password, arg, sizeof(password) - 1);
- break;
- case 'v':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
- "3");
- break;
- case ARGP_KEY_ARG:
- if (state->arg_num >= 1) {
- /* Too many arguments. */
- argp_usage (state);
- }
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg);
- break;
- case ARGP_KEY_END:
- if (state->arg_num < 1) {
- /* Not enough arguments. */
- argp_usage (state);
- }
-
- if (!no_default_keys) {
- set_default_keys(sshbind,
- rsa_already_set,
- ecdsa_already_set);
- }
-
- break;
- default:
- return ARGP_ERR_UNKNOWN;
+ case 'p':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
+ break;
+ case 'k':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ break;
+ case 'r':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ break;
+ case 'e':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ break;
+ case 'a':
+ strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
+ break;
+ case 'u':
+ strncpy(username, arg, sizeof(username) - 1);
+ break;
+ case 'P':
+ strncpy(password, arg, sizeof(password) - 1);
+ break;
+ case 'v':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3");
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 1) {
+ /* Too many arguments. */
+ argp_usage(state);
+ }
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg);
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 1) {
+ /* Not enough arguments. */
+ argp_usage(state);
+ }
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
}
return 0;
}
@@ -235,29 +189,20 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
/* Our argp parser. */
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
#else
-static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
- int no_default_keys = 0;
- int rsa_already_set = 0;
- int ecdsa_already_set = 0;
+static int
+parse_opt(int argc, char **argv, ssh_bind sshbind)
+{
int key;
- while((key = getopt(argc, argv, "a:e:k:np:P:r:u:v")) != -1) {
- if (key == 'n') {
- no_default_keys = 1;
- } else if (key == 'p') {
+ while((key = getopt(argc, argv, "a:e:k:p:P:r:u:v")) != -1) {
+ if (key == 'p') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg);
} else if (key == 'k') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
- /* We can't track the types of keys being added with this
- option, so let's ensure we keep the keys we're adding
- by just not setting the default keys */
- no_default_keys = 1;
} else if (key == 'r') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
- rsa_already_set = 1;
} else if (key == 'e') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
- ecdsa_already_set = 1;
} else if (key == 'a') {
strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1);
} else if (key == 'u') {
@@ -280,7 +225,6 @@ static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
" -e, --ecdsakey=FILE Set the ecdsa key (deprecated alias for 'k').\n"
" -k, --hostkey=FILE Set a host key. Can be used multiple times.\n"
" Implies no default keys.\n"
- " -n, --no-default-keys Do not set default key locations.\n"
" -p, --port=PORT Set the port to bind.\n"
" -P, --pass=PASSWORD Set expected password.\n"
" -r, --rsakey=FILE Set the rsa key (deprecated alias for 'k').\n"
@@ -303,12 +247,6 @@ static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, argv[optind]);
- if (!no_default_keys) {
- set_default_keys(sshbind,
- rsa_already_set,
- ecdsa_already_set);
- }
-
return 0;
}
#endif /* HAVE_ARGP_H */
@@ -339,49 +277,74 @@ struct session_data_struct {
int authenticated;
};
-static int data_function(ssh_session session, ssh_channel channel, void *data,
- uint32_t len, int is_stderr, void *userdata) {
- struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
+static int
+data_function(ssh_session session,
+ ssh_channel channel,
+ void *data,
+ uint32_t len,
+ int is_stderr,
+ void *userdata)
+{
+ struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
- (void) session;
- (void) channel;
- (void) is_stderr;
+ (void)session;
+ (void)channel;
+ (void)is_stderr;
if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) {
return 0;
}
- return write(cdata->child_stdin, (char *) data, len);
+ return write(cdata->child_stdin, (char *)data, len);
}
-static int pty_request(ssh_session session, ssh_channel channel,
- const char *term, int cols, int rows, int py, int px,
- void *userdata) {
+static int
+pty_request(ssh_session session,
+ ssh_channel channel,
+ const char *term,
+ int cols,
+ int rows,
+ int py,
+ int px,
+ void *userdata)
+{
struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
+ int rc;
- (void) session;
- (void) channel;
- (void) term;
+ (void)session;
+ (void)channel;
+ (void)term;
cdata->winsize->ws_row = rows;
cdata->winsize->ws_col = cols;
cdata->winsize->ws_xpixel = px;
cdata->winsize->ws_ypixel = py;
- if (openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL,
- cdata->winsize) != 0) {
+ rc = openpty(&cdata->pty_master,
+ &cdata->pty_slave,
+ NULL,
+ NULL,
+ cdata->winsize);
+ if (rc != 0) {
fprintf(stderr, "Failed to open pty\n");
return SSH_ERROR;
}
return SSH_OK;
}
-static int pty_resize(ssh_session session, ssh_channel channel, int cols,
- int rows, int py, int px, void *userdata) {
+static int
+pty_resize(ssh_session session,
+ ssh_channel channel,
+ int cols,
+ int rows,
+ int py,
+ int px,
+ void *userdata)
+{
struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
- (void) session;
- (void) channel;
+ (void)session;
+ (void)channel;
cdata->winsize->ws_row = rows;
cdata->winsize->ws_col = cols;
@@ -395,30 +358,36 @@ static int pty_resize(ssh_session session, ssh_channel channel, int cols,
return SSH_ERROR;
}
-static int exec_pty(const char *mode, const char *command,
- struct channel_data_struct *cdata) {
- switch(cdata->pid = fork()) {
- case -1:
- close(cdata->pty_master);
- close(cdata->pty_slave);
- fprintf(stderr, "Failed to fork\n");
- return SSH_ERROR;
- case 0:
- close(cdata->pty_master);
- if (login_tty(cdata->pty_slave) != 0) {
- exit(1);
- }
- execl("/bin/sh", "sh", mode, command, NULL);
- exit(0);
- default:
- close(cdata->pty_slave);
- /* pty fd is bi-directional */
- cdata->child_stdout = cdata->child_stdin = cdata->pty_master;
+static int
+exec_pty(const char *mode,
+ const char *command,
+ struct channel_data_struct *cdata)
+{
+ cdata->pid = fork();
+ switch (cdata->pid) {
+ case -1:
+ close(cdata->pty_master);
+ close(cdata->pty_slave);
+ fprintf(stderr, "Failed to fork\n");
+ return SSH_ERROR;
+ case 0:
+ close(cdata->pty_master);
+ if (login_tty(cdata->pty_slave) != 0) {
+ exit(1);
+ }
+ execl("/bin/sh", "sh", mode, command, NULL);
+ exit(0);
+ default:
+ close(cdata->pty_slave);
+ /* pty fd is bi-directional */
+ cdata->child_stdout = cdata->child_stdin = cdata->pty_master;
}
return SSH_OK;
}
-static int exec_nopty(const char *command, struct channel_data_struct *cdata) {
+static int
+exec_nopty(const char *command, struct channel_data_struct *cdata)
+{
int in[2], out[2], err[2];
/* Do the plumbing to be able to talk with the child process. */
@@ -432,23 +401,24 @@ static int exec_nopty(const char *command, struct channel_data_struct *cdata) {
goto stderr_failed;
}
- switch(cdata->pid = fork()) {
- case -1:
- goto fork_failed;
- case 0:
- /* Finish the plumbing in the child process. */
- close(in[1]);
- close(out[0]);
- close(err[0]);
- dup2(in[0], STDIN_FILENO);
- dup2(out[1], STDOUT_FILENO);
- dup2(err[1], STDERR_FILENO);
- close(in[0]);
- close(out[1]);
- close(err[1]);
- /* exec the requested command. */
- execl("/bin/sh", "sh", "-c", command, NULL);
- exit(0);
+ cdata->pid = fork();
+ switch (cdata->pid) {
+ case -1:
+ goto fork_failed;
+ case 0:
+ /* Finish the plumbing in the child process. */
+ close(in[1]);
+ close(out[0]);
+ close(err[0]);
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+ dup2(err[1], STDERR_FILENO);
+ close(in[0]);
+ close(out[1]);
+ close(err[1]);
+ /* exec the requested command. */
+ execl("/bin/sh", "sh", "-c", command, NULL);
+ exit(0);
}
close(in[0]);
@@ -474,15 +444,18 @@ static int exec_nopty(const char *command, struct channel_data_struct *cdata) {
return SSH_ERROR;
}
-static int exec_request(ssh_session session, ssh_channel channel,
- const char *command, void *userdata) {
- struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
-
+static int
+exec_request(ssh_session session,
+ ssh_channel channel,
+ const char *command,
+ void *userdata)
+{
+ struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
- (void) session;
- (void) channel;
+ (void)session;
+ (void)channel;
- if(cdata->pid > 0) {
+ if (cdata->pid > 0) {
return SSH_ERROR;
}
@@ -492,14 +465,15 @@ static int exec_request(ssh_session session, ssh_channel channel,
return exec_nopty(command, cdata);
}
-static int shell_request(ssh_session session, ssh_channel channel,
- void *userdata) {
- struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
+static int
+shell_request(ssh_session session, ssh_channel channel, void *userdata)
+{
+ struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
- (void) session;
- (void) channel;
+ (void)session;
+ (void)channel;
- if(cdata->pid > 0) {
+ if (cdata->pid > 0) {
return SSH_ERROR;
}
@@ -510,20 +484,28 @@ static int shell_request(ssh_session session, ssh_channel channel,
return SSH_OK;
}
-static int subsystem_request(ssh_session session, ssh_channel channel,
- const char *subsystem, void *userdata) {
- /* subsystem requests behave simillarly to exec requests. */
+static int
+subsystem_request(ssh_session session,
+ ssh_channel channel,
+ const char *subsystem,
+ void *userdata)
+{
+ /* subsystem requests behave similarly to exec requests. */
if (strcmp(subsystem, "sftp") == 0) {
return exec_request(session, channel, SFTP_SERVER_PATH, userdata);
}
return SSH_ERROR;
}
-static int auth_password(ssh_session session, const char *user,
- const char *pass, void *userdata) {
- struct session_data_struct *sdata = (struct session_data_struct *) userdata;
+static int
+auth_password(ssh_session session,
+ const char *user,
+ const char *pass,
+ void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
- (void) session;
+ (void)session;
if (strcmp(user, username) == 0 && strcmp(pass, password) == 0) {
sdata->authenticated = 1;
@@ -534,16 +516,26 @@ static int auth_password(ssh_session session, const char *user,
return SSH_AUTH_DENIED;
}
-static int auth_publickey(ssh_session session,
- const char *user,
- struct ssh_key_struct *pubkey,
- char signature_state,
- void *userdata)
+static int
+auth_publickey(ssh_session session,
+ const char *user,
+ struct ssh_key_struct *pubkey,
+ char signature_state,
+ void *userdata)
{
- struct session_data_struct *sdata = (struct session_data_struct *) userdata;
-
- (void) user;
- (void) session;
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
+ ssh_key key = NULL;
+ FILE *fp = NULL;
+ char line[AUTH_KEYS_MAX_LINE_SIZE] = {0};
+ char *p = NULL;
+ const char *q = NULL;
+ unsigned int lineno = 0;
+ int result;
+ int i;
+ enum ssh_keytypes_e type;
+
+ (void)user;
+ (void)session;
if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
return SSH_AUTH_SUCCESS;
@@ -553,45 +545,159 @@ static int auth_publickey(ssh_session session,
return SSH_AUTH_DENIED;
}
- // valid so far. Now look through authorized keys for a match
- if (authorizedkeys[0]) {
- ssh_key key = NULL;
- int result;
- struct stat buf;
-
- if (stat(authorizedkeys, &buf) == 0) {
- result = ssh_pki_import_pubkey_file( authorizedkeys, &key );
- if ((result != SSH_OK) || (key==NULL)) {
- fprintf(stderr,
- "Unable to import public key file %s\n",
- authorizedkeys);
- } else {
- result = ssh_key_cmp( key, pubkey, SSH_KEY_CMP_PUBLIC );
- ssh_key_free(key);
- if (result == 0) {
- sdata->authenticated = 1;
- return SSH_AUTH_SUCCESS;
- }
+ fp = fopen(authorizedkeys, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Error: opening authorized keys file %s failed, reason: %s\n",
+ authorizedkeys, strerror(errno));
+ return SSH_AUTH_DENIED;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ lineno++;
+
+ /* Skip leading whitespace and ignore comments */
+ p = line;
+
+ for (i = 0; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (!isspace((int)p[i])) {
+ break;
+ }
+ }
+
+ if (i >= AUTH_KEYS_MAX_LINE_SIZE) {
+ fprintf(stderr,
+ "warning: The line %d in %s too long! Skipping.\n",
+ lineno,
+ authorizedkeys);
+ continue;
+ }
+
+ if (p[i] == '#' || p[i] == '\0' || p[i] == '\n') {
+ continue;
+ }
+
+ q = &p[i];
+ for (; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (isspace((int)p[i])) {
+ p[i] = '\0';
+ break;
+ }
+ }
+
+ type = ssh_key_type_from_name(q);
+
+ i++;
+ if (i >= AUTH_KEYS_MAX_LINE_SIZE) {
+ fprintf(stderr,
+ "warning: The line %d in %s too long! Skipping.\n",
+ lineno,
+ authorizedkeys);
+ continue;
+ }
+
+ q = &p[i];
+ for (; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (isspace((int)p[i])) {
+ p[i] = '\0';
+ break;
}
}
+
+ result = ssh_pki_import_pubkey_base64(q, type, &key);
+ if (result != SSH_OK) {
+ fprintf(stderr,
+ "Warning: Cannot import key on line no. %d in authorized keys file: %s\n",
+ lineno,
+ authorizedkeys);
+ continue;
+ }
+
+ result = ssh_key_cmp(key, pubkey, SSH_KEY_CMP_PUBLIC);
+ ssh_key_free(key);
+ if (result == 0) {
+ sdata->authenticated = 1;
+ fclose(fp);
+ return SSH_AUTH_SUCCESS;
+ }
}
+ if (ferror(fp) != 0) {
+ fprintf(stderr,
+ "Error: Reading from authorized keys file %s failed, reason: %s\n",
+ authorizedkeys, strerror(errno));
+ }
+ fclose(fp);
- // no matches
- sdata->authenticated = 0;
+ /* no matches */
return SSH_AUTH_DENIED;
}
-static ssh_channel channel_open(ssh_session session, void *userdata) {
- struct session_data_struct *sdata = (struct session_data_struct *) userdata;
+static int kbdint_check_response(ssh_session session)
+{
+ int count, cmp;
+ const char *answer = NULL;
+
+ count = ssh_userauth_kbdint_getnanswers(session);
+ if (count != 2) {
+ return 0;
+ }
+
+ answer = ssh_userauth_kbdint_getanswer(session, 0);
+ cmp = strcasecmp("omnitrix", answer);
+ if (cmp != 0) {
+ return 0;
+ }
+
+ answer = ssh_userauth_kbdint_getanswer(session, 1);
+ cmp = strcmp("000", answer);
+ if (cmp != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+auth_kbdint(ssh_message message, ssh_session session, void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
+ const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
+ const char *instruction = "Most powerful weapon in the galaxy";
+ const char *prompts[2] = {"Name of the weapon: ", "Destruct Code: "};
+ char echo[] = {1, 0};
+ if (!ssh_message_auth_kbdint_is_response(message)) {
+ printf("User %s wants to auth with kbdint\n",
+ ssh_message_auth_user(message));
+ ssh_message_auth_interactive_request(message,
+ name,
+ instruction,
+ 2,
+ prompts,
+ echo);
+ return SSH_AUTH_INFO;
+ } else {
+ if (kbdint_check_response(session)) {
+ sdata->authenticated = 1;
+ return SSH_AUTH_SUCCESS;
+ }
+ return SSH_AUTH_DENIED;
+ }
+}
+
+static ssh_channel
+channel_open(ssh_session session, void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
sdata->channel = ssh_channel_new(session);
return sdata->channel;
}
-static int process_stdout(socket_t fd, int revents, void *userdata) {
+static int
+process_stdout(socket_t fd, int revents, void *userdata)
+{
char buf[BUF_SIZE];
int n = -1;
- ssh_channel channel = (ssh_channel) userdata;
+ ssh_channel channel = (ssh_channel)userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
@@ -603,10 +709,12 @@ static int process_stdout(socket_t fd, int revents, void *userdata) {
return n;
}
-static int process_stderr(socket_t fd, int revents, void *userdata) {
+static int
+process_stderr(socket_t fd, int revents, void *userdata)
+{
char buf[BUF_SIZE];
int n = -1;
- ssh_channel channel = (ssh_channel) userdata;
+ ssh_channel channel = (ssh_channel)userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
@@ -618,7 +726,9 @@ static int process_stderr(socket_t fd, int revents, void *userdata) {
return n;
}
-static void handle_session(ssh_event event, ssh_session session) {
+static void
+handle_session(ssh_event event, ssh_session session)
+{
int n;
int rc = 0;
@@ -662,14 +772,15 @@ static void handle_session(ssh_event event, ssh_session session) {
struct ssh_server_callbacks_struct server_cb = {
.userdata = &sdata,
.auth_password_function = auth_password,
+ .auth_kbdint_function = auth_kbdint,
.channel_open_request_session_function = channel_open,
};
if (authorizedkeys[0]) {
server_cb.auth_pubkey_function = auth_publickey;
- ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
+ ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_INTERACTIVE);
} else
- ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
+ ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
ssh_callbacks_init(&server_cb);
ssh_callbacks_init(&channel_cb);
@@ -731,8 +842,8 @@ static void handle_session(ssh_event event, ssh_session session) {
ssh_channel_close(sdata.channel);
}
}
- } while(ssh_channel_is_open(sdata.channel) &&
- (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
+ } while (ssh_channel_is_open(sdata.channel) &&
+ (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
close(cdata.pty_master);
close(cdata.child_stdin);
@@ -765,12 +876,14 @@ static void handle_session(ssh_event event, ssh_session session) {
#ifdef WITH_FORK
/* SIGCHLD handler for cleaning up dead children. */
-static void sigchld_handler(int signo) {
- (void) signo;
+static void sigchld_handler(int signo)
+{
+ (void)signo;
while (waitpid(-1, NULL, WNOHANG) > 0);
}
#else
-static void *session_thread(void *arg) {
+static void *session_thread(void *arg)
+{
ssh_session session = arg;
ssh_event event;
@@ -789,9 +902,10 @@ static void *session_thread(void *arg) {
}
#endif
-int main(int argc, char **argv) {
- ssh_bind sshbind;
- ssh_session session;
+int main(int argc, char **argv)
+{
+ ssh_bind sshbind = NULL;
+ ssh_session session = NULL;
int rc;
#ifdef WITH_FORK
struct sigaction sa;
@@ -829,7 +943,8 @@ int main(int argc, char **argv) {
}
#endif /* HAVE_ARGP_H */
- if(ssh_bind_listen(sshbind) < 0) {
+ rc = ssh_bind_listen(sshbind);
+ if (rc < 0) {
fprintf(stderr, "%s\n", ssh_get_error(sshbind));
ssh_bind_free(sshbind);
ssh_finalize();
@@ -844,34 +959,36 @@ int main(int argc, char **argv) {
}
/* Blocks until there is a new incoming connection. */
- if(ssh_bind_accept(sshbind, session) != SSH_ERROR) {
+ rc = ssh_bind_accept(sshbind, session);
+ if (rc != SSH_ERROR) {
#ifdef WITH_FORK
ssh_event event;
- switch(fork()) {
- case 0:
- /* Remove the SIGCHLD handler inherited from parent. */
- sa.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &sa, NULL);
- /* Remove socket binding, which allows us to restart the
- * parent process, without terminating existing sessions. */
- ssh_bind_free(sshbind);
-
- event = ssh_event_new();
- if (event != NULL) {
- /* Blocks until the SSH session ends by either
- * child process exiting, or client disconnecting. */
- handle_session(event, session);
- ssh_event_free(event);
- } else {
- fprintf(stderr, "Could not create polling context\n");
- }
- ssh_disconnect(session);
- ssh_free(session);
-
- exit(0);
- case -1:
- fprintf(stderr, "Failed to fork\n");
+ pid_t pid = fork();
+ switch (pid) {
+ case 0:
+ /* Remove the SIGCHLD handler inherited from parent. */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &sa, NULL);
+ /* Remove socket binding, which allows us to restart the
+ * parent process, without terminating existing sessions. */
+ ssh_bind_free(sshbind);
+
+ event = ssh_event_new();
+ if (event != NULL) {
+ /* Blocks until the SSH session ends by either
+ * child process exiting, or client disconnecting. */
+ handle_session(event, session);
+ ssh_event_free(event);
+ } else {
+ fprintf(stderr, "Could not create polling context\n");
+ }
+ ssh_disconnect(session);
+ ssh_free(session);
+
+ exit(0);
+ case -1:
+ fprintf(stderr, "Failed to fork\n");
}
#else
pthread_t tid;
diff --git a/examples/sshd_direct-tcpip.c b/examples/sshd_direct-tcpip.c
index 84389b28..9bb09111 100644
--- a/examples/sshd_direct-tcpip.c
+++ b/examples/sshd_direct-tcpip.c
@@ -361,7 +361,7 @@ my_fd_data_function(UNUSED_PARAM(socket_t fd),
{
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
ssh_channel channel = event_fd_data->channel;
- ssh_session session;
+ ssh_session session = NULL;
int len, i, wr;
char buf[BUF_SIZE];
int blocking;
@@ -455,8 +455,8 @@ open_tcp_socket(ssh_message msg)
{
struct sockaddr_in sin;
int forwardsock = -1;
- struct hostent *host;
- const char *dest_hostname;
+ struct hostent *host = NULL;
+ const char *dest_hostname = NULL;
int dest_port;
forwardsock = socket(AF_INET, SOCK_STREAM, 0);
@@ -499,8 +499,8 @@ message_callback(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(void *userdata))
{
ssh_channel channel;
- int socket_fd, *pFd;
- struct ssh_channel_callbacks_struct *cb_chan;
+ int socket_fd, *pFd = NULL;
+ struct ssh_channel_callbacks_struct *cb_chan = NULL;
struct event_fd_data_struct *event_fd_data;
_ssh_log(SSH_LOG_PACKET, "=== message_callback", "Message type: %d",
@@ -526,7 +526,7 @@ message_callback(UNUSED_PARAM(ssh_session session),
}
pFd = malloc(sizeof *pFd);
- cb_chan = malloc(sizeof *cb_chan);
+ cb_chan = calloc(1, sizeof *cb_chan);
event_fd_data = malloc(sizeof *event_fd_data);
if (pFd == NULL || cb_chan == NULL || event_fd_data == NULL) {
SAFE_FREE(pFd);
@@ -655,8 +655,8 @@ static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
int
main(int argc, char **argv)
{
- ssh_session session;
- ssh_bind sshbind;
+ ssh_session session = NULL;
+ ssh_bind sshbind = NULL;
struct ssh_server_callbacks_struct cb = {
.userdata = NULL,
.auth_password_function = auth_password,
diff --git a/examples/sshnetcat.c b/examples/sshnetcat.c
index 59b0a289..3dba51e8 100644
--- a/examples/sshnetcat.c
+++ b/examples/sshnetcat.c
@@ -39,223 +39,237 @@ clients must be made or how a client should react.
#define BUF_SIZE 4096
#endif
-char *host;
-const char *desthost="localhost";
-const char *port="22";
+char *host = NULL;
+const char *desthost = "localhost";
+const char *port = "22";
#ifdef WITH_PCAP
#include
-char *pcap_file=NULL;
+char *pcap_file = NULL;
#endif
static void usage(void)
{
- fprintf(stderr,"Usage : sshnetcat [user@]host forwarded_host forwarded_port\n");
- exit(1);
+ fprintf(stderr,
+ "Usage : sshnetcat [user@]host forwarded_host forwarded_port\n");
+ exit(1);
}
-static int opts(int argc, char **argv){
+static int opts(int argc, char **argv)
+{
int i;
- while((i=getopt(argc,argv,"P:"))!=-1){
- switch(i){
+ while ((i = getopt(argc, argv, "P:")) != -1) {
+ switch (i) {
#ifdef WITH_PCAP
- case 'P':
- pcap_file=optarg;
- break;
+ case 'P':
+ pcap_file = optarg;
+ break;
#endif
- default:
- fprintf(stderr,"unknown option %c\n",optopt);
- usage();
+ default:
+ fprintf(stderr, "unknown option %c\n", optopt);
+ usage();
}
}
- if(optind < argc)
- host=argv[optind++];
- if(optind < argc)
- desthost=argv[optind++];
- if(optind < argc)
- port=argv[optind++];
- if(host==NULL)
+ if (optind < argc)
+ host = argv[optind++];
+ if (optind < argc)
+ desthost = argv[optind++];
+ if (optind < argc)
+ port = argv[optind++];
+ if (host == NULL)
usage();
return 0;
}
-static void select_loop(ssh_session session,ssh_channel channel){
- fd_set fds;
- struct timeval timeout;
- char buffer[BUF_SIZE];
- /* channels will be set to the channels to poll.
- * outchannels will contain the result of the poll
- */
- ssh_channel channels[2], outchannels[2];
- int lus;
- int eof=0;
- int maxfd;
- int ret;
- while(channel){
- do{
+static void select_loop(ssh_session session, ssh_channel channel)
+{
+ fd_set fds;
+ struct timeval timeout;
+ char buffer[BUF_SIZE];
+ /* channels will be set to the channels to poll.
+ * outchannels will contain the result of the poll
+ */
+ ssh_channel channels[2], outchannels[2];
+ int lus;
+ int eof = 0;
+ int maxfd;
+ int ret;
+ while (channel) {
+ do {
int fd;
ZERO_STRUCT(fds);
- FD_ZERO(&fds);
- if(!eof)
- FD_SET(0,&fds);
- timeout.tv_sec=30;
- timeout.tv_usec=0;
+ FD_ZERO(&fds);
+ if (!eof)
+ FD_SET(0, &fds);
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
fd = ssh_get_fd(session);
if (fd == -1) {
- fprintf(stderr, "Error getting the session file descriptor: %s\n",
- ssh_get_error(session));
+ fprintf(stderr,
+ "Error getting the session file descriptor: %s\n",
+ ssh_get_error(session));
return;
}
FD_SET(fd, &fds);
maxfd = fd + 1;
- channels[0]=channel; // set the first channel we want to read from
- channels[1]=NULL;
- ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout);
- if(ret==EINTR)
- continue;
- if(FD_ISSET(0,&fds)){
- lus=read(0,buffer,sizeof(buffer));
- if(lus)
- ssh_channel_write(channel,buffer,lus);
- else {
- eof=1;
- ssh_channel_send_eof(channel);
- }
- }
- if(channel && ssh_channel_is_closed(channel)){
- ssh_channel_free(channel);
- channel=NULL;
- channels[0]=NULL;
- }
- if(outchannels[0]){
- while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)){
- lus = ssh_channel_read(channel,buffer,sizeof(buffer),0);
- if(lus==-1){
- fprintf(stderr, "Error reading channel: %s\n",
- ssh_get_error(session));
- return;
- }
- if(lus==0){
- ssh_channel_free(channel);
- channel=channels[0]=NULL;
- } else {
- ret = write(1, buffer, lus);
- if (ret < 0) {
- fprintf(stderr, "Error writing to stdin: %s",
- strerror(errno));
- return;
- }
- }
- }
- while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)){ /* stderr */
- lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
- if(lus==-1){
- fprintf(stderr, "Error reading channel: %s\n",
- ssh_get_error(session));
- return;
- }
- if(lus==0){
- ssh_channel_free(channel);
- channel=channels[0]=NULL;
- } else {
- ret = write(2, buffer, lus);
- if (ret < 0) {
- fprintf(stderr, "Error writing to stderr: %s",
- strerror(errno));
- return;
- }
+ channels[0] = channel; // set the first channel we want to read from
+ channels[1] = NULL;
+ ret = ssh_select(channels, outchannels, maxfd, &fds, &timeout);
+ if (ret == EINTR)
+ continue;
+ if (FD_ISSET(0, &fds)) {
+ lus = read(0, buffer, sizeof(buffer));
+ if (lus)
+ ssh_channel_write(channel, buffer, lus);
+ else {
+ eof = 1;
+ ssh_channel_send_eof(channel);
+ }
+ }
+ if (channel && ssh_channel_is_closed(channel)) {
+ ssh_channel_free(channel);
+ channel = NULL;
+ channels[0] = NULL;
+ }
+ if (outchannels[0]) {
+ while (channel && ssh_channel_is_open(channel) &&
+ ssh_channel_poll(channel, 0)) {
+ lus = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
+ if (lus == -1) {
+ fprintf(stderr,
+ "Error reading channel: %s\n",
+ ssh_get_error(session));
+ return;
}
- }
- }
- if(channel && ssh_channel_is_closed(channel)){
- ssh_channel_free(channel);
- channel=NULL;
- }
- } while (ret==EINTR || ret==SSH_EINTR);
-
- }
+ if (lus == 0) {
+ ssh_channel_free(channel);
+ channel = channels[0] = NULL;
+ } else {
+ ret = write(1, buffer, lus);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error writing to stdin: %s",
+ strerror(errno));
+ return;
+ }
+ }
+ }
+ while (channel && ssh_channel_is_open(channel) &&
+ ssh_channel_poll(channel, 1)) { /* stderr */
+ lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
+ if (lus == -1) {
+ fprintf(stderr,
+ "Error reading channel: %s\n",
+ ssh_get_error(session));
+ return;
+ }
+ if (lus == 0) {
+ ssh_channel_free(channel);
+ channel = channels[0] = NULL;
+ } else {
+ ret = write(2, buffer, lus);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error writing to stderr: %s",
+ strerror(errno));
+ return;
+ }
+ }
+ }
+ }
+ if (channel && ssh_channel_is_closed(channel)) {
+ ssh_channel_free(channel);
+ channel = NULL;
+ }
+ } while (ret == EINTR || ret == SSH_EINTR);
+ }
}
-static void forwarding(ssh_session session){
+static void forwarding(ssh_session session)
+{
ssh_channel channel;
int r;
channel = ssh_channel_new(session);
r = ssh_channel_open_forward(channel, desthost, atoi(port), "localhost", 22);
- if(r<0) {
- printf("error forwarding port : %s\n",ssh_get_error(session));
+ if (r < 0) {
+ printf("error forwarding port : %s\n", ssh_get_error(session));
return;
}
- select_loop(session,channel);
+ select_loop(session, channel);
}
-static int client(ssh_session session){
- int auth=0;
- char *banner;
- int state;
+static int client(ssh_session session)
+{
+ int auth = 0;
+ char *banner = NULL;
+ int state;
- if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0)
- return -1;
- ssh_options_parse_config(session, NULL);
+ if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0)
+ return -1;
+ ssh_options_parse_config(session, NULL);
- if(ssh_connect(session)){
- fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
- return -1;
- }
- state=verify_knownhost(session);
- if (state != 0)
- return -1;
- ssh_userauth_none(session, NULL);
- banner=ssh_get_issue_banner(session);
- if(banner){
- printf("%s\n",banner);
- free(banner);
- }
- auth=authenticate_console(session);
- if(auth != SSH_AUTH_SUCCESS){
- return -1;
- }
- forwarding(session);
- return 0;
+ if (ssh_connect(session)) {
+ fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
+ return -1;
+ }
+ state = verify_knownhost(session);
+ if (state != 0)
+ return -1;
+ ssh_userauth_none(session, NULL);
+ banner = ssh_get_issue_banner(session);
+ if (banner) {
+ printf("%s\n", banner);
+ free(banner);
+ }
+ auth = authenticate_console(session);
+ if (auth != SSH_AUTH_SUCCESS) {
+ return -1;
+ }
+ forwarding(session);
+ return 0;
}
#ifdef WITH_PCAP
ssh_pcap_file pcap;
void set_pcap(ssh_session session);
-void set_pcap(ssh_session session){
- if(!pcap_file)
- return;
- pcap=ssh_pcap_file_new();
- if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){
- printf("Error opening pcap file\n");
- ssh_pcap_file_free(pcap);
- pcap=NULL;
- return;
- }
- ssh_set_pcap_file(session,pcap);
+void set_pcap(ssh_session session)
+{
+ if (!pcap_file)
+ return;
+ pcap = ssh_pcap_file_new();
+ if (ssh_pcap_file_open(pcap, pcap_file) == SSH_ERROR) {
+ printf("Error opening pcap file\n");
+ ssh_pcap_file_free(pcap);
+ pcap = NULL;
+ return;
+ }
+ ssh_set_pcap_file(session, pcap);
}
void cleanup_pcap(void);
void cleanup_pcap(void)
{
- ssh_pcap_file_free(pcap);
- pcap = NULL;
+ ssh_pcap_file_free(pcap);
+ pcap = NULL;
}
#endif
-int main(int argc, char **argv){
- ssh_session session;
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
session = ssh_new();
- if(ssh_options_getopt(session, &argc, argv)) {
- fprintf(stderr, "error parsing command line :%s\n",
- ssh_get_error(session));
- usage();
+ if (ssh_options_getopt(session, &argc, argv)) {
+ fprintf(stderr,
+ "error parsing command line :%s\n",
+ ssh_get_error(session));
+ usage();
}
- opts(argc,argv);
+ opts(argc, argv);
#ifdef WITH_PCAP
set_pcap(session);
#endif
diff --git a/include/libssh/CMakeLists.txt b/include/libssh/CMakeLists.txt
index 93445680..109e6837 100644
--- a/include/libssh/CMakeLists.txt
+++ b/include/libssh/CMakeLists.txt
@@ -29,6 +29,13 @@ if (WITH_SERVER)
endif (WITH_SFTP)
endif (WITH_SERVER)
+if (WITH_FIDO2)
+ set(libssh_HDRS
+ ${libssh_HDRS}
+ sk_api.h
+ )
+endif (WITH_FIDO2)
+
install(
FILES
${libssh_HDRS}
diff --git a/include/libssh/auth.h b/include/libssh/auth.h
index b358b7a2..309930d5 100644
--- a/include/libssh/auth.h
+++ b/include/libssh/auth.h
@@ -52,42 +52,45 @@ typedef struct ssh_kbdint_struct* ssh_kbdint;
ssh_kbdint ssh_kbdint_new(void);
void ssh_kbdint_clean(ssh_kbdint kbd);
void ssh_kbdint_free(ssh_kbdint kbd);
+int ssh_userauth_gssapi_keyex(ssh_session session);
/** @internal
* States of authentication in the client-side. They describe
* what was the last response from the server
*/
enum ssh_auth_state_e {
- /** No authentication asked */
- SSH_AUTH_STATE_NONE=0,
- /** Last authentication response was a partial success */
- SSH_AUTH_STATE_PARTIAL,
- /** Last authentication response was a success */
- SSH_AUTH_STATE_SUCCESS,
- /** Last authentication response was failed */
- SSH_AUTH_STATE_FAILED,
- /** Last authentication was erroneous */
- SSH_AUTH_STATE_ERROR,
- /** Last state was a keyboard-interactive ask for info */
- SSH_AUTH_STATE_INFO,
- /** Last state was a public key accepted for authentication */
- SSH_AUTH_STATE_PK_OK,
- /** We asked for a keyboard-interactive authentication */
- SSH_AUTH_STATE_KBDINT_SENT,
- /** We have sent an userauth request with gssapi-with-mic */
- SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
- /** We are exchanging tokens until authentication */
- SSH_AUTH_STATE_GSSAPI_TOKEN,
- /** We have sent the MIC and expecting to be authenticated */
- SSH_AUTH_STATE_GSSAPI_MIC_SENT,
- /** We have offered a pubkey to check if it is supported */
- SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
- /** We have sent pubkey and signature expecting to be authenticated */
- SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
- /** We have sent a password expecting to be authenticated */
- SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
- /** We have sent a request without auth information (method 'none') */
- SSH_AUTH_STATE_AUTH_NONE_SENT,
+ /** No authentication asked */
+ SSH_AUTH_STATE_NONE = 0,
+ /** Last authentication response was a partial success */
+ SSH_AUTH_STATE_PARTIAL,
+ /** Last authentication response was a success */
+ SSH_AUTH_STATE_SUCCESS,
+ /** Last authentication response was failed */
+ SSH_AUTH_STATE_FAILED,
+ /** Last authentication was erroneous */
+ SSH_AUTH_STATE_ERROR,
+ /** Last state was a keyboard-interactive ask for info */
+ SSH_AUTH_STATE_INFO,
+ /** Last state was a public key accepted for authentication */
+ SSH_AUTH_STATE_PK_OK,
+ /** We asked for a keyboard-interactive authentication */
+ SSH_AUTH_STATE_KBDINT_SENT,
+ /** We have sent an userauth request with gssapi-with-mic */
+ SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
+ /** We are exchanging tokens until authentication */
+ SSH_AUTH_STATE_GSSAPI_TOKEN,
+ /** We have sent the MIC and expecting to be authenticated */
+ SSH_AUTH_STATE_GSSAPI_MIC_SENT,
+ /** We have offered a pubkey to check if it is supported */
+ SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
+ /** We have sent pubkey and signature expecting to be authenticated */
+ SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
+ /** We have sent a password expecting to be authenticated */
+ SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
+ /** We have sent a request without auth information (method 'none') */
+ SSH_AUTH_STATE_AUTH_NONE_SENT,
+ /** We have sent the MIC and expecting to be authenticated */
+ SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT,
};
/** @internal
diff --git a/include/libssh/bignum.h b/include/libssh/bignum.h
index 6b5dc1a2..37e87bc0 100644
--- a/include/libssh/bignum.h
+++ b/include/libssh/bignum.h
@@ -31,6 +31,7 @@ extern "C" {
bignum ssh_make_string_bn(ssh_string string);
ssh_string ssh_make_bignum_string(bignum num);
+ssh_string ssh_make_padded_bignum_string(bignum num, size_t pad_len);
void ssh_print_bignum(const char *which, const_bignum num);
#ifdef __cplusplus
diff --git a/include/libssh/bind.h b/include/libssh/bind.h
index cd9199b6..a848003e 100644
--- a/include/libssh/bind.h
+++ b/include/libssh/bind.h
@@ -54,6 +54,8 @@ struct ssh_bind_struct {
char *pubkey_accepted_key_types;
char* moduli_file;
int rsa_min_size;
+ bool gssapi_key_exchange;
+ char *gssapi_key_exchange_algs;
};
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct
diff --git a/include/libssh/bind_config.h b/include/libssh/bind_config.h
index 5f2dccce..54346d8e 100644
--- a/include/libssh/bind_config.h
+++ b/include/libssh/bind_config.h
@@ -52,6 +52,7 @@ enum ssh_bind_config_opcode_e {
BIND_CFG_MATCH,
BIND_CFG_PUBKEY_ACCEPTED_KEY_TYPES,
BIND_CFG_HOSTKEY_ALGORITHMS,
+ BIND_CFG_REQUIRED_RSA_SIZE,
BIND_CFG_MAX /* Keep this one last in the list */
};
diff --git a/include/libssh/blf.h b/include/libssh/blf.h
index 201821a2..71928a7d 100644
--- a/include/libssh/blf.h
+++ b/include/libssh/blf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */
+/* $OpenBSD: blf.h,v 1.8 2021/11/29 01:04:45 djm Exp $ */
/*
* Blowfish - a fast block cipher designed by Bruce Schneier
*
@@ -13,10 +13,7 @@
* 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 acknowledgement:
- * This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
+ * 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
diff --git a/include/libssh/buffer.h b/include/libssh/buffer.h
index 1fce7b76..108225f1 100644
--- a/include/libssh/buffer.h
+++ b/include/libssh/buffer.h
@@ -42,10 +42,6 @@ int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len);
void *ssh_buffer_allocate(struct ssh_buffer_struct *buffer, uint32_t len);
int ssh_buffer_allocate_size(struct ssh_buffer_struct *buffer, uint32_t len);
-int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
- const char *format,
- size_t argc,
- va_list ap);
int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
const char *format,
size_t argc,
@@ -78,6 +74,8 @@ ssh_string ssh_buffer_get_ssh_string(ssh_buffer buffer);
uint32_t ssh_buffer_pass_bytes_end(ssh_buffer buffer, uint32_t len);
uint32_t ssh_buffer_pass_bytes(ssh_buffer buffer, uint32_t len);
+ssh_buffer ssh_buffer_dup(const ssh_buffer buffer);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/callbacks.h b/include/libssh/callbacks.h
index 34016595..e4ecf726 100644
--- a/include/libssh/callbacks.h
+++ b/include/libssh/callbacks.h
@@ -113,6 +113,17 @@ typedef void (*ssh_status_callback) (ssh_session session, float status,
typedef void (*ssh_global_request_callback) (ssh_session session,
ssh_message message, void *userdata);
+/**
+ * @brief SSH connect status callback. These are functions that report the
+ * status of the connection i,e. a function indicating the completed percentage
+ * of the connection
+ * steps.
+ * @param userdata Userdata to be passed to the callback function.
+ * @param status Percentage of connection status, going from 0.0 to 1.0
+ * once connection is done.
+ */
+typedef void (*ssh_connect_status_callback)(void *userdata, float status);
+
/**
* @brief Handles an SSH new channel open X11 request. This happens when the server
* sends back an X11 connection attempt. This is a client-side API
@@ -181,7 +192,7 @@ struct ssh_callbacks_struct {
* This function gets called during connection time to indicate the
* percentage of connection steps completed.
*/
- void (*connect_status_function)(void *userdata, float status);
+ ssh_connect_status_callback connect_status_function;
/**
* This function will be called each time a global request is received.
*/
@@ -209,36 +220,41 @@ typedef struct ssh_callbacks_struct *ssh_callbacks;
* @param user User that wants to authenticate
* @param password Password used for authentication
* @param userdata Userdata to be passed to the callback function.
- * @returns SSH_AUTH_SUCCESS Authentication is accepted.
- * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
- * @returns SSH_AUTH_DENIED Authentication failed.
+ * @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
+ * @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
+ * are needed.
+ * @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user, const char *password,
void *userdata);
/**
- * @brief SSH authentication callback. Tries to authenticates user with the "none" method
- * which is anonymous or passwordless.
+ * @brief SSH authentication callback. Tries to authenticates user with the
+ * "none" method which is anonymous or passwordless.
* @param session Current session handler
* @param user User that wants to authenticate
* @param userdata Userdata to be passed to the callback function.
- * @returns SSH_AUTH_SUCCESS Authentication is accepted.
- * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
- * @returns SSH_AUTH_DENIED Authentication failed.
+ * @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
+ * @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
+ * are needed.
+ * @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, void *userdata);
/**
- * @brief SSH authentication callback. Tries to authenticates user with the "gssapi-with-mic" method
+ * @brief SSH authentication callback. Tries to authenticates user with the
+ * "gssapi-with-mic" method
* @param session Current session handler
* @param user Username of the user (can be spoofed)
* @param principal Authenticated principal of the user, including realm.
* @param userdata Userdata to be passed to the callback function.
- * @returns SSH_AUTH_SUCCESS Authentication is accepted.
- * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
- * @returns SSH_AUTH_DENIED Authentication failed.
- * @warning Implementations should verify that parameter user matches in some way the principal.
- * user and principal can be different. Only the latter is guaranteed to be safe.
+ * @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
+ * @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
+ * are needed.
+ * @returns `SSH_AUTH_DENIED` Authentication failed.
+ * @warning Implementations should verify that parameter user matches in some
+ * way the principal. user and principal can be different. Only the latter is
+ * guaranteed to be safe.
*/
typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *user, const char *principal,
void *userdata);
@@ -248,17 +264,29 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us
* @param session Current session handler
* @param user User that wants to authenticate
* @param pubkey public key used for authentication
- * @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe),
- * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be
- * replied with a SSH_AUTH_DENIED.
+ * @param signature_state `SSH_PUBLICKEY_STATE_NONE` if the key is not signed
+ * (simple public key probe), `SSH_PUBLICKEY_STATE_VALID` if the signature is
+ * valid. Others values should be replied with a `SSH_AUTH_DENIED`.
* @param userdata Userdata to be passed to the callback function.
- * @returns SSH_AUTH_SUCCESS Authentication is accepted.
- * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
- * @returns SSH_AUTH_DENIED Authentication failed.
+ * @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
+ * @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
+ * are needed.
+ * @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey,
char signature_state, void *userdata);
+/**
+ * @brief SSH authentication callback. Tries to authenticates user with the "keyboard-interactive" method
+ * @param message Current message
+ * @param session Current session handler
+ * @param userdata Userdata to be passed to the callback function.
+ * @returns SSH_AUTH_SUCCESS Authentication is accepted.
+ * @returns SSH_AUTH_INFO More info required for authentication.
+ * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
+ * @returns SSH_AUTH_DENIED Authentication failed.
+*/
+typedef int (*ssh_auth_kbdint_callback) (ssh_message message, ssh_session session, void *userdata);
/**
* @brief Handles an SSH service request
@@ -268,7 +296,6 @@ typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user,
* @returns 0 if the request is to be allowed
* @returns -1 if the request should not be allowed
*/
-
typedef int (*ssh_service_request_callback) (ssh_session session, const char *service, void *userdata);
/**
@@ -281,8 +308,9 @@ typedef int (*ssh_service_request_callback) (ssh_session session, const char *se
*/
typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session session, void *userdata);
-/*
+/**
* @brief handle the beginning of a GSSAPI authentication, server side.
+ * Callback should select the oid and also acquire the server credential.
* @param session current session handler
* @param user the username of the client
* @param n_oid number of available oids
@@ -295,35 +323,57 @@ typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session se
typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const char *user,
int n_oid, ssh_string *oids, void *userdata);
-/*
+/**
* @brief handle the negotiation of a security context, server side.
* @param session current session handler
* @param[in] input_token input token provided by client
* @param[out] output_token output of the gssapi accept_sec_context method,
* NULL after completion.
- * @returns SSH_OK if the token was generated correctly or accept_sec_context
+ * @returns `SSH_OK` if the token was generated correctly or accept_sec_context
* returned GSS_S_COMPLETE
- * @returns SSH_ERROR in case of error
+ * @returns `SSH_ERROR` in case of error
* @warning It is not necessary to fill this callback in if libssh is linked
* with libgssapi.
*/
typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session,
ssh_string input_token, ssh_string *output_token, void *userdata);
-/*
+/**
* @brief Verify and authenticates a MIC, server side.
* @param session current session handler
* @param[in] mic input mic to be verified provided by client
* @param[in] mic_buffer buffer of data to be signed.
* @param[in] mic_buffer_size size of mic_buffer
- * @returns SSH_OK if the MIC was authenticated correctly
- * @returns SSH_ERROR in case of error
+ * @returns `SSH_OK` if the MIC was authenticated correctly
+ * @returns `SSH_ERROR` in case of error
* @warning It is not necessary to fill this callback in if libssh is linked
* with libgssapi.
*/
typedef int (*ssh_gssapi_verify_mic_callback) (ssh_session session,
ssh_string mic, void *mic_buffer, size_t mic_buffer_size, void *userdata);
+/**
+ * @brief Handles an SSH new channel open "direct-tcpip" request. This
+ * happens when the client forwards an incoming TCP connection on a port it
+ * wants to forward to the destination. This is a server-side API
+ * @param session current session handler
+ * @param destination_address the address that the TCP connection connected to
+ * @param destination_port the port that the TCP connection connected to
+ * @param originator_address the originator IP address
+ * @param originator_port the originator port
+ * @param userdata Userdata to be passed to the callback function.
+ * @returns a valid ssh_channel handle if the request is to be allowed
+ * @returns NULL if the request should not be allowed
+ * @warning The channel pointer returned by this callback must be closed by the
+ * application.
+ */
+typedef ssh_channel (*ssh_channel_open_request_direct_tcpip_callback)(
+ ssh_session session,
+ const char *destination_address,
+ int destination_port,
+ const char *originator_address,
+ int originator_port,
+ void *userdata);
/**
* This structure can be used to implement a libssh server, with appropriate callbacks.
@@ -365,14 +415,27 @@ struct ssh_server_callbacks_struct {
*/
ssh_channel_open_request_session_callback channel_open_request_session_function;
/** This function will be called when a new gssapi authentication is attempted.
+ * This should select the oid and acquire credential for the server.
*/
ssh_gssapi_select_oid_callback gssapi_select_oid_function;
/** This function will be called when a gssapi token comes in.
*/
ssh_gssapi_accept_sec_ctx_callback gssapi_accept_sec_ctx_function;
- /* This function will be called when a MIC needs to be verified.
+ /** This function will be called when a MIC needs to be verified.
*/
ssh_gssapi_verify_mic_callback gssapi_verify_mic_function;
+ /**
+ * This function will be called when an incoming "direct-tcpip"
+ * request is received.
+ */
+ ssh_channel_open_request_direct_tcpip_callback
+ channel_open_request_direct_tcpip_function;
+
+ /** This function gets called when a client tries to authenticate through
+ * keyboard interactive method.
+ */
+ ssh_auth_kbdint_callback auth_kbdint_function;
+
};
typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
@@ -398,7 +461,7 @@ typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
*
* @param cb The callback structure itself.
*
- * @return SSH_OK on success, SSH_ERROR on error.
+ * @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb);
@@ -529,14 +592,17 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
} \
} while(0)
-/** @brief Prototype for a packet callback, to be called when a new packet arrives
+/** @brief Prototype for a packet callback, to be called when a new packet
+ * arrives
* @param session The current session of the packet
* @param type packet type (see ssh2.h)
- * @param packet buffer containing the packet, excluding size, type and padding fields
+ * @param packet buffer containing the packet, excluding size, type and padding
+ * fields
* @param user user argument to the callback
* and are called each time a packet shows up
- * @returns SSH_PACKET_USED Packet was parsed and used
- * @returns SSH_PACKET_NOT_USED Packet was not used or understood, processing must continue
+ * @returns `SSH_PACKET_USED` Packet was parsed and used
+ * @returns `SSH_PACKET_NOT_USED` Packet was not used or understood, processing
+ * must continue
*/
typedef int (*ssh_packet_callback) (ssh_session session, uint8_t type, ssh_buffer packet, void *user);
@@ -595,7 +661,7 @@ typedef struct ssh_packet_callbacks_struct *ssh_packet_callbacks;
*
* @param cb The callback structure itself.
*
- * @return SSH_OK on success, SSH_ERROR on error.
+ * @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb);
@@ -947,7 +1013,7 @@ typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks;
*
* @param cb The callback structure itself.
*
- * @return SSH_OK on success, SSH_ERROR on error.
+ * @return `SSH_OK` on success, `SSH_ERROR` on error.
* @warning this function will not replace existing callbacks but set the
* new one atop of them.
*/
@@ -966,7 +1032,7 @@ LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel,
*
* @param cb The callback structure itself.
*
- * @return SSH_OK on success, SSH_ERROR on error.
+ * @return `SSH_OK` on success, `SSH_ERROR` on error.
*
* @see ssh_set_channel_callbacks
*/
@@ -983,7 +1049,7 @@ LIBSSH_API int ssh_add_channel_callbacks(ssh_channel channel,
*
* @param cb The callback structure to remove
*
- * @returns SSH_OK on success, SSH_ERROR on error.
+ * @returns `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
ssh_channel_callbacks cb);
@@ -1016,7 +1082,7 @@ struct ssh_threads_callbacks_struct {
* @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which
* contains the different callbacks to be set.
*
- * @returns Always returns SSH_OK.
+ * @returns Always returns `SSH_OK`.
*
* @see ssh_threads_callbacks_struct
* @see SSH_THREADS_PTHREAD
@@ -1056,6 +1122,7 @@ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void);
* @see ssh_threads_set_callbacks
*/
LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void);
+/** @} */
/**
* @brief Set the logging callback function.
@@ -1073,7 +1140,216 @@ LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb);
*/
LIBSSH_API ssh_logging_callback ssh_get_log_callback(void);
-/** @} */
+/**
+ * @brief SSH proxyjump before connection callback. Called before calling
+ * ssh_connect()
+ * @param session Jump session handler
+ * @param userdata Userdata to be passed to the callback function.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+typedef int (*ssh_jump_before_connection_callback)(ssh_session session,
+ void *userdata);
+
+/**
+ * @brief SSH proxyjump verify knownhost callback. Verify the host.
+ * If not specified default function will be used.
+ * @param session Jump session handler
+ * @param userdata Userdata to be passed to the callback function.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+typedef int (*ssh_jump_verify_knownhost_callback)(ssh_session session,
+ void *userdata);
+
+/**
+ * @brief SSH proxyjump user authentication callback. Authenticate the user.
+ * @param session Jump session handler
+ * @param userdata Userdata to be passed to the callback function.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+typedef int (*ssh_jump_authenticate_callback)(ssh_session session,
+ void *userdata);
+
+struct ssh_jump_callbacks_struct {
+ void *userdata;
+ ssh_jump_before_connection_callback before_connection;
+ ssh_jump_verify_knownhost_callback verify_knownhost;
+ ssh_jump_authenticate_callback authenticate;
+};
+
+/* Security key callbacks */
+
+/*
+ * Forward declarations for structs that have been defined in sk_api.h.
+ * If you need to work with the fields inside them, please include
+ * libssh/sk_api.h
+ */
+struct sk_enroll_response;
+struct sk_sign_response;
+struct sk_resident_key;
+struct sk_option;
+
+#define LIBSSH_SK_API_VERSION_MAJOR 0x000a0000
+
+/**
+ * @brief FIDO2/U2F SK API version callback.
+ *
+ * Returns the version of the FIDO2/U2F API that the callbacks implement.
+ * This callback allows custom callback implementations to specify their
+ * SK API version for compatibility checking with libssh's security key
+ * interface.
+ *
+ * @details Version compatibility is determined by comparing the major version
+ * portion (upper 16 bits) of the returned value with SSH_SK_VERSION_MAJOR.
+ *
+ * For compatibility, implementations should return a version where:
+ * (returned_version & SSH_SK_VERSION_MAJOR_MASK) == SSH_SK_VERSION_MAJOR
+ *
+ * This ensures that the callbacks' SK API matches the major version expected
+ * by libssh, while allowing minor version differences for backward
+ * compatibility.
+ *
+ * @see LIBSSH_SK_API_VERSION_MAJOR Current expected major API version
+ * @see SSH_SK_VERSION_MAJOR_MASK Mask for extracting major version (0xffff0000)
+ */
+typedef uint32_t (*sk_api_version_callback)(void);
+
+/**
+ * @brief FIDO2/U2F key enrollment callback.
+ *
+ * Enrolls a new FIDO2/U2F security key credential (private key generation).
+ * This callback handles the creation of new FIDO2/U2F credentials, including
+ * both resident and non-resident keys.
+ *
+ * @param[in] alg The cryptographic algorithm to use
+ * @param[in] challenge Random challenge data for enrollment
+ * @param[in] challenge_len Length of the challenge data
+ * @param[in] application Application identifier (relying party ID)
+ * @param[in] flags Enrollment flags
+ * @param[in] pin PIN for user verification (may be NULL)
+ * @param[in] options Array of enrollment options (device path, user ID, etc.)
+ * @param[out] enroll_response Enrollment response containing public key,
+ * key handle, signature, and attestation data
+ *
+ * @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
+ */
+typedef int (*sk_enroll_callback)(uint32_t alg,
+ const uint8_t *challenge,
+ size_t challenge_len,
+ const char *application,
+ uint8_t flags,
+ const char *pin,
+ struct sk_option **options,
+ struct sk_enroll_response **enroll_response);
+
+/**
+ * @brief FIDO2/U2F security key signing callback.
+ *
+ * Signs data using a FIDO2 security key credential. This callback performs
+ * cryptographic signing operations using previously enrolled FIDO2/U2F
+ * credentials.
+ *
+ * @param[in] alg The cryptographic algorithm used by the key
+ * @param[in] data Data to be signed
+ * @param[in] data_len Length of the data to sign
+ * @param[in] application Application identifier (relying party ID)
+ * @param[in] key_handle Key handle identifying the credential
+ * @param[in] key_handle_len Length of the key handle
+ * @param[in] flags Signing flags
+ * @param[in] pin PIN for user verification (may be NULL)
+ * @param[in] options Array of signing options (device path, etc.)
+ * @param[out] sign_response Signature response containing signature data,
+ * flags, and counter information
+ *
+ * @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
+ */
+typedef int (*sk_sign_callback)(uint32_t alg,
+ const uint8_t *data,
+ size_t data_len,
+ const char *application,
+ const uint8_t *key_handle,
+ size_t key_handle_len,
+ uint8_t flags,
+ const char *pin,
+ struct sk_option **options,
+ struct sk_sign_response **sign_response);
+
+/**
+ * @brief FIDO2 security key resident keys loading callback.
+ *
+ * Enumerates and loads all resident keys (discoverable credentials) stored
+ * on FIDO2 devices. Resident keys are credentials stored directly on
+ * the device itself and can be discovered without prior knowledge
+ * of key handles.
+ *
+ * @param[in] pin PIN for accessing resident keys (required for most operations)
+ * @param[in] options Array of options (device path, etc.)
+ * @param[out] resident_keys Array of resident key structures containing key
+ * data, application IDs, user information, and metadata
+ * @param[out] num_keys_found Number of resident keys found and loaded
+ *
+ * @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
+ */
+typedef int (*sk_load_resident_keys_callback)(
+ const char *pin,
+ struct sk_option **options,
+ struct sk_resident_key ***resident_keys,
+ size_t *num_keys_found);
+
+/**
+ * @brief FIDO2/U2F security key callbacks structure.
+ *
+ * This structure contains callbacks for FIDO2/U2F operations.
+ * It allows applications to provide custom implementations of FIDO2/U2F
+ * operations to override the default libfido2-based implementation.
+ *
+ * @warning These callbacks will only be called if libssh was built with
+ * FIDO2/U2F support enabled. (WITH_FIDO2 = ON).
+ */
+struct ssh_sk_callbacks_struct {
+ /** DON'T SET THIS use ssh_callbacks_init() instead. */
+ size_t size;
+
+ /**
+ * This callback returns the SK API version used by the callback
+ * implementation.
+ *
+ * @see sk_api_version_callback for detailed documentation
+ */
+ sk_api_version_callback api_version;
+
+ /**
+ * This callback enrolls a new FIDO2/U2F credential, generating
+ * a new key pair and optionally storing it on the device itself
+ * (resident keys).
+ *
+ * @see sk_enroll_callback for detailed documentation
+ */
+ sk_enroll_callback enroll;
+
+ /**
+ * This callback performs cryptographic signing operations using a
+ * previously enrolled FIDO2/U2F credential.
+ *
+ * @see sk_sign_callback for detailed documentation
+ */
+ sk_sign_callback sign;
+
+ /**
+ * This callback enumerates and loads all resident keys (discoverable
+ * credentials) stored on the FIDO2 device.
+ *
+ * @see sk_load_resident_keys_callback for detailed documentation
+ */
+ sk_load_resident_keys_callback load_resident_keys;
+};
+
+typedef struct ssh_sk_callbacks_struct *ssh_sk_callbacks;
+
+const struct ssh_sk_callbacks_struct *ssh_sk_get_default_callbacks(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/channels.h b/include/libssh/channels.h
index cb2bea43..7a3535ec 100644
--- a/include/libssh/channels.h
+++ b/include/libssh/channels.h
@@ -80,7 +80,12 @@ struct ssh_channel_struct {
ssh_buffer stdout_buffer;
ssh_buffer stderr_buffer;
void *userarg;
- int exit_status;
+ struct {
+ bool status;
+ uint32_t code;
+ char *signal;
+ bool core_dumped;
+ } exit;
enum ssh_channel_request_state_e request_state;
struct ssh_list *callbacks; /* list of ssh_channel_callbacks */
diff --git a/include/libssh/config.h b/include/libssh/config.h
index 21702391..87cc25be 100644
--- a/include/libssh/config.h
+++ b/include/libssh/config.h
@@ -24,6 +24,7 @@
#ifndef LIBSSH_CONFIG_H_
#define LIBSSH_CONFIG_H_
+#include "libssh/libssh.h"
enum ssh_config_opcode_e {
/* Unknown opcode */
@@ -65,7 +66,15 @@ enum ssh_config_opcode_e {
SOC_IDENTITIESONLY,
SOC_CONTROLMASTER,
SOC_CONTROLPATH,
+ SOC_CERTIFICATE,
+ SOC_REQUIRED_RSA_SIZE,
+ SOC_ADDRESSFAMILY,
+ SOC_GSSAPIKEYEXCHANGE,
+ SOC_GSSAPIKEXALGORITHMS,
SOC_MAX /* Keep this one last in the list */
};
+enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword);
+int ssh_config_parse_line_cli(ssh_session session, const char *line);
+
#endif /* LIBSSH_CONFIG_H_ */
diff --git a/include/libssh/config_parser.h b/include/libssh/config_parser.h
index a7dd42a2..f5d1fee8 100644
--- a/include/libssh/config_parser.h
+++ b/include/libssh/config_parser.h
@@ -30,6 +30,9 @@
extern "C" {
#endif
+#include "libssh/libssh.h"
+#include
+
char *ssh_config_get_cmd(char **str);
char *ssh_config_get_token(char **str);
@@ -49,14 +52,32 @@ int ssh_config_get_yesno(char **str, int notfound);
* be stored or NULL if we do not care about the result.
* @param[out] port Pointer to the location, where the new port will
* be stored or NULL if we do not care about the result.
+ * @param[in] ignore_port Set to true if we should not attempt to parse
+ * port number.
*
* @returns SSH_OK if the provided string is in format of SSH URI,
* SSH_ERROR on failure
*/
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port);
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port);
+
+/**
+ * @brief: Parse the ProxyJump configuration line and if parsing,
+ * stores the result in the configuration option
+ *
+ * @param[in] session The ssh session
+ * @param[in] s The string to be parsed.
+ * @param[in] do_parsing Whether to parse or not.
+ *
+ * @returns SSH_OK if the provided string is formatted and parsed correctly
+ * SSH_ERROR on failure
+ */
+int ssh_config_parse_proxy_jump(ssh_session session,
+ const char *s,
+ bool do_parsing);
#ifdef __cplusplus
}
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index 32016827..dd7fa2e8 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -45,10 +45,11 @@
#ifdef HAVE_OPENSSL_ECDH_H
#include
#endif
+#include "libssh/curve25519.h"
#include "libssh/dh.h"
#include "libssh/ecdh.h"
#include "libssh/kex.h"
-#include "libssh/curve25519.h"
+#include "libssh/sntrup761.h"
#define DIGEST_MAX_LEN 64
@@ -56,39 +57,59 @@
#define AES_GCM_IVLEN 12
enum ssh_key_exchange_e {
- /* diffie-hellman-group1-sha1 */
- SSH_KEX_DH_GROUP1_SHA1=1,
- /* diffie-hellman-group14-sha1 */
- SSH_KEX_DH_GROUP14_SHA1,
+ /* diffie-hellman-group1-sha1 */
+ SSH_KEX_DH_GROUP1_SHA1 = 1,
+ /* diffie-hellman-group14-sha1 */
+ SSH_KEX_DH_GROUP14_SHA1,
#ifdef WITH_GEX
- /* diffie-hellman-group-exchange-sha1 */
- SSH_KEX_DH_GEX_SHA1,
- /* diffie-hellman-group-exchange-sha256 */
- SSH_KEX_DH_GEX_SHA256,
+ /* diffie-hellman-group-exchange-sha1 */
+ SSH_KEX_DH_GEX_SHA1,
+ /* diffie-hellman-group-exchange-sha256 */
+ SSH_KEX_DH_GEX_SHA256,
#endif /* WITH_GEX */
- /* ecdh-sha2-nistp256 */
- SSH_KEX_ECDH_SHA2_NISTP256,
- /* ecdh-sha2-nistp384 */
- SSH_KEX_ECDH_SHA2_NISTP384,
- /* ecdh-sha2-nistp521 */
- SSH_KEX_ECDH_SHA2_NISTP521,
- /* curve25519-sha256@libssh.org */
- SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG,
- /* curve25519-sha256 */
- SSH_KEX_CURVE25519_SHA256,
- /* diffie-hellman-group16-sha512 */
- SSH_KEX_DH_GROUP16_SHA512,
- /* diffie-hellman-group18-sha512 */
- SSH_KEX_DH_GROUP18_SHA512,
- /* diffie-hellman-group14-sha256 */
- SSH_KEX_DH_GROUP14_SHA256,
+ /* ecdh-sha2-nistp256 */
+ SSH_KEX_ECDH_SHA2_NISTP256,
+ /* ecdh-sha2-nistp384 */
+ SSH_KEX_ECDH_SHA2_NISTP384,
+ /* ecdh-sha2-nistp521 */
+ SSH_KEX_ECDH_SHA2_NISTP521,
+ /* curve25519-sha256@libssh.org */
+ SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG,
+ /* curve25519-sha256 */
+ SSH_KEX_CURVE25519_SHA256,
+ /* diffie-hellman-group16-sha512 */
+ SSH_KEX_DH_GROUP16_SHA512,
+ /* diffie-hellman-group18-sha512 */
+ SSH_KEX_DH_GROUP18_SHA512,
+ /* diffie-hellman-group14-sha256 */
+ SSH_KEX_DH_GROUP14_SHA256,
+ /* sntrup761x25519-sha512@openssh.com */
+ SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
+ /* sntrup761x25519-sha512 */
+ SSH_KEX_SNTRUP761X25519_SHA512,
+ /* mlkem768x25519-sha256 */
+ SSH_KEX_MLKEM768X25519_SHA256,
+ /* mlkem768nistp256-sha256 */
+ SSH_KEX_MLKEM768NISTP256_SHA256,
+#ifdef HAVE_MLKEM1024
+ /* mlkem1024nistp384-sha384 */
+ SSH_KEX_MLKEM1024NISTP384_SHA384,
+#endif /* HAVE_MLKEM1024 */
+ /* gss-group14-sha256-* */
+ SSH_GSS_KEX_DH_GROUP14_SHA256,
+ /* gss-group16-sha512-* */
+ SSH_GSS_KEX_DH_GROUP16_SHA512,
+ /* gss-nistp256-sha256-* */
+ SSH_GSS_KEX_ECDH_NISTP256_SHA256,
+ /* gss-curve25519-sha256-* */
+ SSH_GSS_KEX_CURVE25519_SHA256,
};
enum ssh_cipher_e {
SSH_NO_CIPHER=0,
-#ifdef WITH_BLOWFISH_CIPHER
+#ifdef HAVE_BLOWFISH
SSH_BLOWFISH_CBC,
-#endif /* WITH_BLOWFISH_CIPHER */
+#endif /* HAVE_BLOWFISH */
SSH_3DES_CBC,
SSH_AES128_CBC,
SSH_AES192_CBC,
@@ -105,6 +126,9 @@ struct dh_ctx;
struct ssh_crypto_struct {
bignum shared_secret;
+ ssh_string hybrid_client_init;
+ ssh_string hybrid_server_reply;
+ ssh_string hybrid_shared_secret;
struct dh_ctx *dh_ctx;
#ifdef WITH_GEX
size_t dh_pmin; size_t dh_pn; size_t dh_pmax; /* preferred group parameters */
@@ -125,9 +149,28 @@ struct ssh_crypto_struct {
ssh_string ecdh_server_pubkey;
#endif
#ifdef HAVE_CURVE25519
+#ifdef HAVE_LIBCRYPTO
+ EVP_PKEY *curve25519_privkey;
+#elif defined(HAVE_GCRYPT_CURVE25519)
+ gcry_sexp_t curve25519_privkey;
+#else
ssh_curve25519_privkey curve25519_privkey;
+#endif
ssh_curve25519_pubkey curve25519_client_pubkey;
ssh_curve25519_pubkey curve25519_server_pubkey;
+#endif
+#ifdef HAVE_OPENSSL_MLKEM
+ EVP_PKEY *mlkem_privkey;
+#else
+ unsigned char *mlkem_privkey;
+ size_t mlkem_privkey_len;
+#endif
+ ssh_string mlkem_client_pubkey;
+ ssh_string mlkem_ciphertext;
+#ifdef HAVE_SNTRUP761
+ ssh_sntrup761_privkey sntrup761_privkey;
+ ssh_sntrup761_pubkey sntrup761_client_pubkey;
+ ssh_sntrup761_ciphertext sntrup761_ciphertext;
#endif
ssh_string dh_server_signature; /* information used by dh_handshake. */
size_t session_id_len;
@@ -223,9 +266,8 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
size_t requested_len);
int secure_memcmp(const void *s1, const void *s2, size_t n);
-#if defined(HAVE_LIBCRYPTO) && !defined(WITH_PKCS11_PROVIDER)
-ENGINE *pki_get_engine(void);
-#endif /* HAVE_LIBCRYPTO */
+
+void compress_cleanup(struct ssh_crypto_struct *crypto);
#ifdef __cplusplus
}
diff --git a/include/libssh/curve25519.h b/include/libssh/curve25519.h
index a55f52c7..e5691157 100644
--- a/include/libssh/curve25519.h
+++ b/include/libssh/curve25519.h
@@ -50,6 +50,10 @@ int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned c
typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE];
typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
+int ssh_curve25519_init(ssh_session session);
+int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k);
+int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k);
+int ssh_curve25519_build_k(ssh_session session);
int ssh_client_curve25519_init(ssh_session session);
void ssh_client_curve25519_remove_callbacks(ssh_session session);
diff --git a/include/libssh/ecdh.h b/include/libssh/ecdh.h
index 4c4c54eb..2f763528 100644
--- a/include/libssh/ecdh.h
+++ b/include/libssh/ecdh.h
@@ -48,6 +48,7 @@ extern "C" {
extern struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks;
/* Backend-specific functions. */
+int ssh_ecdh_init(ssh_session session);
int ssh_client_ecdh_init(ssh_session session);
void ssh_client_ecdh_remove_callbacks(ssh_session session);
int ecdh_build_k(ssh_session session);
diff --git a/include/libssh/ed25519.h b/include/libssh/ed25519.h
index 72a86c0b..a6bcdaf3 100644
--- a/include/libssh/ed25519.h
+++ b/include/libssh/ed25519.h
@@ -29,12 +29,41 @@
* @{
*/
+/** @internal
+ * @brief ED25519 public key.
+ * Ed25519 public key consist of 32 bytes.
+ */
#define ED25519_PK_LEN 32
+
+/** @internal
+ * @brief ED25519 secret key.
+ * Ed25519 secret key consist of 64 bytes.
+ */
#define ED25519_SK_LEN 64
+
+/** @internal
+ * @brief ED25519 signature.
+ * Ed25519 signatures consist of 64 bytes.
+ */
#define ED25519_SIG_LEN 64
+/** @internal
+ * @brief ED25519 public key.
+ * The public key consists of 32 bytes and can be used for signature
+ * verification.
+ */
typedef uint8_t ed25519_pubkey[ED25519_PK_LEN];
+
+/** @internal
+ * @brief ED25519 private key.
+ * The private key consists of 64 bytes and should be kept secret.
+ */
typedef uint8_t ed25519_privkey[ED25519_SK_LEN];
+
+/** @internal
+ * @brief ED25519 signature.
+ * Ed25519 signatures consists of 64 bytes.
+ */
typedef uint8_t ed25519_signature[ED25519_SIG_LEN];
#ifdef __cplusplus
@@ -46,7 +75,7 @@ extern "C" {
* @param[out] pk generated public key
* @param[out] sk generated secret key
* @return 0 on success, -1 on error.
- * */
+ */
int crypto_sign_ed25519_keypair(ed25519_pubkey pk, ed25519_privkey sk);
/** @internal
diff --git a/include/libssh/gssapi.h b/include/libssh/gssapi.h
index b0c74c73..fd1216f2 100644
--- a/include/libssh/gssapi.h
+++ b/include/libssh/gssapi.h
@@ -22,21 +22,53 @@
#define GSSAPI_H_
#include "config.h"
+#ifdef WITH_GSSAPI
#include "session.h"
+#include
/* all OID begin with the tag identifier + length */
#define SSH_OID_TAG 06
+#define GSSAPI_KEY_EXCHANGE_SUPPORTED "gss-group14-sha256-," \
+ "gss-group16-sha512-," \
+ "gss-nistp256-sha256-," \
+ "gss-curve25519-sha256-"
+
typedef struct ssh_gssapi_struct *ssh_gssapi;
#ifdef __cplusplus
extern "C" {
#endif
+/** current state of an GSSAPI authentication */
+enum ssh_gssapi_state_e {
+ SSH_GSSAPI_STATE_NONE, /* no status */
+ SSH_GSSAPI_STATE_RCV_TOKEN, /* Expecting a token */
+ SSH_GSSAPI_STATE_RCV_MIC, /* Expecting a MIC */
+};
+
+struct ssh_gssapi_struct{
+ enum ssh_gssapi_state_e state; /* current state */
+ gss_cred_id_t server_creds; /* credentials of server */
+ gss_cred_id_t client_creds; /* creds delegated by the client */
+ gss_ctx_id_t ctx; /* the authentication context */
+ gss_name_t client_name; /* Identity of the client */
+ char *user; /* username of client */
+ char *canonic_user; /* canonic form of the client's username */
+ struct {
+ gss_name_t server_name; /* identity of server */
+ OM_uint32 flags; /* flags used for init context */
+ gss_OID oid; /* mech being used for authentication */
+ gss_cred_id_t creds; /* creds used to initialize context */
+ gss_cred_id_t client_deleg_creds; /* delegated creds (const, not freeable) */
+ } client;
+};
+
#ifdef WITH_SERVER
int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic);
+int ssh_gssapi_server_oids(gss_OID_set *selected);
#endif /* WITH_SERVER */
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token);
@@ -44,10 +76,28 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response);
+int ssh_gssapi_init(ssh_session session);
+void ssh_gssapi_log_error(int verb, const char *msg_a, int maj_stat, int min_stat);
int ssh_gssapi_auth_mic(ssh_session session);
+void ssh_gssapi_free(ssh_session session);
+int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids);
+char *ssh_gssapi_name_to_char(gss_name_t name);
+int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host);
+OM_uint32 ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
+ gss_buffer_desc *input_token,
+ gss_buffer_desc *output_token,
+ OM_uint32 *ret_flags);
+
+char *ssh_gssapi_oid_hash(ssh_string oid);
+char *ssh_gssapi_kex_mechs(ssh_session session);
+int ssh_gssapi_check_client_config(ssh_session session);
+ssh_buffer ssh_gssapi_build_mic(ssh_session session, const char *context);
+int ssh_gssapi_auth_keyex_mic(ssh_session session,
+ gss_buffer_desc *mic_token_buf);
#ifdef __cplusplus
}
#endif
+#endif /* WITH_GSSAPI */
#endif /* GSSAPI_H */
diff --git a/include/libssh/hybrid_mlkem.h b/include/libssh/hybrid_mlkem.h
new file mode 100644
index 00000000..ca9b6a20
--- /dev/null
+++ b/include/libssh/hybrid_mlkem.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 by Red Hat, Inc.
+ *
+ * Author: Sahana Prasad
+ * Author: Pavol Žáčik
+ * Author: Claude (Anthropic)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HYBRID_MLKEM_H_
+#define HYBRID_MLKEM_H_
+
+#include "libssh/mlkem.h"
+#include "libssh/wrapper.h"
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NISTP256_SHARED_SECRET_SIZE 32
+#define NISTP384_SHARED_SECRET_SIZE 48
+
+int ssh_client_hybrid_mlkem_init(ssh_session session);
+void ssh_client_hybrid_mlkem_remove_callbacks(ssh_session session);
+
+#ifdef WITH_SERVER
+void ssh_server_hybrid_mlkem_init(ssh_session session);
+#endif /* WITH_SERVER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HYBRID_MLKEM_H_ */
diff --git a/include/libssh/kex-gss.h b/include/libssh/kex-gss.h
new file mode 100644
index 00000000..65ae2fe4
--- /dev/null
+++ b/include/libssh/kex-gss.h
@@ -0,0 +1,36 @@
+/*
+ * kex-gss.h - GSSAPI key exchange
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2024 by Gauravsingh Sisodia
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+#ifndef KEX_GSS_H_
+#define KEX_GSS_H_
+
+#include "config.h"
+#ifdef WITH_GSSAPI
+
+int ssh_client_gss_kex_init(ssh_session session);
+void ssh_server_gss_kex_init(ssh_session session);
+int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet);
+void ssh_client_gss_kex_remove_callbacks(ssh_session session);
+void ssh_client_gss_kex_remove_callback_hostkey(ssh_session session);
+
+#endif /* WITH_GSSAPI */
+#endif /* KEX_GSS_H_ */
diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 6da6693c..435ecc88 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -31,6 +31,9 @@ struct ssh_kex_struct {
char *methods[SSH_KEX_METHODS];
};
+/* crypto.h needs ssh_kex_struct so it is included below the struct definition */
+#include "libssh/crypto.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -40,6 +43,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit);
int ssh_send_kex(ssh_session session);
void ssh_list_kex(struct ssh_kex_struct *kex);
int ssh_set_client_kex(ssh_session session);
+int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex);
int ssh_kex_select_methods(ssh_session session);
int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name);
char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list);
@@ -51,10 +55,10 @@ char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list);
char **ssh_space_tokenize(const char *chain);
int ssh_get_kex1(ssh_session session);
char *ssh_find_matching(const char *in_d, const char *what_d);
-const char *ssh_kex_get_supported_method(uint32_t algo);
-const char *ssh_kex_get_default_methods(uint32_t algo);
-const char *ssh_kex_get_fips_methods(uint32_t algo);
-const char *ssh_kex_get_description(uint32_t algo);
+const char *ssh_kex_get_supported_method(enum ssh_kex_types_e type);
+const char *ssh_kex_get_default_methods(enum ssh_kex_types_e type);
+const char *ssh_kex_get_fips_methods(enum ssh_kex_types_e type);
+const char *ssh_kex_get_description(enum ssh_kex_types_e type);
char *ssh_client_select_hostkeys(ssh_session session);
int ssh_send_rekex(ssh_session session);
int server_set_kex(ssh_session session);
@@ -63,6 +67,7 @@ int ssh_make_sessionid(ssh_session session);
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
int ssh_hashbufout_add_cookie(ssh_session session);
int ssh_generate_session_keys(ssh_session session);
+bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto);
#ifdef __cplusplus
}
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 79a5fd5c..f9aea0d7 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -40,11 +40,6 @@ typedef EVP_MD_CTX* SHA384CTX;
typedef EVP_MD_CTX* SHA512CTX;
typedef EVP_MD_CTX* MD5CTX;
typedef EVP_MD_CTX* HMACCTX;
-#ifdef HAVE_ECC
-typedef EVP_MD_CTX *EVPCTX;
-#else
-typedef void *EVPCTX;
-#endif
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH
@@ -126,6 +121,15 @@ typedef BN_CTX* bignum_CTX;
ssh_string pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p);
int pki_key_ecgroup_name_to_nid(const char *group);
+
+#if defined(WITH_PKCS11_URI)
+#if defined(WITH_PKCS11_PROVIDER)
+int pki_load_pkcs11_provider(void);
+#else
+ENGINE *pki_get_engine(void);
+#endif
+#endif /* WITH_PKCS11_PROVIDER */
+
#endif /* HAVE_LIBCRYPTO */
#endif /* LIBCRYPTO_H_ */
diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h
index 966fb044..ce0beefd 100644
--- a/include/libssh/libgcrypt.h
+++ b/include/libssh/libgcrypt.h
@@ -32,7 +32,6 @@ typedef gcry_md_hd_t SHA384CTX;
typedef gcry_md_hd_t SHA512CTX;
typedef gcry_md_hd_t MD5CTX;
typedef gcry_md_hd_t HMACCTX;
-typedef gcry_md_hd_t EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define MD5_DIGEST_LEN 16
@@ -99,9 +98,9 @@ int ssh_gcry_rand_range(bignum rnd, bignum max);
#define bignum_rand_range(rnd, max) ssh_gcry_rand_range(rnd, max);
#define bignum_dup(orig, dest) do { \
if (*(dest) == NULL) { \
- *(dest) = gcry_mpi_copy(orig); \
+ *(dest) = gcry_mpi_copy((const gcry_mpi_t)orig); \
} else { \
- gcry_mpi_set(*(dest), orig); \
+ gcry_mpi_set(*(dest), (const gcry_mpi_t)orig); \
} \
} while(0)
/* Helper functions for data conversions. */
diff --git a/include/libssh/libmbedcrypto.h b/include/libssh/libmbedcrypto.h
index a4ee010b..71ebcccd 100644
--- a/include/libssh/libmbedcrypto.h
+++ b/include/libssh/libmbedcrypto.h
@@ -42,7 +42,6 @@ typedef mbedtls_md_context_t *SHA384CTX;
typedef mbedtls_md_context_t *SHA512CTX;
typedef mbedtls_md_context_t *MD5CTX;
typedef mbedtls_md_context_t *HMACCTX;
-typedef mbedtls_md_context_t *EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
@@ -130,7 +129,7 @@ int ssh_mbedcry_hex2bn(bignum *dest, char *data);
*(dest) = bignum_new(); \
} \
if (*(dest) != NULL) { \
- mbedtls_mpi_copy(orig, *(dest)); \
+ mbedtls_mpi_copy(*(dest), orig); \
} \
} while(0)
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 03c3a93f..aa7b9ef1 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -1,7 +1,7 @@
/*
* This file is part of the SSH Library
*
- * Copyright (c) 2003-2023 by Aris Adamantiadis and the libssh team
+ * Copyright (c) 2003-2026 by Aris Adamantiadis and the libssh team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -49,9 +49,10 @@
#endif
#endif
+#include
#include
+#include
#include
-#include
#ifdef _MSC_VER
typedef int mode_t;
@@ -106,6 +107,7 @@ typedef struct ssh_session_struct* ssh_session;
typedef struct ssh_string_struct* ssh_string;
typedef struct ssh_event_struct* ssh_event;
typedef struct ssh_connector_struct * ssh_connector;
+typedef struct ssh_pki_ctx_struct *ssh_pki_ctx;
typedef void* ssh_gssapi_creds;
/* Socket type */
@@ -150,13 +152,14 @@ enum ssh_auth_e {
};
/* auth flags */
-#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
-#define SSH_AUTH_METHOD_NONE 0x0001u
-#define SSH_AUTH_METHOD_PASSWORD 0x0002u
-#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
-#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
-#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
-#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
+#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
+#define SSH_AUTH_METHOD_NONE 0x0001u
+#define SSH_AUTH_METHOD_PASSWORD 0x0002u
+#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
+#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
+#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
+#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
+#define SSH_AUTH_METHOD_GSSAPI_KEYEX 0x0040u
/* messages */
enum ssh_requests_e {
@@ -295,7 +298,8 @@ enum ssh_keytypes_e{
enum ssh_keycmp_e {
SSH_KEY_CMP_PUBLIC = 0,
- SSH_KEY_CMP_PRIVATE
+ SSH_KEY_CMP_PRIVATE = 1,
+ SSH_KEY_CMP_CERTIFICATE = 2,
};
#define SSH_ADDRSTRLEN 46
@@ -368,53 +372,67 @@ enum ssh_control_master_options_e {
SSH_CONTROL_MASTER_AUTOASK
};
+enum ssh_address_family_options_e {
+ SSH_ADDRESS_FAMILY_ANY,
+ SSH_ADDRESS_FAMILY_INET,
+ SSH_ADDRESS_FAMILY_INET6
+};
+
enum ssh_options_e {
- SSH_OPTIONS_HOST,
- SSH_OPTIONS_PORT,
- SSH_OPTIONS_PORT_STR,
- SSH_OPTIONS_FD,
- SSH_OPTIONS_USER,
- SSH_OPTIONS_SSH_DIR,
- SSH_OPTIONS_IDENTITY,
- SSH_OPTIONS_ADD_IDENTITY,
- SSH_OPTIONS_KNOWNHOSTS,
- SSH_OPTIONS_TIMEOUT,
- SSH_OPTIONS_TIMEOUT_USEC,
- SSH_OPTIONS_SSH1,
- SSH_OPTIONS_SSH2,
- SSH_OPTIONS_LOG_VERBOSITY,
- SSH_OPTIONS_LOG_VERBOSITY_STR,
- SSH_OPTIONS_CIPHERS_C_S,
- SSH_OPTIONS_CIPHERS_S_C,
- SSH_OPTIONS_COMPRESSION_C_S,
- SSH_OPTIONS_COMPRESSION_S_C,
- SSH_OPTIONS_PROXYCOMMAND,
- SSH_OPTIONS_BINDADDR,
- SSH_OPTIONS_STRICTHOSTKEYCHECK,
- SSH_OPTIONS_COMPRESSION,
- SSH_OPTIONS_COMPRESSION_LEVEL,
- SSH_OPTIONS_KEY_EXCHANGE,
- SSH_OPTIONS_HOSTKEYS,
- SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
- SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
- SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
- SSH_OPTIONS_HMAC_C_S,
- SSH_OPTIONS_HMAC_S_C,
- SSH_OPTIONS_PASSWORD_AUTH,
- SSH_OPTIONS_PUBKEY_AUTH,
- SSH_OPTIONS_KBDINT_AUTH,
- SSH_OPTIONS_GSSAPI_AUTH,
- SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
- SSH_OPTIONS_NODELAY,
- SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
- SSH_OPTIONS_PROCESS_CONFIG,
- SSH_OPTIONS_REKEY_DATA,
- SSH_OPTIONS_REKEY_TIME,
- SSH_OPTIONS_RSA_MIN_SIZE,
- SSH_OPTIONS_IDENTITY_AGENT,
- SSH_OPTIONS_IDENTITIES_ONLY,
- SSH_OPTIONS_CONTROL_MASTER,
- SSH_OPTIONS_CONTROL_PATH,
+ SSH_OPTIONS_HOST,
+ SSH_OPTIONS_PORT,
+ SSH_OPTIONS_PORT_STR,
+ SSH_OPTIONS_FD,
+ SSH_OPTIONS_USER,
+ SSH_OPTIONS_SSH_DIR,
+ SSH_OPTIONS_IDENTITY,
+ SSH_OPTIONS_ADD_IDENTITY,
+ SSH_OPTIONS_KNOWNHOSTS,
+ SSH_OPTIONS_TIMEOUT,
+ SSH_OPTIONS_TIMEOUT_USEC,
+ SSH_OPTIONS_SSH1,
+ SSH_OPTIONS_SSH2,
+ SSH_OPTIONS_LOG_VERBOSITY,
+ SSH_OPTIONS_LOG_VERBOSITY_STR,
+ SSH_OPTIONS_CIPHERS_C_S,
+ SSH_OPTIONS_CIPHERS_S_C,
+ SSH_OPTIONS_COMPRESSION_C_S,
+ SSH_OPTIONS_COMPRESSION_S_C,
+ SSH_OPTIONS_PROXYCOMMAND,
+ SSH_OPTIONS_BINDADDR,
+ SSH_OPTIONS_STRICTHOSTKEYCHECK,
+ SSH_OPTIONS_COMPRESSION,
+ SSH_OPTIONS_COMPRESSION_LEVEL,
+ SSH_OPTIONS_KEY_EXCHANGE,
+ SSH_OPTIONS_HOSTKEYS,
+ SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
+ SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
+ SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
+ SSH_OPTIONS_HMAC_C_S,
+ SSH_OPTIONS_HMAC_S_C,
+ SSH_OPTIONS_PASSWORD_AUTH,
+ SSH_OPTIONS_PUBKEY_AUTH,
+ SSH_OPTIONS_KBDINT_AUTH,
+ SSH_OPTIONS_GSSAPI_AUTH,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ SSH_OPTIONS_NODELAY,
+ SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+ SSH_OPTIONS_PROCESS_CONFIG,
+ SSH_OPTIONS_REKEY_DATA,
+ SSH_OPTIONS_REKEY_TIME,
+ SSH_OPTIONS_RSA_MIN_SIZE,
+ SSH_OPTIONS_IDENTITY_AGENT,
+ SSH_OPTIONS_IDENTITIES_ONLY,
+ SSH_OPTIONS_CONTROL_MASTER,
+ SSH_OPTIONS_CONTROL_PATH,
+ SSH_OPTIONS_CERTIFICATE,
+ SSH_OPTIONS_PROXYJUMP,
+ SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
+ SSH_OPTIONS_PKI_CONTEXT,
+ SSH_OPTIONS_ADDRESS_FAMILY,
+ SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
+ SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
+ SSH_OPTIONS_NEXT_IDENTITY,
};
enum {
@@ -452,8 +470,19 @@ LIBSSH_API int ssh_blocking_flush(ssh_session session, int timeout);
LIBSSH_API ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms);
LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel,int cols,int rows);
LIBSSH_API int ssh_channel_close(ssh_channel channel);
+#define SSH_CHANNEL_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ ssh_channel_free(x); \
+ (x) = NULL; \
+ } \
+ } while (0)
LIBSSH_API void ssh_channel_free(ssh_channel channel);
-LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel);
+LIBSSH_API int ssh_channel_get_exit_state(ssh_channel channel,
+ uint32_t *pexit_code,
+ char **pexit_signal,
+ int *pcore_dumped);
+SSH_DEPRECATED LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel);
LIBSSH_API ssh_session ssh_channel_get_session(ssh_channel channel);
LIBSSH_API int ssh_channel_is_closed(ssh_channel channel);
LIBSSH_API int ssh_channel_is_eof(ssh_channel channel);
@@ -477,6 +506,8 @@ LIBSSH_API int ssh_channel_request_exec(ssh_channel channel, const char *cmd);
LIBSSH_API int ssh_channel_request_pty(ssh_channel channel);
LIBSSH_API int ssh_channel_request_pty_size(ssh_channel channel, const char *term,
int cols, int rows);
+LIBSSH_API int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *term,
+ int cols, int rows, const unsigned char* modes, size_t modes_len);
LIBSSH_API int ssh_channel_request_shell(ssh_channel channel);
LIBSSH_API int ssh_channel_request_send_signal(ssh_channel channel, const char *signum);
LIBSSH_API int ssh_channel_request_send_break(ssh_channel channel, uint32_t length);
@@ -684,6 +715,12 @@ typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len,
/** @} */
+enum ssh_file_format_e {
+ SSH_FILE_FORMAT_DEFAULT = 0,
+ SSH_FILE_FORMAT_OPENSSH,
+ SSH_FILE_FORMAT_PEM,
+};
+
LIBSSH_API ssh_key ssh_key_new(void);
#define SSH_KEY_FREE(x) \
do { if ((x) != NULL) { ssh_key_free(x); x = NULL; } } while(0)
@@ -697,9 +734,17 @@ LIBSSH_API int ssh_key_cmp(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what);
LIBSSH_API ssh_key ssh_key_dup(const ssh_key key);
+LIBSSH_API uint32_t ssh_key_get_sk_flags(const ssh_key key);
+LIBSSH_API ssh_string ssh_key_get_sk_application(const ssh_key key);
+LIBSSH_API ssh_string ssh_key_get_sk_user_id(const ssh_key key);
+
+SSH_DEPRECATED LIBSSH_API int
+ssh_pki_generate(enum ssh_keytypes_e type, int parameter, ssh_key *pkey);
+
+LIBSSH_API int ssh_pki_generate_key(enum ssh_keytypes_e type,
+ ssh_pki_ctx pki_context,
+ ssh_key *pkey);
-LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
- ssh_key *pkey);
LIBSSH_API int ssh_pki_import_privkey_base64(const char *b64_key,
const char *passphrase,
ssh_auth_callback auth_fn,
@@ -710,6 +755,13 @@ LIBSSH_API int ssh_pki_export_privkey_base64(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data,
char **b64_key);
+LIBSSH_API int
+ssh_pki_export_privkey_base64_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key,
+ enum ssh_file_format_e format);
LIBSSH_API int ssh_pki_import_privkey_file(const char *filename,
const char *passphrase,
ssh_auth_callback auth_fn,
@@ -720,6 +772,13 @@ LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data,
const char *filename);
+LIBSSH_API int
+ssh_pki_export_privkey_file_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename,
+ enum ssh_file_format_e format);
LIBSSH_API int ssh_pki_copy_cert_to_privkey(const ssh_key cert_key,
ssh_key privkey);
@@ -775,10 +834,8 @@ LIBSSH_API int ssh_userauth_try_publickey(ssh_session session,
LIBSSH_API int ssh_userauth_publickey(ssh_session session,
const char *username,
const ssh_key privkey);
-#ifndef _WIN32
LIBSSH_API int ssh_userauth_agent(ssh_session session,
const char *username);
-#endif
LIBSSH_API int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
char** value);
LIBSSH_API int ssh_userauth_publickey_auto(ssh_session session,
@@ -808,6 +865,7 @@ LIBSSH_API int ssh_string_fill(ssh_string str, const void *data, size_t len);
do { if ((x) != NULL) { ssh_string_free(x); x = NULL; } } while(0)
LIBSSH_API void ssh_string_free(ssh_string str);
LIBSSH_API ssh_string ssh_string_from_char(const char *what);
+LIBSSH_API ssh_string ssh_string_from_data(const void *data, size_t len);
LIBSSH_API size_t ssh_string_len(ssh_string str);
LIBSSH_API ssh_string ssh_string_new(size_t size);
LIBSSH_API const char *ssh_string_get_char(ssh_string str);
@@ -815,6 +873,7 @@ LIBSSH_API char *ssh_string_to_char(ssh_string str);
#define SSH_STRING_FREE_CHAR(x) \
do { if ((x) != NULL) { ssh_string_free_char(x); x = NULL; } } while(0)
LIBSSH_API void ssh_string_free_char(char *s);
+LIBSSH_API int ssh_string_cmp(ssh_string s1, ssh_string s2);
LIBSSH_API int ssh_getpass(const char *prompt, char *buf, size_t len, int echo,
int verify);
@@ -839,6 +898,7 @@ LIBSSH_API const char* ssh_get_cipher_in(ssh_session session);
LIBSSH_API const char* ssh_get_cipher_out(ssh_session session);
LIBSSH_API const char* ssh_get_hmac_in(ssh_session session);
LIBSSH_API const char* ssh_get_hmac_out(ssh_session session);
+LIBSSH_API const char *ssh_get_supported_methods(enum ssh_kex_types_e type);
LIBSSH_API ssh_buffer ssh_buffer_new(void);
LIBSSH_API void ssh_buffer_free(ssh_buffer buffer);
@@ -851,6 +911,105 @@ LIBSSH_API void *ssh_buffer_get(ssh_buffer buffer);
LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer);
LIBSSH_API int ssh_session_set_disconnect_message(ssh_session session, const char *message);
+/* SSHSIG hashes data independently from the key used, so we use a new enum
+ to avoid confusion. See
+ https://gitlab.com/jas/ietf-sshsig-format/-/blob/cc70a225cbd695d5a6f20aaebdb4b92b0818e43a/ietf-sshsig-format.md#L137
+ */
+enum sshsig_digest_e {
+ SSHSIG_DIGEST_SHA2_256 = 0,
+ SSHSIG_DIGEST_SHA2_512 = 1,
+};
+
+LIBSSH_API int sshsig_sign(const void *data,
+ size_t data_length,
+ ssh_key privkey,
+ ssh_pki_ctx pki_context,
+ const char *sig_namespace,
+ enum sshsig_digest_e hash_alg,
+ char **signature);
+LIBSSH_API int sshsig_verify(const void *data,
+ size_t data_length,
+ const char *signature,
+ const char *sig_namespace,
+ ssh_key *sign_key);
+
+/* PKI context API */
+
+enum ssh_pki_options_e {
+ SSH_PKI_OPTION_RSA_KEY_SIZE,
+
+ /* Security Key options */
+ SSH_PKI_OPTION_SK_APPLICATION,
+ SSH_PKI_OPTION_SK_FLAGS,
+ SSH_PKI_OPTION_SK_USER_ID,
+ SSH_PKI_OPTION_SK_CHALLENGE,
+ SSH_PKI_OPTION_SK_CALLBACKS,
+};
+
+/* FIDO2/U2F Operation Flags */
+
+/** Requires user presence confirmation (tap/touch) */
+#ifndef SSH_SK_USER_PRESENCE_REQD
+#define SSH_SK_USER_PRESENCE_REQD 0x01
+#endif
+
+/** Requires user verification (PIN/biometric) - FIDO2 only */
+#ifndef SSH_SK_USER_VERIFICATION_REQD
+#define SSH_SK_USER_VERIFICATION_REQD 0x04
+#endif
+
+/** Force resident key enrollment even if a resident key with given user ID
+ * already exists - FIDO2 only */
+#ifndef SSH_SK_FORCE_OPERATION
+#define SSH_SK_FORCE_OPERATION 0x10
+#endif
+
+/** Create/use resident key stored on authenticator - FIDO2 only */
+#ifndef SSH_SK_RESIDENT_KEY
+#define SSH_SK_RESIDENT_KEY 0x20
+#endif
+
+LIBSSH_API ssh_pki_ctx ssh_pki_ctx_new(void);
+
+LIBSSH_API int ssh_pki_ctx_options_set(ssh_pki_ctx context,
+ enum ssh_pki_options_e option,
+ const void *value);
+
+LIBSSH_API int ssh_pki_ctx_set_sk_pin_callback(ssh_pki_ctx context,
+ ssh_auth_callback pin_callback,
+ void *userdata);
+
+#define SSH_SK_OPTION_NAME_DEVICE_PATH "device"
+#define SSH_SK_OPTION_NAME_USER_ID "user"
+
+LIBSSH_API int ssh_pki_ctx_sk_callbacks_option_set(ssh_pki_ctx context,
+ const char *name,
+ const char *value,
+ bool required);
+
+LIBSSH_API int ssh_pki_ctx_sk_callbacks_options_clear(ssh_pki_ctx context);
+
+LIBSSH_API int
+ssh_pki_ctx_get_sk_attestation_buffer(const struct ssh_pki_ctx_struct *context,
+ ssh_buffer *attestation_buffer);
+
+LIBSSH_API void ssh_pki_ctx_free(ssh_pki_ctx context);
+
+#define SSH_PKI_CTX_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ ssh_pki_ctx_free(x); \
+ x = NULL; \
+ } \
+ } while (0)
+
+/* Security key resident keys API */
+
+LIBSSH_API int
+ssh_sk_resident_keys_load(const struct ssh_pki_ctx_struct *pki_context,
+ ssh_key **resident_keys_result,
+ size_t *num_keys_found_result);
+
#ifndef LIBSSH_LEGACY_0_4
#include "libssh/legacy.h"
#endif
diff --git a/include/libssh/libsshpp.hpp b/include/libssh/libsshpp.hpp
index 602c7aec..553a7777 100644
--- a/include/libssh/libsshpp.hpp
+++ b/include/libssh/libsshpp.hpp
@@ -498,8 +498,22 @@ class Channel {
return_throwable;
}
- int getExitStatus(){
- return ssh_channel_get_exit_status(channel);
+ /*
+ * @deprecated Please use getExitState()
+ */
+ int getExitStatus() {
+ uint32_t exit_status = (uint32_t)-1;
+ ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL);
+ return exit_status;
+ }
+ void_throwable getExitState(uint32_t & pexit_code,
+ char **pexit_signal,
+ int & pcore_dumped) {
+ ssh_throw(ssh_channel_get_exit_state(channel,
+ &pexit_code,
+ pexit_signal,
+ &pcore_dumped));
+ return_throwable;
}
Session &getSession(){
return *session;
@@ -587,9 +601,12 @@ class Channel {
ssh_throw(err);
return_throwable;
}
- void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0){
+ void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0,
+ const unsigned char* modes=NULL, size_t modes_len=0){
int err;
- if(term != NULL && cols != 0 && rows != 0)
+ if(term != NULL && cols != 0 && rows != 0 && modes != NULL)
+ err=ssh_channel_request_pty_size_modes(channel,term,cols,rows,modes,modes_len);
+ else if(term != NULL && cols != 0 && rows != 0)
err=ssh_channel_request_pty_size(channel,term,cols,rows);
else
err=ssh_channel_request_pty(channel);
diff --git a/include/libssh/messages.h b/include/libssh/messages.h
index 160306cc..9dd6b06c 100644
--- a/include/libssh/messages.h
+++ b/include/libssh/messages.h
@@ -28,6 +28,7 @@ struct ssh_auth_request {
int method;
char *password;
struct ssh_key_struct *pubkey;
+ struct ssh_key_struct *server_pubkey;
char *sigtype;
enum ssh_publickey_state_e signature_state;
char kbdint_response;
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index fe86d251..2a241276 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -33,8 +33,10 @@
# endif /* _MSC_VER */
#else
-# include
+#include
+#include
#endif /* _WIN32 */
+#include
#ifdef __cplusplus
extern "C" {
@@ -42,8 +44,9 @@ extern "C" {
/* in misc.c */
/* gets the user home dir. */
-char *ssh_get_user_home_dir(void);
+char *ssh_get_user_home_dir(ssh_session session);
char *ssh_get_local_username(void);
+char *ssh_get_local_hostname(void);
int ssh_file_readaccess_ok(const char *file);
int ssh_dir_writeable(const char *path);
@@ -65,6 +68,12 @@ struct ssh_iterator {
const void *data;
};
+struct ssh_jump_info_struct {
+ char *hostname;
+ char *username;
+ int port;
+};
+
struct ssh_timestamp {
long seconds;
long useconds;
@@ -100,13 +109,14 @@ const void *_ssh_list_pop_head(struct ssh_list *list);
#define ssh_list_pop_head(type, ssh_list)\
((type)_ssh_list_pop_head(ssh_list))
+#define SSH_LIST_FREE(x) \
+ do { if ((x) != NULL) { ssh_list_free(x); (x) = NULL; } } while(0)
+
int ssh_make_milliseconds(unsigned long sec, unsigned long usec);
void ssh_timestamp_init(struct ssh_timestamp *ts);
int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout);
int ssh_timeout_update(struct ssh_timestamp *ts, int timeout);
-int ssh_match_group(const char *group, const char *object);
-
void uint64_inc(unsigned char *counter);
void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len);
@@ -122,6 +132,14 @@ char *ssh_strreplace(const char *src, const char *pattern, const char *repl);
ssize_t ssh_readn(int fd, void *buf, size_t nbytes);
ssize_t ssh_writen(int fd, const void *buf, size_t nbytes);
+int ssh_check_hostname_syntax(const char *hostname);
+int ssh_check_username_syntax(const char *username);
+
+void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list);
+bool ssh_libssh_proxy_jumps(void);
+
+FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/mlkem.h b/include/libssh/mlkem.h
new file mode 100644
index 00000000..2b76a760
--- /dev/null
+++ b/include/libssh/mlkem.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 by Red Hat, Inc.
+ *
+ * Author: Pavol Žáčik
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef MLKEM_H_
+#define MLKEM_H_
+
+#include "libssh/crypto.h"
+#include "libssh/libssh.h"
+#include "libssh/session.h"
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mlkem_type_info {
+ size_t pubkey_size;
+ size_t ciphertext_size;
+#ifdef HAVE_GCRYPT_MLKEM
+ size_t privkey_size;
+ enum gcry_kem_algos alg;
+#elif defined(HAVE_OPENSSL_MLKEM)
+ const char *name;
+#else
+ size_t privkey_size;
+#endif
+};
+
+extern const struct mlkem_type_info MLKEM768_INFO;
+#ifdef HAVE_MLKEM1024
+extern const struct mlkem_type_info MLKEM1024_INFO;
+#endif
+
+#define MLKEM_SHARED_SECRET_SIZE 32
+
+typedef unsigned char ssh_mlkem_shared_secret[MLKEM_SHARED_SECRET_SIZE];
+
+const struct mlkem_type_info *
+kex_type_to_mlkem_info(enum ssh_key_exchange_e kex_type);
+
+int ssh_mlkem_init(ssh_session session);
+
+int ssh_mlkem_encapsulate(ssh_session session,
+ ssh_mlkem_shared_secret shared_secret);
+
+int ssh_mlkem_decapsulate(const ssh_session session,
+ ssh_mlkem_shared_secret shared_secret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MLKEM_H_ */
diff --git a/include/libssh/mlkem_native.h b/include/libssh/mlkem_native.h
new file mode 100644
index 00000000..d5fd8334
--- /dev/null
+++ b/include/libssh/mlkem_native.h
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 by Red Hat, Inc.
+ *
+ * Author: Jakub Jelen
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef MLKEM_NATIVE_H_
+#define MLKEM_NATIVE_H_
+
+#include
+#include
+#include
+#include
+#include
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+A monomorphic instance of libcrux_ml_kem.types.MlKemPrivateKey
+with const generics
+- $2400size_t
+*/
+typedef struct libcrux_ml_kem_types_MlKemPrivateKey_d9_s {
+ uint8_t value[2400U];
+} libcrux_ml_kem_types_MlKemPrivateKey_d9;
+
+/**
+A monomorphic instance of libcrux_ml_kem.types.MlKemPublicKey
+with const generics
+- $1184size_t
+*/
+typedef struct libcrux_ml_kem_types_MlKemPublicKey_30_s {
+ uint8_t value[1184U];
+} libcrux_ml_kem_types_MlKemPublicKey_30;
+
+typedef struct libcrux_ml_kem_mlkem768_MlKem768KeyPair_s {
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 sk;
+ libcrux_ml_kem_types_MlKemPublicKey_30 pk;
+} libcrux_ml_kem_mlkem768_MlKem768KeyPair;
+
+typedef struct libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s {
+ uint8_t value[1088U];
+} libcrux_ml_kem_mlkem768_MlKem768Ciphertext;
+
+/**
+A monomorphic instance of K.
+with types libcrux_ml_kem_types_MlKemCiphertext[[$1088size_t]],
+uint8_t[32size_t]
+
+*/
+typedef struct tuple_c2_s {
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext fst;
+ uint8_t snd[32U];
+} tuple_c2;
+
+/**
+ Generate ML-KEM 768 Key Pair
+*/
+libcrux_ml_kem_mlkem768_MlKem768KeyPair
+libcrux_ml_kem_mlkem768_portable_generate_key_pair(uint8_t randomness[64U]);
+
+/**
+ Validate a public key.
+
+ Returns `true` if valid, and `false` otherwise.
+*/
+bool libcrux_ml_kem_mlkem768_portable_validate_public_key(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
+
+/**
+ Encapsulate ML-KEM 768
+
+ Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
+ The input is a reference to an [`MlKem768PublicKey`] and [`SHARED_SECRET_SIZE`]
+ bytes of `randomness`.
+*/
+tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
+ uint8_t randomness[32U]);
+
+/**
+ Decapsulate ML-KEM 768
+
+ Generates an [`MlKemSharedSecret`].
+ The input is a reference to an [`MlKem768PrivateKey`] and an
+ [`MlKem768Ciphertext`].
+*/
+void libcrux_ml_kem_mlkem768_portable_decapsulate(
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
+ uint8_t ret[32U]);
+
+/* rename some types to be a bit more ergonomic */
+#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s
+#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_30_s
+#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s
+#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s
+#define libcrux_mlkem768_enc_result tuple_c2_s
+/* defines for PRNG inputs */
+#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN 64U
+#define LIBCRUX_ML_KEM_ENC_PRNG_LEN 32
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MLKEM_NATIVE_H_ */
diff --git a/include/libssh/options.h b/include/libssh/options.h
index 9050d3be..63b207fa 100644
--- a/include/libssh/options.h
+++ b/include/libssh/options.h
@@ -25,6 +25,7 @@
extern "C" {
#endif
+int ssh_config_parse(ssh_session session, FILE *fp, bool global);
int ssh_config_parse_file(ssh_session session, const char *filename);
int ssh_config_parse_string(ssh_session session, const char *input);
int ssh_options_set_algo(ssh_session session,
@@ -33,6 +34,8 @@ int ssh_options_set_algo(ssh_session session,
char **place);
int ssh_options_apply(ssh_session session);
+char *ssh_options_get_algo(ssh_session session, enum ssh_kex_types_e algo);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index 7f10a709..531d7e4b 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -58,6 +58,7 @@ extern "C" {
SSH_PACKET_CALLBACK(ssh_packet_unimplemented);
SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback);
SSH_PACKET_CALLBACK(ssh_packet_ignore_callback);
+SSH_PACKET_CALLBACK(ssh_packet_debug_callback);
SSH_PACKET_CALLBACK(ssh_packet_dh_reply);
SSH_PACKET_CALLBACK(ssh_packet_newkeys);
SSH_PACKET_CALLBACK(ssh_packet_service_accept);
@@ -67,6 +68,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info);
SSH_PACKET_CALLBACK(ssh_packet_kexdh_init);
#endif
+int ssh_packet_send_newkeys(ssh_session session);
int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum);
int ssh_packet_parse_type(ssh_session session);
//int packet_flush(ssh_session session, int enforce_blocking);
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 096a645f..e22c05f8 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -46,11 +46,24 @@
#define MAX_PUBKEY_SIZE 0x100000 /* 1M */
#define MAX_PRIVKEY_SIZE 0x400000 /* 4M */
+#define RSA_MIN_KEY_SIZE 1024
+#define RSA_MIN_FIPS_KEY_SIZE 2048
+#define RSA_DEFAULT_KEY_SIZE 3072
+
#define SSH_KEY_FLAG_EMPTY 0x0
#define SSH_KEY_FLAG_PUBLIC 0x0001
#define SSH_KEY_FLAG_PRIVATE 0x0002
#define SSH_KEY_FLAG_PKCS11_URI 0x0004
+/* Constants matching the Lightweight Secure Shell Signature Format */
+/* https://datatracker.ietf.org/doc/draft-josefsson-sshsig-format */
+#define SSHSIG_VERSION 0x01
+#define SSHSIG_MAGIC_PREAMBLE "SSHSIG"
+#define SSHSIG_MAGIC_PREAMBLE_LEN (sizeof(SSHSIG_MAGIC_PREAMBLE) - 1)
+#define SSHSIG_BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----"
+#define SSHSIG_END_SIGNATURE "-----END SSH SIGNATURE-----"
+#define SSHSIG_LINE_LENGTH 76
+
struct ssh_key_struct {
enum ssh_keytypes_e type;
int flags;
@@ -60,22 +73,31 @@ struct ssh_key_struct {
gcry_sexp_t rsa;
gcry_sexp_t ecdsa;
#elif defined(HAVE_LIBMBEDCRYPTO)
- mbedtls_pk_context *rsa;
+ mbedtls_pk_context *pk;
mbedtls_ecdsa_context *ecdsa;
#elif defined(HAVE_LIBCRYPTO)
- /* This holds either ENGINE key for PKCS#11 support or just key in
- * high-level format */
+ /* This holds either ENGINE/PROVIDER key for PKCS#11 support
+ * or just key in high-level format */
EVP_PKEY *key;
+ /* keep this around for FIPS mode so we can parse the public keys. We won't
+ * be able to use them nor use the private keys though */
uint8_t *ed25519_pubkey;
- uint8_t *ed25519_privkey;
#endif /* HAVE_LIBGCRYPT */
#ifndef HAVE_LIBCRYPTO
ed25519_pubkey *ed25519_pubkey;
ed25519_privkey *ed25519_privkey;
#endif /* HAVE_LIBCRYPTO */
ssh_string sk_application;
- void *cert;
+ ssh_buffer cert;
enum ssh_keytypes_e cert_type;
+
+ /* Security Key specific private data */
+ uint8_t sk_flags;
+ ssh_string sk_key_handle;
+ ssh_string sk_reserved;
+
+ /* Resident key specific metadata */
+ ssh_string sk_user_id;
};
struct ssh_signature_struct {
@@ -127,6 +149,11 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name);
((kt) >= SSH_KEYTYPE_ECDSA_P256_CERT01 &&\
(kt) <= SSH_KEYTYPE_ED25519_CERT01))
+#define is_sk_key_type(kt) \
+ ((kt) == SSH_KEYTYPE_SK_ECDSA || (kt) == SSH_KEYTYPE_SK_ED25519 || \
+ (kt) == SSH_KEYTYPE_SK_ECDSA_CERT01 || \
+ (kt) == SSH_KEYTYPE_SK_ED25519_CERT01)
+
/* SSH Signature Functions */
ssh_signature ssh_signature_new(void);
void ssh_signature_free(ssh_signature sign);
@@ -153,6 +180,10 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
int ssh_pki_import_cert_blob(const ssh_string cert_blob,
ssh_key *pkey);
+/* SSH Private Key Functions */
+int ssh_pki_export_privkey_blob(const ssh_key key,
+ ssh_string *pblob);
+
/* SSH Signing Functions */
ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf,
diff --git a/include/libssh/pki_context.h b/include/libssh/pki_context.h
new file mode 100644
index 00000000..6fb48bcf
--- /dev/null
+++ b/include/libssh/pki_context.h
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef PKI_CONTEXT_H
+#define PKI_CONTEXT_H
+
+#include "libssh/callbacks.h"
+#include "libssh/libssh.h"
+
+/**
+ * @brief Security key context structure
+ *
+ * Context structure containing all parameters and callbacks
+ * needed for FIDO2/U2F security key operations.
+ */
+struct ssh_pki_ctx_struct {
+ /** @brief Desired RSA modulus size in bits
+ *
+ * Specified size of RSA keys to generate. If set to 0, defaults to 3072
+ * bits. Must be greater than or equal to 1024, as anything below is
+ * considered insecure.
+ */
+ int rsa_key_size;
+
+ /** @brief Security key callbacks
+ *
+ * Provides enroll/sign/load_resident_keys operations.
+ */
+ const struct ssh_sk_callbacks_struct *sk_callbacks;
+
+ /** @brief Application identifier string for the security key credential
+ *
+ * FIDO2 relying party identifier, typically "ssh:user@hostname" format.
+ * This is required for all security key operations.
+ */
+ char *sk_application;
+
+ /** @brief FIDO2 operation flags
+ *
+ * Bitfield controlling authenticator behavior. Combine with bitwise OR:
+ * - SSH_SK_USER_PRESENCE_REQD (0x01): Require user touch
+ * - SSH_SK_USER_VERIFICATION_REQD (0x04): Require PIN/biometric
+ * - SSH_SK_FORCE_OPERATION (0x10): Override duplicate detection
+ * - SSH_SK_RESIDENT_KEY (0x20): Create discoverable credential
+ */
+ uint8_t sk_flags;
+
+ /** @brief PIN callback for authenticator user verification (optional)
+ *
+ * Callback invoked to obtain a PIN or perform user verification when
+ * SSH_SK_USER_VERIFICATION_REQD is set or the authenticator requires it.
+ * If NULL, no interactive PIN retrieval is performed.
+ */
+ ssh_auth_callback sk_pin_callback;
+
+ /** @brief User supplied pointer passed to callbacks (optional)
+ *
+ * Generic pointer set by the application and forwarded to
+ * interactive callbacks (e.g. PIN callback) to allow applications to
+ * carry state context.
+ */
+ void *sk_userdata;
+
+ /** @brief Custom challenge data for enrollment (optional)
+ *
+ * Buffer containing challenge data signed by the authenticator.
+ * If NULL, a random 32-byte challenge is automatically generated.
+ */
+ ssh_buffer sk_challenge_buffer;
+
+ /** @brief Options to be passed to the sk_callbacks (optional)
+ *
+ * NULL-terminated array of sk_option pointers owned by this context.
+ */
+ struct sk_option **sk_callbacks_options;
+
+ /** @brief The buffer used to store attestation information returned in a
+ * key enrollment operation
+ */
+ ssh_buffer sk_attestation_buffer;
+};
+
+/* Internal PKI context functions */
+ssh_pki_ctx ssh_pki_ctx_dup(const ssh_pki_ctx context);
+
+#endif /* PKI_CONTEXT_H */
diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index c63e129d..2a6f8966 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -61,6 +61,7 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type);
/* SSH Key Functions */
+ssh_key pki_key_dup_common_init(const ssh_key key, int demote);
ssh_key pki_key_dup(const ssh_key key, int demote);
int pki_key_generate_rsa(ssh_key key, int parameter);
int pki_key_generate_ecdsa(ssh_key key, int parameter);
@@ -92,7 +93,7 @@ int pki_pubkey_build_rsa(ssh_key key,
ssh_string e,
ssh_string n);
int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e);
-ssh_string pki_publickey_to_blob(const ssh_key key);
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type);
/* SSH Private Key Functions */
int pki_privkey_build_rsa(ssh_key key,
@@ -106,7 +107,6 @@ int pki_privkey_build_ecdsa(ssh_key key,
int nid,
ssh_string e,
ssh_string exp);
-ssh_string pki_publickey_to_blob(const ssh_key key);
/* SSH Signature Functions */
ssh_signature pki_sign_data(const ssh_key privkey,
@@ -143,11 +143,13 @@ int pki_ed25519_key_cmp(const ssh_key k1,
enum ssh_keycmp_e what);
int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key);
int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key);
+int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey);
ssh_string pki_ed25519_signature_to_blob(ssh_signature sig);
int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob);
int pki_privkey_build_ed25519(ssh_key key,
ssh_string pubkey,
ssh_string privkey);
+int pki_pubkey_build_ed25519(ssh_key key, ssh_string pubkey);
/* PKI Container OpenSSH */
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key);
@@ -162,6 +164,16 @@ int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type);
#endif /* WITH_PKCS11_URI */
bool ssh_key_size_allowed_rsa(int min_size, ssh_key key);
+
+/* Security Key Helper Functions */
+int pki_buffer_pack_sk_priv_data(ssh_buffer buffer, const ssh_key key);
+int pki_buffer_unpack_sk_priv_data(ssh_buffer buffer, ssh_key key);
+int pki_sk_signature_buffer_prepare(const ssh_key key,
+ const ssh_signature sig,
+ const unsigned char *input,
+ size_t input_len,
+ ssh_buffer *sk_buffer_out);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/pki_sk.h b/include/libssh/pki_sk.h
new file mode 100644
index 00000000..f6c33300
--- /dev/null
+++ b/include/libssh/pki_sk.h
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef PKI_SK_H
+#define PKI_SK_H
+
+#include "libssh/libssh.h"
+#include "libssh/pki.h"
+
+#include
+
+#define SSH_SK_MAX_USER_ID_LEN 64
+
+/**
+ * @brief Enroll a new security key using a U2F/FIDO2 authenticator
+ *
+ * Creates a new security key credential configured according to the parameters
+ * in the PKI context. This function handles key enrollment for both ECDSA and
+ * Ed25519 algorithms, generates appropriate challenges, and returns the
+ * enrolled key with optional attestation data.
+ *
+ * The PKI context must be configured with appropriate security key parameters
+ * using ssh_pki_ctx_options_set() before calling this function. Required
+ * options include SSH_PKI_OPTION_SK_APPLICATION, SSH_PKI_OPTION_SK_USER_ID, and
+ * SSH_PKI_OPTION_SK_CALLBACKS.
+ *
+ * @param[in] context The PKI context containing security key configuration and
+ * parameters
+ * @param[in] key_type The type of key to enroll (SSH_KEYTYPE_SK_ECDSA or
+ * SSH_KEYTYPE_SK_ED25519)
+ * @param[out] enrolled_key_result Pointer to store the enrolled ssh_key
+ *
+ * @return SSH_OK on success, SSH_ERROR on failure
+ *
+ * @see ssh_pki_ctx_new()
+ * @see ssh_pki_ctx_options_set()
+ * @see ssh_pki_ctx_get_sk_attestation_buffer()
+ */
+int pki_sk_enroll_key(ssh_pki_ctx context,
+ enum ssh_keytypes_e key_type,
+ ssh_key *enrolled_key_result);
+
+/**
+ * @brief Sign arbitrary data using a security key and a PKI context
+ *
+ * This function performs signing operations configured according to the
+ * parameters in the PKI context and returns a properly formatted
+ * ssh_signature. The caller must free the signature when it is no longer
+ * needed.
+ *
+ * The PKI context should be configured with appropriate security key parameters
+ * using ssh_pki_ctx_options_set() before calling this function. The security
+ * key must have been previously enrolled or loaded.
+ *
+ * @param[in] context The PKI context containing security key configuration and
+ * parameters
+ * @param[in] key The security key to use for signing
+ * @param[in] data The data to sign
+ * @param[in] data_len Length of data to sign
+ *
+ * @return A valid ssh_signature on success, NULL on failure
+ *
+ * @see ssh_pki_ctx_new()
+ * @see ssh_pki_ctx_options_set()
+ * @see pki_sk_enroll_key()
+ * @see ssh_signature_free()
+ */
+ssh_signature pki_sk_do_sign(ssh_pki_ctx context,
+ const ssh_key key,
+ const uint8_t *data,
+ size_t data_len);
+
+#endif /* PKI_SK_H */
diff --git a/include/libssh/poll.h b/include/libssh/poll.h
index 8e30676e..478764b6 100644
--- a/include/libssh/poll.h
+++ b/include/libssh/poll.h
@@ -157,6 +157,7 @@ void ssh_poll_ctx_free(ssh_poll_ctx ctx);
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p);
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s);
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p);
+bool ssh_poll_is_locked(ssh_poll_handle p);
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout);
ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session);
int ssh_event_add_poll(ssh_event event, ssh_poll_handle p);
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index 4ec3b2a7..56b84c5d 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -30,10 +30,11 @@
#define _LIBSSH_PRIV_H
#include
+#include
#include
#include
#include
-#include
+#include
#if !defined(HAVE_STRTOULL)
# if defined(HAVE___STRTOULL)
@@ -47,6 +48,10 @@
# endif
#endif /* !defined(HAVE_STRTOULL) */
+#ifdef HAVE_TERMIOS_H
+#include
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -160,6 +165,9 @@ int ssh_gettimeofday(struct timeval *__p, void *__t);
#define gettimeofday ssh_gettimeofday
+struct tm *ssh_localtime(const time_t *timer, struct tm *result);
+# define localtime_r ssh_localtime
+
#define _XCLOSESOCKET closesocket
# ifdef HAVE_IO_H
@@ -270,6 +278,10 @@ void ssh_log_common(struct ssh_common_struct *common,
const char *function,
const char *format, ...) PRINTF_ATTRIBUTE(4, 5);
+void _ssh_remove_legacy_log_cb(void);
+
+/* log.c */
+void _ssh_reset_log_cb(void);
/* ERROR HANDLING */
@@ -304,6 +316,7 @@ int ssh_auth_reply_success(ssh_session session, int partial);
/* client.c */
int ssh_send_banner(ssh_session session, int is_server);
+void ssh_session_socket_close(ssh_session session);
/* connect.c */
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
@@ -320,7 +333,13 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen);
/* match.c */
int match_pattern_list(const char *string, const char *pattern,
size_t len, int dolower);
-int match_hostname(const char *host, const char *pattern, unsigned int len);
+int match_hostname(const char *host, const char *pattern, size_t len);
+#ifndef _WIN32
+int match_cidr_address_list(const char *address,
+ const char *addrlist,
+ int sa_family);
+#endif
+int match_group(const char *group, const char *object);
/* connector.c */
int ssh_connector_set_event(ssh_connector connector, ssh_event event);
@@ -338,17 +357,48 @@ int ssh_connector_remove_event(ssh_connector connector);
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
/** Zero a structure */
-#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#define ZERO_STRUCT(x) memset(&(x), 0, sizeof(x))
/** Zero a structure given a pointer to the structure */
-#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((x), 0, sizeof(*(x))); } while(0)
/** Get the size of an array */
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-#ifndef HAVE_EXPLICIT_BZERO
-void explicit_bzero(void *s, size_t n);
-#endif /* !HAVE_EXPLICIT_BZERO */
+/** Securely zero memory in a way that won't be optimized away */
+#if defined(HAVE_MEMSET_EXPLICIT)
+#define ssh_burn(ptr, len) memset_explicit((ptr), '\0', (len))
+#elif defined(HAVE_EXPLICIT_BZERO)
+#define ssh_burn(ptr, len) explicit_bzero((ptr), (len))
+#elif defined(HAVE_MEMSET_S)
+#define ssh_burn(ptr, len) memset_s((ptr), (len), '\0', (len))
+#elif defined(HAVE_SECURE_ZERO_MEMORY)
+#define ssh_burn(ptr, len) SecureZeroMemory((ptr), (len))
+#else
+#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION)
+#define ssh_burn(ptr, len) \
+ do { \
+ memset((ptr), '\0', (len)); \
+ __asm__ volatile("" : : "g"(ptr) : "memory"); \
+ } while (0)
+#else
+#define ssh_burn(ptr, len) \
+ do { \
+ memset((ptr), '\0', (len)); \
+ } while (0)
+#endif
+#endif
+
+void burn_free(void *ptr, size_t len);
+
+/** Free memory space after zeroing it */
+#define BURN_FREE(x, len) \
+ do { \
+ if ((x) != NULL) { \
+ burn_free((x), (len)); \
+ (x) = NULL; \
+ } \
+ } while (0)
/**
* This is a hack to fix warnings. The idea is to use this everywhere that we
@@ -369,6 +419,7 @@ void explicit_bzero(void *s, size_t n);
*/
#define discard_const_p(type, ptr) ((type *)discard_const(ptr))
+#ifndef __VA_NARG__
/**
* Get the argument count of variadic arguments
*/
@@ -400,6 +451,7 @@ void explicit_bzero(void *s, size_t n);
29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+#endif
#define CLOSE_SOCKET(s) do { if ((s) != SSH_INVALID_SOCKET) { _XCLOSESOCKET(s); (s) = SSH_INVALID_SOCKET;} } while(0)
@@ -452,6 +504,13 @@ bool is_ssh_initialized(void);
#define SSH_ERRNO_MSG_MAX 1024
char *ssh_strerror(int err_num, char *buf, size_t buflen);
+/** 55 defined options (5 bytes each) + terminator */
+#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
+int encode_current_tty_opts(unsigned char *buf, size_t buflen);
+
+/** The default maximum file size for a configuration file */
+#define SSH_MAX_CONFIG_FILE_SIZE 16 * 1024 * 1024
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/server.h b/include/libssh/server.h
index 4033dac7..ee800567 100644
--- a/include/libssh/server.h
+++ b/include/libssh/server.h
@@ -36,28 +36,31 @@ extern "C" {
#endif
enum ssh_bind_options_e {
- SSH_BIND_OPTIONS_BINDADDR,
- SSH_BIND_OPTIONS_BINDPORT,
- SSH_BIND_OPTIONS_BINDPORT_STR,
- SSH_BIND_OPTIONS_HOSTKEY,
- SSH_BIND_OPTIONS_DSAKEY, /* deprecated */
- SSH_BIND_OPTIONS_RSAKEY, /* deprecated */
- SSH_BIND_OPTIONS_BANNER,
- SSH_BIND_OPTIONS_LOG_VERBOSITY,
- SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
- SSH_BIND_OPTIONS_ECDSAKEY, /* deprecated */
- SSH_BIND_OPTIONS_IMPORT_KEY,
- SSH_BIND_OPTIONS_KEY_EXCHANGE,
- SSH_BIND_OPTIONS_CIPHERS_C_S,
- SSH_BIND_OPTIONS_CIPHERS_S_C,
- SSH_BIND_OPTIONS_HMAC_C_S,
- SSH_BIND_OPTIONS_HMAC_S_C,
- SSH_BIND_OPTIONS_CONFIG_DIR,
- SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES,
- SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS,
- SSH_BIND_OPTIONS_PROCESS_CONFIG,
- SSH_BIND_OPTIONS_MODULI,
- SSH_BIND_OPTIONS_RSA_MIN_SIZE,
+ SSH_BIND_OPTIONS_BINDADDR,
+ SSH_BIND_OPTIONS_BINDPORT,
+ SSH_BIND_OPTIONS_BINDPORT_STR,
+ SSH_BIND_OPTIONS_HOSTKEY,
+ SSH_BIND_OPTIONS_DSAKEY, /* deprecated */
+ SSH_BIND_OPTIONS_RSAKEY, /* deprecated */
+ SSH_BIND_OPTIONS_BANNER,
+ SSH_BIND_OPTIONS_LOG_VERBOSITY,
+ SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
+ SSH_BIND_OPTIONS_ECDSAKEY, /* deprecated */
+ SSH_BIND_OPTIONS_IMPORT_KEY,
+ SSH_BIND_OPTIONS_KEY_EXCHANGE,
+ SSH_BIND_OPTIONS_CIPHERS_C_S,
+ SSH_BIND_OPTIONS_CIPHERS_S_C,
+ SSH_BIND_OPTIONS_HMAC_C_S,
+ SSH_BIND_OPTIONS_HMAC_S_C,
+ SSH_BIND_OPTIONS_CONFIG_DIR,
+ SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES,
+ SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS,
+ SSH_BIND_OPTIONS_PROCESS_CONFIG,
+ SSH_BIND_OPTIONS_MODULI,
+ SSH_BIND_OPTIONS_RSA_MIN_SIZE,
+ SSH_BIND_OPTIONS_IMPORT_KEY_STR,
+ SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
+ SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
};
typedef struct ssh_bind_struct* ssh_bind;
@@ -117,7 +120,7 @@ LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);
*
* @param[in] userdata A pointer to private data to pass to the callbacks.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @code
* struct ssh_callbacks_struct cb = {
@@ -171,7 +174,7 @@ LIBSSH_API void ssh_bind_fd_toaccept(ssh_bind ssh_bind_o);
* @param ssh_bind_o The ssh server bind to accept a connection.
* @param session A preallocated ssh session
* @see ssh_new
- * @return SSH_OK when a connection is established
+ * @return `SSH_OK` when a connection is established
*/
LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
@@ -185,7 +188,7 @@ LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
* inbound connection
* @see ssh_new
* @see ssh_bind_accept
- * @return SSH_OK when a connection is established
+ * @return `SSH_OK` when a connection is established
*/
LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session,
socket_t fd);
@@ -197,7 +200,7 @@ LIBSSH_API ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session);
*
* @param session A connected ssh session
* @see ssh_bind_accept
- * @return SSH_OK if the key exchange was successful
+ * @return `SSH_OK` if the key exchange was successful
*/
LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
@@ -214,14 +217,16 @@ LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
* @see ssh_handle_key_exchange
* @see ssh_options_set
*
- * @return SSH_OK if initialization succeeds.
+ * @return `SSH_OK` if initialization succeeds.
*/
-
LIBSSH_API int ssh_server_init_kex(ssh_session session);
/**
* @brief Free a ssh servers bind.
*
+ * Note that this will also free options that have been set on the bind,
+ * including keys set with SSH_BIND_OPTIONS_IMPORT_KEY.
+ *
* @param ssh_bind_o The ssh server bind to free.
*/
LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
@@ -242,6 +247,7 @@ LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
* SSH_AUTH_METHOD_HOSTBASED
* SSH_AUTH_METHOD_INTERACTIVE
* SSH_AUTH_METHOD_GSSAPI_MIC
+ * SSH_AUTH_METHOD_GSSAPI_KEYEX
*/
LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
@@ -253,7 +259,7 @@ LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
*
* @param[in] banner The server's banner.
*
- * @return SSH_OK on success, SSH_ERROR on error.
+ * @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_send_issue_banner(ssh_session session, const ssh_string banner);
@@ -292,7 +298,7 @@ LIBSSH_API const char *ssh_message_auth_user(ssh_message msg);
*
* @param[in] msg The message to get the password from.
*
- * @return The username or NULL if an error occurred.
+ * @return The password or NULL if an error occurred.
*
* @see ssh_message_get()
* @see ssh_message_type()
diff --git a/include/libssh/session.h b/include/libssh/session.h
index b3b3e4e6..da39df2a 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -58,16 +58,17 @@ enum ssh_dh_state_e {
};
enum ssh_pending_call_e {
- SSH_PENDING_CALL_NONE = 0,
- SSH_PENDING_CALL_CONNECT,
- SSH_PENDING_CALL_AUTH_NONE,
- SSH_PENDING_CALL_AUTH_PASSWORD,
- SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
- SSH_PENDING_CALL_AUTH_PUBKEY,
- SSH_PENDING_CALL_AUTH_AGENT,
- SSH_PENDING_CALL_AUTH_KBDINT_INIT,
- SSH_PENDING_CALL_AUTH_KBDINT_SEND,
- SSH_PENDING_CALL_AUTH_GSSAPI_MIC
+ SSH_PENDING_CALL_NONE = 0,
+ SSH_PENDING_CALL_CONNECT,
+ SSH_PENDING_CALL_AUTH_NONE,
+ SSH_PENDING_CALL_AUTH_PASSWORD,
+ SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
+ SSH_PENDING_CALL_AUTH_PUBKEY,
+ SSH_PENDING_CALL_AUTH_AGENT,
+ SSH_PENDING_CALL_AUTH_KBDINT_INIT,
+ SSH_PENDING_CALL_AUTH_KBDINT_SEND,
+ SSH_PENDING_CALL_AUTH_GSSAPI_MIC,
+ SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX,
};
/* libssh calls may block an undefined amount of time */
@@ -84,6 +85,15 @@ enum ssh_pending_call_e {
* sending it twice during key exchange to simplify the state machine. */
#define SSH_SESSION_FLAG_KEXINIT_SENT 0x0008
+/* The current SSH2 session implements the "strict KEX" feature and should behave
+ * differently on SSH2_MSG_NEWKEYS. */
+#define SSH_SESSION_FLAG_KEX_STRICT 0x0010
+/* Unexpected packets have been sent while the session was still unencrypted */
+#define SSH_SESSION_FLAG_KEX_TAINTED 0x0020
+/* The scp on server can not handle quoted paths. Skip the mitigation for
+ * CVE-2019-14889 when using scp */
+#define SSH_SESSION_FLAG_SCP_QUOTING_BROKEN 0x0040
+
/* codes to use with ssh_handle_packets*() */
/* Infinite timeout */
#define SSH_TIMEOUT_INFINITE -1
@@ -114,6 +124,8 @@ enum ssh_pending_call_e {
/* server-sig-algs extension */
#define SSH_EXT_SIG_RSA_SHA256 0x02
#define SSH_EXT_SIG_RSA_SHA512 0x04
+/* Host-bound public key authentication extension */
+#define SSH_EXT_PUBLICKEY_HOSTBOUND 0x08
/* members that are common to ssh_session and ssh_bind */
struct ssh_common_struct {
@@ -134,6 +146,7 @@ struct ssh_session_struct {
uint32_t send_seq;
uint32_t recv_seq;
struct ssh_timestamp last_rekey_time;
+ bool proxy_root;
int connected;
/* !=0 when the user got a session handle */
@@ -189,6 +202,8 @@ struct ssh_session_struct {
*/
bool first_kex_follows_guess_wrong;
+ ssh_string gssapi_key_exchange_mic;
+
ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf;
struct ssh_crypto_struct *current_crypto;
@@ -231,17 +246,22 @@ struct ssh_session_struct {
struct {
struct ssh_list *identity;
struct ssh_list *identity_non_exp;
+ struct ssh_iterator *identity_it;
+ struct ssh_list *certificate;
+ struct ssh_list *certificate_non_exp;
+ struct ssh_list *proxy_jumps;
+ struct ssh_list *proxy_jumps_user_cb;
+ char *proxy_jumps_str;
char *username;
char *host;
char *bindaddr; /* bind the client to an ip addr */
+ char *homedir;
char *sshdir;
char *knownhosts;
char *global_knownhosts;
char *wanted_methods[SSH_KEX_METHODS];
char *pubkey_accepted_types;
char *ProxyCommand;
- char *custombanner;
- char *moduli_file;
char *agent_socket;
unsigned long timeout; /* seconds */
unsigned long timeout_usec;
@@ -251,6 +271,8 @@ struct ssh_session_struct {
char compressionlevel;
char *gss_server_identity;
char *gss_client_identity;
+ bool gssapi_key_exchange;
+ char *gssapi_key_exchange_algs;
int gss_delegate_creds;
int flags;
int exp_flags;
@@ -263,10 +285,22 @@ struct ssh_session_struct {
bool identities_only;
int control_master;
char *control_path;
+ int address_family;
} opts;
+
+ /* server options */
+ struct {
+ char *custombanner;
+ char *moduli_file;
+ } server_opts;
+
/* counters */
ssh_counter socket_counter;
ssh_counter raw_counter;
+
+ /* PKI context structure containing various parameters to configure PKI
+ * operations */
+ struct ssh_pki_ctx_struct *pki_context;
};
/** @internal
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index e210a1be..b7d6ad94 100644
--- a/include/libssh/sftp.h
+++ b/include/libssh/sftp.h
@@ -74,9 +74,27 @@ typedef struct sftp_file_struct* sftp_file;
typedef struct sftp_message_struct* sftp_message;
typedef struct sftp_packet_struct* sftp_packet;
typedef struct sftp_request_queue_struct* sftp_request_queue;
+
+/**
+ * @brief SFTP session handle.
+ *
+ * This type represents an active SFTP session associated with an SSH channel.
+ * It is created and destroyed via the libssh SFTP API and is internally
+ * managed by libssh. It is used by applications to perform SFTP operations
+ * such as file access and directory management.
+ *
+ * The internal structure of this type is opaque and must not be accessed
+ * directly by applications.
+ *
+ * @see sftp_new
+ * @see sftp_free
+ */
typedef struct sftp_session_struct* sftp_session;
typedef struct sftp_status_message_struct* sftp_status_message;
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
+typedef struct sftp_limits_struct* sftp_limits_t;
+typedef struct sftp_aio_struct* sftp_aio;
+typedef struct sftp_name_id_map_struct *sftp_name_id_map;
struct sftp_session_struct {
ssh_session session;
@@ -90,6 +108,7 @@ struct sftp_session_struct {
void **handles;
sftp_ext ext;
sftp_packet read_packet;
+ sftp_limits_t limits;
};
struct sftp_packet_struct {
@@ -200,6 +219,32 @@ struct sftp_statvfs_struct {
uint64_t f_namemax; /** maximum filename length */
};
+/**
+ * @brief SFTP limits structure.
+ */
+struct sftp_limits_struct {
+ uint64_t max_packet_length; /** maximum number of bytes in a single sftp packet */
+ uint64_t max_read_length; /** maximum length in a SSH_FXP_READ packet */
+ uint64_t max_write_length; /** maximum length in a SSH_FXP_WRITE packet */
+ uint64_t max_open_handles; /** maximum number of active handles allowed by server */
+};
+
+/**
+ * @brief SFTP names map structure to store the mapping between ids and names.
+ *
+ * This is mainly for the use of sftp_get_users_groups_by_id() function.
+ */
+struct sftp_name_id_map_struct {
+ /** @brief Count of name-id pairs in the map */
+ uint32_t count;
+
+ /** @brief Array of ids, ids[i] mapped to names[i] */
+ uint32_t *ids;
+
+ /** @brief Array of names, names[i] mapped to ids[i] */
+ char **names;
+};
+
/**
* @brief Creates a new sftp session.
*
@@ -207,24 +252,30 @@ struct sftp_statvfs_struct {
* with the server inside of the provided ssh session. This function call is
* usually followed by the sftp_init(), which initializes SFTP protocol itself.
*
- * @param session The ssh session to use.
+ * @param session The ssh session to use. The session *must* be in
+ * blocking mode since most `sftp_*` functions do not
+ * support the non-blocking API.
*
* @return A new sftp session or NULL on error.
*
* @see sftp_free()
* @see sftp_init()
+ * @see ssh_set_blocking()
*/
LIBSSH_API sftp_session sftp_new(ssh_session session);
/**
* @brief Start a new sftp session with an existing channel.
*
- * @param session The ssh session to use.
+ * @param session The ssh session to use. The session *must* be in
+ * blocking mode since most `sftp_*` functions do not
+ * support the non-blocking API.
* @param channel An open session channel with subsystem already allocated
*
* @return A new sftp session or NULL on error.
*
* @see sftp_free()
+ * @see ssh_set_blocking()
*/
LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel);
@@ -476,13 +527,18 @@ LIBSSH_API void sftp_file_set_blocking(sftp_file handle);
/**
* @brief Read from a file using an opened sftp file handle.
*
+ * This function caps the length a user is allowed to read from an sftp file.
+ *
+ * The value used for the cap is same as the value of the max_read_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
* @param file The opened sftp file handle to be read from.
*
* @param buf Pointer to buffer to receive read data.
*
* @param count Size of the buffer in bytes.
*
- * @return Number of bytes written, < 0 on error with ssh and sftp
+ * @return Number of bytes read, < 0 on error with ssh and sftp
* error set.
*
* @see sftp_get_error()
@@ -520,7 +576,8 @@ LIBSSH_API ssize_t sftp_read(sftp_file file, void *buf, size_t count);
* @see sftp_async_read()
* @see sftp_open()
*/
-LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len);
+SSH_DEPRECATED LIBSSH_API int sftp_async_read_begin(sftp_file file,
+ uint32_t len);
/**
* @brief Wait for an asynchronous read to complete and save the data.
@@ -545,11 +602,19 @@ LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len);
*
* @see sftp_async_read_begin()
*/
-LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_t id);
+SSH_DEPRECATED LIBSSH_API int sftp_async_read(sftp_file file,
+ void *data,
+ uint32_t len,
+ uint32_t id);
/**
* @brief Write to a file using an opened sftp file handle.
*
+ * This function caps the length a user is allowed to write to an sftp file.
+ *
+ * The value used for the cap is same as the value of the max_write_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
* @param file Open sftp file handle to write to.
*
* @param buf Pointer to buffer to write data.
@@ -565,6 +630,229 @@ LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_
*/
LIBSSH_API ssize_t sftp_write(sftp_file file, const void *buf, size_t count);
+/**
+ * @brief Deallocate memory corresponding to a sftp aio handle.
+ *
+ * This function deallocates memory corresponding to the aio handle returned
+ * by the sftp_aio_begin_*() functions. Users can use this function to free
+ * memory corresponding to an aio handle for an outstanding async i/o request
+ * on encountering some error.
+ *
+ * @param aio sftp aio handle corresponding to which memory has
+ * to be deallocated.
+ *
+ * @see sftp_aio_begin_read()
+ * @see sftp_aio_wait_read()
+ * @see sftp_aio_begin_write()
+ * @see sftp_aio_wait_write()
+ */
+LIBSSH_API void sftp_aio_free(sftp_aio aio);
+#define SFTP_AIO_FREE(x) \
+ do { if(x != NULL) {sftp_aio_free(x); x = NULL;} } while(0)
+
+/**
+ * @brief Start an asynchronous read from a file using an opened sftp
+ * file handle.
+ *
+ * Its goal is to avoid the slowdowns related to the request/response pattern
+ * of a synchronous read. To do so, you must call 2 functions :
+ *
+ * sftp_aio_begin_read() and sftp_aio_wait_read().
+ *
+ * - The first step is to call sftp_aio_begin_read(). This function sends a
+ * read request to the sftp server, dynamically allocates memory to store
+ * information about the sent request and provides the caller an sftp aio
+ * handle to that memory.
+ *
+ * - The second step is to call sftp_aio_wait_read() and pass it the address
+ * of a location storing the sftp aio handle provided by
+ * sftp_aio_begin_read().
+ *
+ * These two functions do not close the open sftp file handle passed to
+ * sftp_aio_begin_read() irrespective of whether they fail or not.
+ *
+ * It is the responsibility of the caller to ensure that the open sftp file
+ * handle passed to sftp_aio_begin_read() must not be closed before the
+ * corresponding call to sftp_aio_wait_read(). After sftp_aio_wait_read()
+ * returns, it is caller's decision whether to immediately close the file by
+ * calling sftp_close() or to keep it open and perform some more operations
+ * on it.
+ *
+ * This function caps the length a user is allowed to read from an sftp file,
+ * the value of len parameter after capping is returned on success.
+ *
+ * The value used for the cap is same as the value of the max_read_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
+ * @param file The opened sftp file handle to be read from.
+ *
+ * @param len Number of bytes to read.
+ *
+ * @param aio Pointer to a location where the sftp aio handle
+ * (corresponding to the sent request) should be stored.
+ *
+ * @returns On success, the number of bytes the server is
+ * requested to read (value of len parameter after
+ * capping). On error, SSH_ERROR with sftp and ssh
+ * errors set.
+ *
+ * @warning When calling this function, the internal file offset is
+ * updated corresponding to the number of bytes requested
+ * to read.
+ *
+ * @warning A call to sftp_aio_begin_read() sends a request to
+ * the server. When the server answers, libssh allocates
+ * memory to store it until sftp_aio_wait_read() is called.
+ * Not calling sftp_aio_wait_read() will lead to memory
+ * leaks.
+ *
+ * @see sftp_aio_wait_read()
+ * @see sftp_aio_free()
+ * @see sftp_open()
+ * @see sftp_close()
+ * @see sftp_get_error()
+ * @see ssh_get_error()
+ */
+LIBSSH_API ssize_t sftp_aio_begin_read(sftp_file file,
+ size_t len,
+ sftp_aio *aio);
+
+/**
+ * @brief Wait for an asynchronous read to complete and store the read data
+ * in the supplied buffer.
+ *
+ * A pointer to an sftp aio handle should be passed while calling
+ * this function. Except when the return value is SSH_AGAIN,
+ * this function releases the memory corresponding to the supplied
+ * aio handle and assigns NULL to that aio handle using the passed
+ * pointer to that handle.
+ *
+ * If the file is opened in non-blocking mode and the request hasn't been
+ * executed yet, this function returns SSH_AGAIN and must be called again
+ * using the same sftp aio handle.
+ *
+ * @param aio Pointer to the sftp aio handle returned by
+ * sftp_aio_begin_read().
+ *
+ * @param buf Pointer to the buffer in which read data will be stored.
+ *
+ * @param buf_size Size of the buffer in bytes. It should be bigger or
+ * equal to the length parameter of the
+ * sftp_aio_begin_read() call.
+ *
+ * @return Number of bytes read, 0 on EOF, SSH_ERROR if an error
+ * occurred, SSH_AGAIN if the file is opened in nonblocking
+ * mode and the request hasn't been executed yet.
+ *
+ * @warning A call to this function with an invalid sftp aio handle
+ * may never return.
+ *
+ * @see sftp_aio_begin_read()
+ * @see sftp_aio_free()
+ */
+LIBSSH_API ssize_t sftp_aio_wait_read(sftp_aio *aio,
+ void *buf,
+ size_t buf_size);
+
+/**
+ * @brief Start an asynchronous write to a file using an opened sftp
+ * file handle.
+ *
+ * Its goal is to avoid the slowdowns related to the request/response pattern
+ * of a synchronous write. To do so, you must call 2 functions :
+ *
+ * sftp_aio_begin_write() and sftp_aio_wait_write().
+ *
+ * - The first step is to call sftp_aio_begin_write(). This function sends a
+ * write request to the sftp server, dynamically allocates memory to store
+ * information about the sent request and provides the caller an sftp aio
+ * handle to that memory.
+ *
+ * - The second step is to call sftp_aio_wait_write() and pass it the address
+ * of a location storing the sftp aio handle provided by
+ * sftp_aio_begin_write().
+ *
+ * These two functions do not close the open sftp file handle passed to
+ * sftp_aio_begin_write() irrespective of whether they fail or not.
+ *
+ * It is the responsibility of the caller to ensure that the open sftp file
+ * handle passed to sftp_aio_begin_write() must not be closed before the
+ * corresponding call to sftp_aio_wait_write(). After sftp_aio_wait_write()
+ * returns, it is caller's decision whether to immediately close the file by
+ * calling sftp_close() or to keep it open and perform some more operations
+ * on it.
+ *
+ * This function caps the length a user is allowed to write to an sftp file,
+ * the value of len parameter after capping is returned on success.
+ *
+ * The value used for the cap is same as the value of the max_write_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
+ * @param file The opened sftp file handle to write to.
+ *
+ * @param buf Pointer to the buffer containing data to write.
+ *
+ * @param len Number of bytes to write.
+ *
+ * @param aio Pointer to a location where the sftp aio handle
+ * (corresponding to the sent request) should be stored.
+ *
+ * @returns On success, the number of bytes the server is
+ * requested to write (value of len parameter after
+ * capping). On error, SSH_ERROR with sftp and ssh errors
+ * set.
+ *
+ * @warning When calling this function, the internal file offset is
+ * updated corresponding to the number of bytes requested
+ * to write.
+ *
+ * @warning A call to sftp_aio_begin_write() sends a request to
+ * the server. When the server answers, libssh allocates
+ * memory to store it until sftp_aio_wait_write() is
+ * called. Not calling sftp_aio_wait_write() will lead to
+ * memory leaks.
+ *
+ * @see sftp_aio_wait_write()
+ * @see sftp_aio_free()
+ * @see sftp_open()
+ * @see sftp_close()
+ * @see sftp_get_error()
+ * @see ssh_get_error()
+ */
+LIBSSH_API ssize_t sftp_aio_begin_write(sftp_file file,
+ const void *buf,
+ size_t len,
+ sftp_aio *aio);
+
+/**
+ * @brief Wait for an asynchronous write to complete.
+ *
+ * A pointer to an sftp aio handle should be passed while calling
+ * this function. Except when the return value is SSH_AGAIN,
+ * this function releases the memory corresponding to the supplied
+ * aio handle and assigns NULL to that aio handle using the passed
+ * pointer to that handle.
+ *
+ * If the file is opened in non-blocking mode and the request hasn't
+ * been executed yet, this function returns SSH_AGAIN and must be called
+ * again using the same sftp aio handle.
+ *
+ * @param aio Pointer to the sftp aio handle returned by
+ * sftp_aio_begin_write().
+ *
+ * @return Number of bytes written on success, SSH_ERROR
+ * if an error occurred, SSH_AGAIN if the file is
+ * opened in nonblocking mode and the request hasn't
+ * been executed yet.
+ *
+ * @warning A call to this function with an invalid sftp aio handle
+ * may never return.
+ *
+ * @see sftp_aio_begin_write()
+ * @see sftp_aio_free()
+ */
+LIBSSH_API ssize_t sftp_aio_wait_write(sftp_aio *aio);
+
/**
* @brief Seek to a specific location in a file.
*
@@ -605,8 +893,7 @@ LIBSSH_API unsigned long sftp_tell(sftp_file file);
* @param file Open sftp file handle.
*
* @return The offset of the current byte relative to the beginning
- * of the file associated with the file descriptor. < 0 on
- * error.
+ * of the file associated with the file descriptor.
*/
LIBSSH_API uint64_t sftp_tell64(sftp_file file);
@@ -699,6 +986,29 @@ LIBSSH_API int sftp_rename(sftp_session sftp, const char *original, const char
*/
LIBSSH_API int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr);
+/**
+ * @brief This request is like setstat (excluding mode and size) but sets file
+ * attributes on symlinks themselves.
+ *
+ * Note, that this function can only set time values using 32 bit values due to
+ * the restrictions in the SFTP protocol version 3 implemented by libssh.
+ * The support for 64 bit time values was introduced in SFTP version 5, which is
+ * not implemented by libssh nor any major SFTP servers.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param file The symbolic link which attributes should be changed.
+ *
+ * @param attr The file attributes structure with the attributes set
+ * which should be changed.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ *
+ * @see sftp_get_error()
+ */
+LIBSSH_API int
+sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr);
+
/**
* @brief Change the file owner and group
*
@@ -842,6 +1152,24 @@ LIBSSH_API void sftp_statvfs_free(sftp_statvfs_t statvfs_o);
*/
LIBSSH_API int sftp_fsync(sftp_file file);
+/**
+ * @brief Get information about the various limits the server might impose.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @return A limits structure or NULL on error.
+ *
+ * @see sftp_get_error()
+ */
+LIBSSH_API sftp_limits_t sftp_limits(sftp_session sftp);
+
+/**
+ * @brief Free the memory of an allocated limits.
+ *
+ * @param limits The limits to free.
+ */
+LIBSSH_API void sftp_limits_free(sftp_limits_t limits);
+
/**
* @brief Canonicalize a sftp path.
*
@@ -864,6 +1192,92 @@ LIBSSH_API char *sftp_canonicalize_path(sftp_session sftp, const char *path);
*/
LIBSSH_API int sftp_server_version(sftp_session sftp);
+/**
+ * @brief Canonicalize path using expand-path@openssh.com extension
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param path The path to be canonicalized.
+ *
+ * @return A pointer to the newly allocated canonicalized path,
+ * NULL on error. The caller needs to free the memory
+ * using ssh_string_free_char().
+ */
+LIBSSH_API char *sftp_expand_path(sftp_session sftp, const char *path);
+
+/**
+ * @brief Get the specified user's home directory
+ *
+ * This calls the "home-directory" extension. You should check if the extension
+ * is supported using:
+ *
+ * @code
+ * int supported = sftp_extension_supported(sftp, "home-directory", "1");
+ * @endcode
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param username username of the user whose home directory is requested.
+ *
+ * @return On success, a newly allocated string containing the
+ * absolute real-path of the home directory of the user.
+ * NULL on error. The caller needs to free the memory
+ * using ssh_string_free_char().
+ */
+LIBSSH_API char *sftp_home_directory(sftp_session sftp, const char *username);
+
+/**
+ * @brief Create a new sftp_name_id_map struct.
+ *
+ * @param count The number of ids/names to store in the map.
+ *
+ * @return A pointer to the newly allocated sftp_name_id_map
+ * struct.
+ */
+LIBSSH_API sftp_name_id_map sftp_name_id_map_new(uint32_t count);
+
+/**
+ * @brief Free the memory of an allocated `sftp_name_id_map` struct.
+ *
+ * @param map A pointer to the `sftp_name_id_map` struct to free.
+ */
+LIBSSH_API void sftp_name_id_map_free(sftp_name_id_map map);
+
+/**
+ * @brief Retrieves usernames and group names based on provided user and group
+ * IDs.
+ *
+ * The retrieved names are stored in the `names` field of the
+ * `sftp_name_id_map` structure. In case a uid or gid is not found, an empty
+ * string is stored.
+ *
+ * This calls the "users-groups-by-id@openssh.com" extension.
+ * You should check if the extension is supported using:
+ *
+ * @code
+ * int supported = sftp_extension_supported(sftp,
+ * "users-groups-by-id@openssh.com", "1");
+ * @endcode
+ *
+ * @param sftp The SFTP session handle.
+ *
+ * @param users_map A pointer to a `sftp_name_id_map` struct with the user
+ * IDs. Can be NULL if only group names are needed.
+ *
+ * @param groups_map A pointer to a `sftp_name_id_map` struct with the group
+ * IDs. Can be NULL if only user names are needed.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ *
+ * @note The caller needs to free the memory used for
+ * the maps later using `sftp_name_id_map_free()`.
+ *
+ * @see sftp_get_error()
+ */
+LIBSSH_API int sftp_get_users_groups_by_id(sftp_session sftp,
+ sftp_name_id_map users_map,
+ sftp_name_id_map groups_map);
+
#ifdef WITH_SERVER
/**
* @brief Create a new sftp server session.
diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h
index 70987b1e..4b578257 100644
--- a/include/libssh/sftp_priv.h
+++ b/include/libssh/sftp_priv.h
@@ -21,6 +21,8 @@
#ifndef SFTP_PRIV_H
#define SFTP_PRIV_H
+#include
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -54,6 +56,61 @@ int sftp_reply_version(sftp_client_message client_msg);
*/
int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data, uint32_t len);
+void sftp_set_error(sftp_session sftp, int errnum);
+
+void sftp_message_free(sftp_message msg);
+
+int sftp_read_and_dispatch(sftp_session sftp);
+
+sftp_message sftp_dequeue(sftp_session sftp, uint32_t id);
+
+/**
+ * @brief Receive the response of an sftp request
+ *
+ * In blocking mode, if the response hasn't arrived at the time of call, this
+ * function waits for the response to arrive.
+ *
+ * @param sftp The sftp session via which the request was sent.
+ *
+ * @param id The request identifier of the request whose
+ * corresponding response is required.
+ *
+ * @param blocking Flag to indicate the operating mode. true indicates
+ * blocking mode and false indicates non-blocking mode
+ *
+ * @param msg_ptr Pointer to the location to store the response message.
+ * In case of success, the message is allocated
+ * dynamically and must be freed (using
+ * sftp_message_free()) by the caller after usage. In case
+ * of failure, this is left untouched.
+ *
+ * @returns SSH_OK on success
+ * @returns SSH_ERROR on failure with the sftp and ssh errors set
+ * @returns SSH_AGAIN in case of non-blocking mode if the response hasn't
+ * arrived yet.
+ *
+ * @warning In blocking mode, this may block indefinitely for an invalid request
+ * identifier.
+ */
+int sftp_recv_response_msg(sftp_session sftp,
+ uint32_t id,
+ bool blocking,
+ sftp_message *msg_ptr);
+
+/*
+ * Assigns a new SFTP ID for new requests and assures there is no collision
+ * between them.
+ * Returns a new ID ready to use in a request
+ */
+static inline uint32_t sftp_get_new_id(sftp_session session)
+{
+ return ++session->id_counter;
+}
+
+sftp_status_message parse_status_msg(sftp_message msg);
+
+void status_msg_free(sftp_status_message status);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/sftpserver.h b/include/libssh/sftpserver.h
index d7ed6e49..01b17c7f 100644
--- a/include/libssh/sftpserver.h
+++ b/include/libssh/sftpserver.h
@@ -29,6 +29,10 @@ extern "C" {
#endif
#include
+
+#include "libssh/libssh.h"
+#include "libssh/sftp.h"
+
/**
* @defgroup libssh_sftp_server The libssh SFTP server API
*
diff --git a/include/libssh/sk_api.h b/include/libssh/sk_api.h
new file mode 100644
index 00000000..f85f4b8b
--- /dev/null
+++ b/include/libssh/sk_api.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2019 Google LLC
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+/*
+ * This file is a copy of the OpenSSH project's sk-api.h file pulled from
+ * https://github.com/openssh/openssh-portable/commit/a9cbe10da2be5be76755af0cea029db0f9c1f263
+ * with only the flags, algorithms, error codes, and struct definitions. The
+ * function declarations and other OpenSSH-specific code have been removed.
+ */
+
+#ifndef SK_API_H
+#define SK_API_H 1
+
+#include
+#include
+
+/* FIDO2/U2F Operation Flags */
+
+/** Requires user presence confirmation (tap/touch) */
+#ifndef SSH_SK_USER_PRESENCE_REQD
+#define SSH_SK_USER_PRESENCE_REQD 0x01
+#endif
+
+/** Requires user verification (PIN/biometric) - FIDO2 only */
+#ifndef SSH_SK_USER_VERIFICATION_REQD
+#define SSH_SK_USER_VERIFICATION_REQD 0x04
+#endif
+
+/** Force resident key enrollment even if a resident key with given user ID
+ * already exists - FIDO2 only */
+#ifndef SSH_SK_FORCE_OPERATION
+#define SSH_SK_FORCE_OPERATION 0x10
+#endif
+
+/** Create/use resident key stored on authenticator - FIDO2 only */
+#ifndef SSH_SK_RESIDENT_KEY
+#define SSH_SK_RESIDENT_KEY 0x20
+#endif
+
+/* Algorithms */
+
+/** ECDSA with P-256 curve */
+#define SSH_SK_ECDSA 0x00
+
+/** Ed25519 - FIDO2 only */
+#define SSH_SK_ED25519 0x01
+
+/* Error codes */
+
+/** General unspecified failure */
+#define SSH_SK_ERR_GENERAL -1
+
+/** Requested algorithm/feature/option not supported */
+#define SSH_SK_ERR_UNSUPPORTED -2
+
+/** PIN (or other user verification) required but either missing or invalid */
+#define SSH_SK_ERR_PIN_REQUIRED -3
+
+/** No suitable security key / authenticator device was found */
+#define SSH_SK_ERR_DEVICE_NOT_FOUND -4
+
+/** Attempt to create a resident key that already exists (duplicate) */
+#define SSH_SK_ERR_CREDENTIAL_EXISTS -5
+
+/**
+ * @brief Response structure for FIDO2/U2F key enrollment operations
+ *
+ * Contains all data returned by a FIDO2/U2F authenticator after successful
+ * enrollment of a new credential.
+ */
+struct sk_enroll_response {
+ /** @brief FIDO2/U2F authenticator flags from the enrollment operation
+ *
+ * Contains flags indicating authenticator capabilities and state during
+ * enrollment, such as user presence (UP), user verification
+ * (UV), and resident key.
+ */
+ uint8_t flags;
+
+ /** @brief Public key data in standard format
+ *
+ * For ECDSA (P-256): 65 bytes in SEC1 uncompressed point format
+ * (0x04 prefix + 32-byte X coordinate + 32-byte Y coordinate)
+ * For Ed25519: 32 bytes containing the raw public key (FIDO2 only)
+ */
+ uint8_t *public_key;
+
+ /** @brief Length of public_key buffer in bytes
+ *
+ * Expected values: 65 for ECDSA P-256, 32 for Ed25519
+ */
+ size_t public_key_len;
+
+ /** @brief Opaque credential handle/ID used to identify this key
+ *
+ * Authenticator-generated binary data that uniquely identifies this
+ * credential. Used in subsequent sign operations to specify which
+ * key to use. Format and contents are authenticator-specific.
+ */
+ uint8_t *key_handle;
+
+ /** @brief Length of key_handle buffer in bytes
+ *
+ * Length varies by authenticator.
+ */
+ size_t key_handle_len;
+
+ /** @brief Enrollment signature over the enrollment data
+ *
+ * FIDO2/U2F authenticator signature proving the credential was created
+ * by this specific authenticator. Used for enrollment verification.
+ * Format depends on algorithm.
+ */
+ uint8_t *signature;
+
+ /** @brief Length of signature buffer in bytes
+ *
+ * Length varies by algorithm.
+ */
+ size_t signature_len;
+
+ /** @brief X.509 attestation certificate
+ *
+ * Certificate that attests to the authenticity of the authenticator
+ * and the enrollment operation. Used to verify the authenticator's
+ * identity and manufacturer.
+ */
+ uint8_t *attestation_cert;
+
+ /** @brief Length of attestation_cert buffer in bytes */
+ size_t attestation_cert_len;
+
+ /** @brief FIDO2/U2F authenticator data from enrollment
+ *
+ * CBOR-encoded authenticator data containing RP ID hash, flags,
+ * counter, and attested credential data. Used for attestation
+ * verification according to the FIDO2 specification.
+ */
+ uint8_t *authdata;
+
+ /** @brief Length of authdata buffer in bytes
+ *
+ * Length varies depending on credential data and extensions.
+ */
+ size_t authdata_len;
+};
+
+/**
+ * @brief Response structure for FIDO2/U2F key signing operations
+ *
+ * Contains signature components and metadata returned by a FIDO2/U2F
+ * authenticator after a successful signing operation.
+ */
+struct sk_sign_response {
+ /** @brief FIDO2/U2F authenticator flags from the signing operation
+ *
+ * Contains flags indicating authenticator state during signing,
+ * including user presence (UP) and user verification (UV) flags.
+ * Used to verify that proper user interaction occurred while signing.
+ */
+ uint8_t flags;
+
+ /** @brief Authenticator signature counter value
+ *
+ * Monotonically increasing counter maintained by the authenticator.
+ * Incremented on each successful signing operation. Used to detect
+ * cloned or duplicated authenticators.
+ */
+ uint32_t counter;
+
+ /** @brief R component of ECDSA signature or Ed25519 signature */
+ uint8_t *sig_r;
+
+ /** @brief Length of sig_r buffer in bytes */
+ size_t sig_r_len;
+
+ /** @brief S component of ECDSA signature */
+ uint8_t *sig_s;
+
+ /** @brief Length of sig_s buffer in bytes */
+ size_t sig_s_len;
+};
+
+/**
+ * @brief Structure representing a resident/discoverable credential
+ *
+ * Represents a FIDO2 resident key (discoverable credential) that is
+ * stored on the authenticator and can be discovered without providing
+ * a credential ID.
+ */
+struct sk_resident_key {
+ /** @brief Cryptographic algorithm identifier for this key
+ *
+ * SSH_SK_ECDSA (0x00): ECDSA with P-256 curve
+ * SSH_SK_ED25519 (0x01): Ed25519 signature algorithm
+ */
+ uint32_t alg;
+
+ /** @brief Slot/index number of this key on the authenticator
+ *
+ * Zero-based index indicating the position of this resident key
+ * in the authenticator's internal storage. Used for key management
+ * and identification when multiple resident keys exist.
+ */
+ size_t slot;
+
+ /** @brief Relying Party (application) identifier string
+ *
+ * The RP ID (typically a domain name) that this resident key
+ * is associated with. Determines which application/service
+ * this key can be used for.
+ */
+ char *application;
+
+ /** @brief Embedded enrollment response containing key material
+ *
+ * Contains the same data as returned during initial enrollment,
+ * including public key, key handle, and associated metadata.
+ */
+ struct sk_enroll_response key;
+
+ /** @brief Flags associated with this resident key
+ *
+ * SSH_SK_USER_PRESENCE_REQD: Requires user presence for operations
+ * SSH_SK_USER_VERIFICATION_REQD: Requires user verification
+ * (PIN/biometric)
+ */
+ uint8_t flags;
+
+ /** @brief User identifier associated with this resident key
+ *
+ * Binary user ID that was provided during key enrollment.
+ * Used to identify which user account this key belongs to.
+ */
+ uint8_t *user_id;
+
+ /** @brief Length of user_id buffer in bytes
+ *
+ * Length of the user identifier.
+ */
+ size_t user_id_len;
+};
+
+/**
+ * @brief Configuration option structure for FIDO2/U2F operations
+ *
+ * Represents a single configuration parameter that can be passed
+ * to FIDO2/U2F middleware.
+ */
+struct sk_option {
+ /** @brief Option name/key identifier */
+ char *name;
+
+ /** @brief Option value as bytes */
+ char *value;
+
+ /** @brief Indicates if this option is required for the operation
+ *
+ * Non-zero if this option must be processed and cannot be ignored.
+ * Zero if this option is advisory and can be skipped if the
+ * middleware does not support it.
+ */
+ uint8_t required;
+};
+
+/** Current SK API version */
+#define SSH_SK_VERSION_MAJOR 0x000a0000
+#define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
+
+#endif /* SK_API_H */
diff --git a/include/libssh/sk_common.h b/include/libssh/sk_common.h
new file mode 100644
index 00000000..7c18448d
--- /dev/null
+++ b/include/libssh/sk_common.h
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef SK_COMMON_H
+#define SK_COMMON_H
+
+#include "libssh/callbacks.h"
+#include "libssh/sk_api.h"
+
+#include
+
+#define SK_MAX_USER_ID_LEN 64
+
+#define SK_NOT_SUPPORTED_MSG \
+ "Security Key functionality is not supported in this build of libssh. " \
+ "Please enable support by building using the WITH_FIDO2 build option."
+
+/**
+ * @brief Convert security key error code to human-readable string
+ *
+ * Converts a security key error code to a descriptive string representation
+ * that can be used for logging user-facing error messages.
+ *
+ * @param[in] sk_err The security key error code to convert.
+ *
+ * @return Constant string describing the error. Never returns NULL.
+ * Returns "Unknown error" for unrecognized error codes.
+ *
+ * @note The returned string is statically allocated and should not be freed.
+ */
+const char *ssh_sk_err_to_string(int sk_err);
+
+/**
+ * @brief Securely clear the contents of an sk_enroll_response structure
+ *
+ * Overwrites sensitive data within the enrollment response structure with
+ * zeros to prevent information leakage. This function only clears and frees the
+ * contents and does not free the structure itself.
+ *
+ * @param[in] enroll_response The enrollment response structure to clear.
+ * Can be NULL (no operation performed).
+ *
+ * @note This function only frees the memory for the contents and does not free
+ * memory for the structure itself. Use sk_enroll_response_free() for complete
+ * cleanup, which also performs secure clearing internally.
+ */
+void sk_enroll_response_burn(struct sk_enroll_response *enroll_response);
+
+/**
+ * @brief Securely free an sk_enroll_response structure
+ *
+ * Performs secure clearing of sensitive data within the enrollment response
+ * structure before freeing the allocated memory. This function internally
+ * calls sk_enroll_response_burn() before deallocation.
+ *
+ * @param[in] enroll_response The enrollment response structure to free.
+ * Can be NULL (no operation performed).
+ *
+ * @note Developers do not need to call sk_enroll_response_burn() before
+ * calling this function, as secure clearing is performed automatically.
+ */
+void sk_enroll_response_free(struct sk_enroll_response *enroll_response);
+
+/**
+ * @brief Free an sk_sign_response structure
+ *
+ * Frees the memory allocated for a sign response structure and all its
+ * associated data. This function performs secure clearing of sensitive
+ * data before deallocation.
+ *
+ * @param[in] sign_response The sign response structure to free.
+ * Can be NULL (no operation performed).
+ *
+ * @note This is a secure free operation that clears sensitive data before
+ * memory deallocation to prevent information leakage.
+ */
+void sk_sign_response_free(struct sk_sign_response *sign_response);
+
+/**
+ * @brief Free an sk_resident_key structure
+ *
+ * Frees the memory allocated for a resident key structure and all its
+ * associated data. This function performs secure clearing of sensitive
+ * data before deallocation.
+ *
+ * @param[in] resident_key The resident key structure to free.
+ * Can be NULL (no operation performed).
+ *
+ * @note This is a secure free operation that clears sensitive data before
+ * memory deallocation to prevent information leakage.
+ */
+void sk_resident_key_free(struct sk_resident_key *resident_key);
+
+/**
+ * @brief Free an sk_option array and all its contents
+ *
+ * Frees a NULL-terminated array of sk_option structures, including all
+ * allocated memory for option names and values within each structure.
+ *
+ * @param[in] options NULL-terminated array of sk_option pointers to free.
+ * Can be NULL (no operation performed).
+ *
+ * @note The options array must be NULL-terminated for proper freeing.
+ * Each sk_option structure and its name/value strings will be freed.
+ */
+void sk_options_free(struct sk_option **options);
+
+/**
+ * @brief Validate options and extract values for specific keys
+ *
+ * Validates that all required options are supported and extracts values
+ * for the specified keys. This function is primarily intended for use
+ * by the SK callback implementations.
+ *
+ * @param[in] options NULL-terminated array of sk_option pointers to validate.
+ * @param[in] keys NULL-terminated array of supported option keys.
+ * @param[out] values Pointer to array that will be allocated and filled with
+ * copied values (same order as keys). The caller must free
+ * this array and all contained strings when done.
+ *
+ * @return SSH_OK on success, SSH_ERROR if unsupported required options found
+ * or memory allocation fails.
+ *
+ * @note The values array is allocated by this function and contains copies
+ * of the option values. The caller must free both the array and all
+ * non-NULL string values within it. Values for keys not found in
+ * options will be set to NULL.
+ */
+int sk_options_validate_get(const struct sk_option **options,
+ const char **keys,
+ char ***values);
+
+/**
+ * @brief Duplicate an array of sk_option structures
+ *
+ * Creates a deep copy of an array of security key options. Each option
+ * structure and its string fields are duplicated.
+ *
+ * @param[in] options The array of options to duplicate. Must be
+ * NULL-terminated array of struct sk_option pointers.
+ * Can be NULL.
+ *
+ * @return A newly allocated array of duplicated options on success,
+ * NULL on failure or if options is NULL.
+ * The returned array should be freed with SK_OPTIONS_FREE().
+ */
+struct sk_option **sk_options_dup(const struct sk_option **options);
+
+/**
+ * @brief Check version compatibility of security key callbacks
+ *
+ * Validates that the provided security key callbacks use an SK API
+ * version whose major portion is the same as the major version that libssh
+ * supports.
+ *
+ * @param[in] callbacks Pointer to the sk_callbacks structure to check.
+ *
+ * @return true if the callbacks are compatible, false otherwise.
+ */
+bool sk_callbacks_check_compatibility(
+ const struct ssh_sk_callbacks_struct *callbacks);
+
+/* Convenience macros for secure freeing with NULL checks and pointer reset */
+#define SK_ENROLL_RESPONSE_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ sk_enroll_response_free(x); \
+ x = NULL; \
+ } \
+ } while (0)
+
+#define SK_SIGN_RESPONSE_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ sk_sign_response_free(x); \
+ x = NULL; \
+ } \
+ } while (0)
+
+#define SK_RESIDENT_KEY_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ sk_resident_key_free(x); \
+ x = NULL; \
+ } \
+ } while (0)
+
+#define SK_OPTIONS_FREE(x) \
+ do { \
+ if ((x) != NULL) { \
+ sk_options_free(x); \
+ x = NULL; \
+ } \
+ } while (0)
+
+#endif /* SK_COMMON_H */
diff --git a/include/libssh/sk_usbhid.h b/include/libssh/sk_usbhid.h
new file mode 100644
index 00000000..025c3daf
--- /dev/null
+++ b/include/libssh/sk_usbhid.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef SK_USBHID_H
+#define SK_USBHID_H
+
+/**
+ * @brief Get the USB-HID security key callbacks.
+ *
+ * This function returns a pointer to the implementation of
+ * security key callbacks for FIDO2/U2F devices using the USB-HID
+ * protocol.
+ *
+ * @return Pointer to the ssh_sk_callbacks_struct
+ *
+ * @see ssh_sk_callbacks_struct
+ */
+const struct ssh_sk_callbacks_struct *ssh_sk_get_usbhid_callbacks(void);
+
+#endif /* SK_USBHID_H */
diff --git a/include/libssh/sntrup761.h b/include/libssh/sntrup761.h
new file mode 100644
index 00000000..aa05a9fd
--- /dev/null
+++ b/include/libssh/sntrup761.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013 by Aris Adamantiadis
+ * Copyright (c) 2023 Simon Josefsson
+ * Copyright (c) 2025 Jakub Jelen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation,
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SNTRUP761_H_
+#define SNTRUP761_H_
+
+#include "config.h"
+#include "curve25519.h"
+#include "libssh.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_CURVE25519
+#define HAVE_SNTRUP761 1
+#endif
+
+/*
+ * Derived from public domain source, written by (in alphabetical order):
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#include
+#include
+
+#define SNTRUP761_SECRETKEY_SIZE 1763
+#define SNTRUP761_PUBLICKEY_SIZE 1158
+#define SNTRUP761_CIPHERTEXT_SIZE 1039
+#define SNTRUP761_SIZE 32
+
+typedef void sntrup761_random_func(void *ctx, size_t length, uint8_t *dst);
+
+void sntrup761_keypair(uint8_t *pk,
+ uint8_t *sk,
+ void *random_ctx,
+ sntrup761_random_func *random);
+void sntrup761_enc(uint8_t *c,
+ uint8_t *k,
+ const uint8_t *pk,
+ void *random_ctx,
+ sntrup761_random_func *random);
+void sntrup761_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk);
+
+typedef unsigned char ssh_sntrup761_pubkey[SNTRUP761_PUBLICKEY_SIZE];
+typedef unsigned char ssh_sntrup761_privkey[SNTRUP761_SECRETKEY_SIZE];
+typedef unsigned char ssh_sntrup761_ciphertext[SNTRUP761_CIPHERTEXT_SIZE];
+
+int ssh_client_sntrup761x25519_init(ssh_session session);
+void ssh_client_sntrup761x25519_remove_callbacks(ssh_session session);
+
+#ifdef WITH_SERVER
+void ssh_server_sntrup761x25519_init(ssh_session session);
+#endif /* WITH_SERVER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SNTRUP761_H_ */
diff --git a/include/libssh/socket.h b/include/libssh/socket.h
index cdd3c837..f929c27c 100644
--- a/include/libssh/socket.h
+++ b/include/libssh/socket.h
@@ -33,14 +33,15 @@ void ssh_socket_cleanup(void);
ssh_socket ssh_socket_new(ssh_session session);
void ssh_socket_reset(ssh_socket s);
void ssh_socket_free(ssh_socket s);
-void ssh_socket_set_fd(ssh_socket s, socket_t fd);
+int ssh_socket_set_fd(ssh_socket s, socket_t fd);
socket_t ssh_socket_get_fd(ssh_socket s);
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p);
int ssh_socket_unix(ssh_socket s, const char *path);
+#if WITH_EXEC
void ssh_execute_command(const char *command, socket_t in, socket_t out);
-#ifndef _WIN32
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command);
#endif
+int ssh_socket_connect_proxyjump(ssh_socket s);
void ssh_socket_close(ssh_socket s);
int ssh_socket_write(ssh_socket s,const void *buffer, uint32_t len);
int ssh_socket_is_open(ssh_socket s);
diff --git a/include/libssh/ssh2.h b/include/libssh/ssh2.h
index 35214330..dc395438 100644
--- a/include/libssh/ssh2.h
+++ b/include/libssh/ssh2.h
@@ -16,14 +16,23 @@
#define SSH2_MSG_KEXDH_REPLY 31
#define SSH2_MSG_KEX_ECDH_INIT 30
#define SSH2_MSG_KEX_ECDH_REPLY 31
-#define SSH2_MSG_ECMQV_INIT 30
-#define SSH2_MSG_ECMQV_REPLY 31
+#define SSH2_MSG_KEX_HYBRID_INIT 30
+#define SSH2_MSG_KEX_HYBRID_REPLY 31
#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
#define SSH2_MSG_KEX_DH_GEX_GROUP 31
#define SSH2_MSG_KEX_DH_GEX_INIT 32
#define SSH2_MSG_KEX_DH_GEX_REPLY 33
#define SSH2_MSG_KEX_DH_GEX_REQUEST 34
+
+#define SSH2_MSG_KEXGSS_INIT 30
+#define SSH2_MSG_KEXGSS_CONTINUE 31
+#define SSH2_MSG_KEXGSS_COMPLETE 32
+#define SSH2_MSG_KEXGSS_HOSTKEY 33
+#define SSH2_MSG_KEXGSS_ERROR 34
+#define SSH2_MSG_KEXGSS_GROUPREQ 40
+#define SSH2_MSG_KEXGSS_GROUP 41
+
#define SSH2_MSG_USERAUTH_REQUEST 50
#define SSH2_MSG_USERAUTH_FAILURE 51
#define SSH2_MSG_USERAUTH_SUCCESS 52
diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h
index 36589cff..9214a928 100644
--- a/include/libssh/wrapper.h
+++ b/include/libssh/wrapper.h
@@ -72,33 +72,34 @@ struct ssh_crypto_struct;
typedef struct ssh_mac_ctx_struct *ssh_mac_ctx;
MD5CTX md5_init(void);
-void md5_update(MD5CTX c, const void *data, size_t len);
-void md5_final(unsigned char *md,MD5CTX c);
+void md5_ctx_free(MD5CTX);
+int md5_update(MD5CTX c, const void *data, size_t len);
+int md5_final(unsigned char *md, MD5CTX c);
+int md5(const unsigned char *digest, size_t len, unsigned char *hash);
SHACTX sha1_init(void);
-void sha1_update(SHACTX c, const void *data, size_t len);
-void sha1_final(unsigned char *md,SHACTX c);
-void sha1(const unsigned char *digest,size_t len,unsigned char *hash);
+void sha1_ctx_free(SHACTX);
+int sha1_update(SHACTX c, const void *data, size_t len);
+int sha1_final(unsigned char *md,SHACTX c);
+int sha1(const unsigned char *digest,size_t len, unsigned char *hash);
SHA256CTX sha256_init(void);
-void sha256_update(SHA256CTX c, const void *data, size_t len);
-void sha256_final(unsigned char *md,SHA256CTX c);
-void sha256(const unsigned char *digest, size_t len, unsigned char *hash);
+void sha256_ctx_free(SHA256CTX);
+int sha256_update(SHA256CTX c, const void *data, size_t len);
+int sha256_final(unsigned char *md,SHA256CTX c);
+int sha256(const unsigned char *digest, size_t len, unsigned char *hash);
SHA384CTX sha384_init(void);
-void sha384_update(SHA384CTX c, const void *data, size_t len);
-void sha384_final(unsigned char *md,SHA384CTX c);
-void sha384(const unsigned char *digest, size_t len, unsigned char *hash);
+void sha384_ctx_free(SHA384CTX);
+int sha384_update(SHA384CTX c, const void *data, size_t len);
+int sha384_final(unsigned char *md,SHA384CTX c);
+int sha384(const unsigned char *digest, size_t len, unsigned char *hash);
SHA512CTX sha512_init(void);
-void sha512_update(SHA512CTX c, const void *data, size_t len);
-void sha512_final(unsigned char *md,SHA512CTX c);
-void sha512(const unsigned char *digest, size_t len, unsigned char *hash);
-
-void evp(int nid, unsigned char *digest, size_t len, unsigned char *hash, unsigned int *hlen);
-EVPCTX evp_init(int nid);
-void evp_update(EVPCTX ctx, const void *data, size_t len);
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen);
+void sha512_ctx_free(SHA512CTX);
+int sha512_update(SHA512CTX c, const void *data, size_t len);
+int sha512_final(unsigned char *md,SHA512CTX c);
+int sha512(const unsigned char *digest, size_t len, unsigned char *hash);
HMACCTX hmac_init(const void *key,size_t len, enum ssh_hmac_e type);
int hmac_update(HMACCTX c, const void *data, size_t len);
@@ -129,6 +130,7 @@ int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, EVP_PKEY **pkey,
int evp_dup_dsa_pkey(const ssh_key key, ssh_key new_key, int demote);
int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote);
int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote);
+int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote);
#endif /* HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER */
#ifdef __cplusplus
diff --git a/libssh.pc.cmake b/libssh.pc.cmake
index f288b94d..970db5e2 100644
--- a/libssh.pc.cmake
+++ b/libssh.pc.cmake
@@ -8,3 +8,4 @@ Description: The SSH Library
Version: @PROJECT_VERSION@
Libs: -L${libdir} -lssh
Cflags: -I${includedir}
+Requires.private: @LIBSSH_PC_REQUIRES_PRIVATE@
diff --git a/src/ABI/current b/src/ABI/current
index b617d997..91f3b438 100644
--- a/src/ABI/current
+++ b/src/ABI/current
@@ -1 +1 @@
-4.9.0
\ No newline at end of file
+4.11.0
\ No newline at end of file
diff --git a/src/ABI/libssh-4.10.0.symbols b/src/ABI/libssh-4.10.0.symbols
new file mode 100644
index 00000000..34949837
--- /dev/null
+++ b/src/ABI/libssh-4.10.0.symbols
@@ -0,0 +1,445 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.10.1.symbols b/src/ABI/libssh-4.10.1.symbols
new file mode 100644
index 00000000..34949837
--- /dev/null
+++ b/src/ABI/libssh-4.10.1.symbols
@@ -0,0 +1,445 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.10.2.symbols b/src/ABI/libssh-4.10.2.symbols
new file mode 100644
index 00000000..34949837
--- /dev/null
+++ b/src/ABI/libssh-4.10.2.symbols
@@ -0,0 +1,445 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.10.3.symbols b/src/ABI/libssh-4.10.3.symbols
new file mode 100644
index 00000000..34949837
--- /dev/null
+++ b/src/ABI/libssh-4.10.3.symbols
@@ -0,0 +1,445 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.10.4.symbols b/src/ABI/libssh-4.10.4.symbols
new file mode 100644
index 00000000..34949837
--- /dev/null
+++ b/src/ABI/libssh-4.10.4.symbols
@@ -0,0 +1,445 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.11.0.symbols b/src/ABI/libssh-4.11.0.symbols
new file mode 100644
index 00000000..0b1a917e
--- /dev/null
+++ b/src/ABI/libssh-4.11.0.symbols
@@ -0,0 +1,465 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_aio_begin_read
+sftp_aio_begin_write
+sftp_aio_free
+sftp_aio_wait_read
+sftp_aio_wait_write
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_channel_default_data_callback
+sftp_channel_default_subsystem_request
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_expand_path
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_get_users_groups_by_id
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_hardlink
+sftp_home_directory
+sftp_init
+sftp_limits
+sftp_limits_free
+sftp_lsetstat
+sftp_lstat
+sftp_mkdir
+sftp_name_id_map_free
+sftp_name_id_map_new
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_state
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_pty_size_modes
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_supported_methods
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_get_sk_application
+ssh_key_get_sk_flags
+ssh_key_get_sk_user_id
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_ctx_free
+ssh_pki_ctx_get_sk_attestation_buffer
+ssh_pki_ctx_new
+ssh_pki_ctx_options_set
+ssh_pki_ctx_set_sk_pin_callback
+ssh_pki_ctx_sk_callbacks_option_set
+ssh_pki_ctx_sk_callbacks_options_clear
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_base64_format
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_file_format
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_generate_key
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_request_no_more_sessions
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_sk_resident_keys_load
+ssh_string_burn
+ssh_string_cmp
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_from_data
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+sshsig_sign
+sshsig_verify
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.1.symbols b/src/ABI/libssh-4.9.1.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.1.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.2.symbols b/src/ABI/libssh-4.9.2.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.2.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.3.symbols b/src/ABI/libssh-4.9.3.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.3.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.4.symbols b/src/ABI/libssh-4.9.4.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.4.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.5.symbols b/src/ABI/libssh-4.9.5.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.5.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/ABI/libssh-4.9.6.symbols b/src/ABI/libssh-4.9.6.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.6.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 24f85dc0..a46f585b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,50 +9,20 @@ set(LIBSSH_LINK_LIBRARIES
${LIBSSH_REQUIRED_LIBRARIES}
)
-if (OPENSSL_CRYPTO_LIBRARIES)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${OPENSSL_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${OPENSSL_CRYPTO_LIBRARIES}
- )
-endif (OPENSSL_CRYPTO_LIBRARIES)
-
-if (MBEDTLS_CRYPTO_LIBRARY)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${MBEDTLS_INCLUDE_DIR}
- )
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${MBEDTLS_CRYPTO_LIBRARY}
- )
-endif (MBEDTLS_CRYPTO_LIBRARY)
+if (TARGET OpenSSL::Crypto)
+ list(APPEND LIBSSH_LINK_LIBRARIES OpenSSL::Crypto)
+endif ()
-if (GCRYPT_LIBRARIES)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${GCRYPT_INCLUDE_DIR}
- )
+if (TARGET MbedTLS::mbedcrypto)
+ list(APPEND LIBSSH_LINK_LIBRARIES MbedTLS::mbedcrypto)
+endif ()
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${GCRYPT_LIBRARIES})
-endif()
+if (TARGET libgcrypt::libgcrypt)
+ list(APPEND LIBSSH_LINK_LIBRARIES ${GCRYPT_LIBRARIES})
+endif ()
if (WITH_ZLIB)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${ZLIB_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${ZLIB_LIBRARY}
- )
+ list(APPEND LIBSSH_LINK_LIBRARIES ZLIB::ZLIB)
endif (WITH_ZLIB)
if (WITH_GSSAPI AND GSSAPI_FOUND)
@@ -86,11 +56,24 @@ if (MINGW AND Threads_FOUND)
)
endif()
-# This needs to be last for mingw to build
+if (HAVE_LIBFIDO2)
+ set(LIBSSH_PRIVATE_INCLUDE_DIRS
+ ${LIBSSH_PRIVATE_INCLUDE_DIRS}
+ ${LIBFIDO2_INCLUDE_DIR}
+ )
+
+ set(LIBSSH_LINK_LIBRARIES
+ ${LIBSSH_LINK_LIBRARIES}
+ ${LIBFIDO2_LIBRARIES}
+ )
+endif (HAVE_LIBFIDO2)
+
+# The ws2_32 needs to be last for mingw to build
# https://gitlab.com/libssh/libssh-mirror/-/issues/84
if (WIN32)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
+ iphlpapi
ws2_32
)
endif (WIN32)
@@ -116,10 +99,13 @@ set(libssh_SRCS
connector.c
crypto_common.c
curve25519.c
+ sntrup761.c
dh.c
ecdh.c
error.c
getpass.c
+ gzip.c
+ hybrid_mlkem.c
init.c
kdf.c
kex.c
@@ -130,12 +116,14 @@ set(libssh_SRCS
match.c
messages.c
misc.c
+ mlkem.c
options.c
packet.c
packet_cb.c
packet_crypt.c
pcap.c
pki.c
+ pki_context.c
pki_container_openssh.c
poll.c
session.c
@@ -143,6 +131,7 @@ set(libssh_SRCS
socket.c
string.c
threads.c
+ ttyopts.c
wrapper.c
external/bcrypt_pbkdf.c
external/blowfish.c
@@ -201,6 +190,20 @@ if (WITH_GCRYPT)
chachapoly.c
)
endif (NOT HAVE_GCRYPT_CHACHA_POLY)
+
+ if (HAVE_GCRYPT_CURVE25519)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ curve25519_gcrypt.c
+ )
+ endif(HAVE_GCRYPT_CURVE25519)
+
+ if (HAVE_GCRYPT_MLKEM)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ mlkem_gcrypt.c
+ )
+ endif (HAVE_GCRYPT_MLKEM)
elseif (WITH_MBEDTLS)
set(libssh_SRCS
${libssh_SRCS}
@@ -217,6 +220,7 @@ elseif (WITH_MBEDTLS)
external/fe25519.c
external/ge25519.c
external/sc25519.c
+ external/sntrup761.c
)
if (NOT (HAVE_MBEDTLS_CHACHA20_H AND HAVE_MBEDTLS_POLY1305_H))
set(libssh_SRCS
@@ -226,17 +230,24 @@ elseif (WITH_MBEDTLS)
chachapoly.c
)
endif()
-
+ if (HAVE_MBEDTLS_CURVE25519)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ curve25519_mbedcrypto.c
+ )
+ endif(HAVE_MBEDTLS_CURVE25519)
else (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
threads/libcrypto.c
pki_crypto.c
ecdh_crypto.c
+ curve25519_crypto.c
getrandom_crypto.c
md_crypto.c
libcrypto.c
dh_crypto.c
+ external/sntrup761.c
)
if (NOT HAVE_OPENSSL_EVP_CHACHA20)
set(libssh_SRCS
@@ -246,12 +257,20 @@ else (WITH_GCRYPT)
chachapoly.c
)
endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
+ if (HAVE_OPENSSL_MLKEM)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ mlkem_crypto.c
+ )
+ endif (HAVE_OPENSSL_MLKEM)
endif (WITH_GCRYPT)
if (WITH_SFTP)
set(libssh_SRCS
${libssh_SRCS}
sftp.c
+ sftp_common.c
+ sftp_aio.c
)
if (WITH_SERVER)
@@ -278,29 +297,52 @@ if (WITH_GEX)
)
endif (WITH_GEX)
-if (WITH_ZLIB)
- set(libssh_SRCS
- ${libssh_SRCS}
- gzip.c
- )
-endif(WITH_ZLIB)
-
if (WITH_GSSAPI AND GSSAPI_FOUND)
set(libssh_SRCS
${libssh_SRCS}
gssapi.c
+ kex-gss.c
)
endif (WITH_GSSAPI AND GSSAPI_FOUND)
if (NOT WITH_NACL)
- if (NOT HAVE_LIBCRYPTO)
+ if (NOT (HAVE_LIBCRYPTO OR HAVE_MBEDTLS_CURVE25519 OR HAVE_GCRYPT_CURVE25519))
set(libssh_SRCS
${libssh_SRCS}
+ curve25519_fallback.c
external/curve25519_ref.c
)
endif()
endif (NOT WITH_NACL)
+if (NOT HAVE_MLKEM1024)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ mlkem_native.c
+ external/libcrux_mlkem768_sha3.c
+ )
+ if (WITH_WERROR_DECLARATION_AFTER_STATEMENT_FLAG)
+ set_source_files_properties(external/libcrux_mlkem768_sha3.c
+ PROPERTIES
+ COMPILE_FLAGS -Wno-error=declaration-after-statement)
+ endif()
+endif()
+
+if (WITH_FIDO2)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ sk_common.c
+ pki_sk.c
+ )
+
+ if (HAVE_LIBFIDO2)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ sk_usbhid.c
+ )
+ endif (HAVE_LIBFIDO2)
+endif (WITH_FIDO2)
+
# Set the path to the default map file
set(MAP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.map")
@@ -344,6 +386,7 @@ endif ()
target_include_directories(ssh
PUBLIC
$
+ $
$
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
@@ -367,6 +410,8 @@ endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT)
set_target_properties(ssh
PROPERTIES
+ C_STANDARD
+ 99
VERSION
${LIBRARY_VERSION}
SOVERSION
@@ -383,6 +428,10 @@ if (MINGW)
target_link_libraries(ssh PRIVATE "-Wl,--enable-stdcall-fixup")
target_compile_definitions(ssh PRIVATE "_POSIX_SOURCE")
endif ()
+if (WITH_COVERAGE)
+ include(CodeCoverage)
+ append_coverage_compiler_flags_to_target(ssh)
+endif (WITH_COVERAGE)
install(TARGETS ssh
@@ -407,6 +456,7 @@ if (BUILD_STATIC_LIB)
target_include_directories(ssh-static
PUBLIC
$
+ $
$
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
target_link_libraries(ssh-static
@@ -434,6 +484,9 @@ if (BUILD_STATIC_LIB)
if (WIN32)
target_compile_definitions(ssh-static PUBLIC "LIBSSH_STATIC")
endif (WIN32)
+ if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(ssh-static)
+ endif (WITH_COVERAGE)
endif (BUILD_STATIC_LIB)
message(STATUS "Threads_FOUND=${Threads_FOUND}")
diff --git a/src/agent.c b/src/agent.c
index 1c79c6eb..bb1669c3 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -67,87 +67,94 @@
(((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
((x) == SSH2_AGENT_FAILURE))
-static uint32_t atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read) {
- char *b = buf;
- uint32_t pos = 0;
- ssize_t res;
- ssh_pollfd_t pfd;
- ssh_channel channel = agent->channel;
- socket_t fd;
-
- /* Using a socket ? */
- if (channel == NULL) {
- fd = ssh_socket_get_fd(agent->sock);
- pfd.fd = fd;
- pfd.events = do_read ? POLLIN : POLLOUT;
-
- while (n > pos) {
- if (do_read) {
- res = recv(fd, b + pos, n - pos, 0);
- } else {
- res = send(fd, b + pos, n - pos, 0);
- }
- switch (res) {
- case -1:
- if (errno == EINTR) {
- continue;
- }
+static uint32_t
+atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read)
+{
+ char *b = buf;
+ uint32_t pos = 0;
+ ssize_t res;
+ ssh_pollfd_t pfd;
+ ssh_channel channel = agent->channel;
+ socket_t fd;
+
+ /* Using a socket ? */
+ if (channel == NULL) {
+ fd = ssh_socket_get_fd(agent->sock);
+ pfd.fd = fd;
+ pfd.events = do_read ? POLLIN : POLLOUT;
+
+ while (n > pos) {
+ if (do_read) {
+ res = recv(fd, b + pos, n - pos, 0);
+ } else {
+ res = send(fd, b + pos, n - pos, 0);
+ }
+ switch (res) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
#ifdef EWOULDBLOCK
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
#else
- if (errno == EAGAIN) {
+ if (errno == EAGAIN) {
#endif
- (void) ssh_poll(&pfd, 1, -1);
- continue;
- }
- return 0;
- case 0:
- /* read returns 0 on end-of-file */
- errno = do_read ? 0 : EPIPE;
- return pos;
- default:
- pos += (uint32_t) res;
+ (void)ssh_poll(&pfd, 1, -1);
+ continue;
+ }
+ return 0;
+ case 0:
+ /* read returns 0 on end-of-file */
+ errno = do_read ? 0 : EPIPE;
+ return pos;
+ default:
+ pos += (uint32_t)res;
+ }
}
- }
- return pos;
+ return pos;
} else {
- /* using an SSH channel */
- while (n > pos){
- if (do_read)
- res = ssh_channel_read(channel,b + pos, n-pos, 0);
- else
- res = ssh_channel_write(channel, b+pos, n-pos);
- if (res == SSH_AGAIN)
- continue;
- if (res == SSH_ERROR)
- return 0;
- pos += (uint32_t)res;
- }
- return pos;
+ /* using an SSH channel */
+ while (n > pos) {
+ if (do_read) {
+ res = ssh_channel_read(channel, b + pos, n - pos, 0);
+ } else {
+ res = ssh_channel_write(channel, b + pos, n - pos);
+ }
+ if (res == SSH_AGAIN) {
+ continue;
+ }
+ if (res == SSH_ERROR) {
+ return 0;
+ }
+ pos += (uint32_t)res;
+ }
+ return pos;
}
}
-ssh_agent ssh_agent_new(struct ssh_session_struct *session) {
- ssh_agent agent = NULL;
+ssh_agent ssh_agent_new(struct ssh_session_struct *session)
+{
+ ssh_agent agent = NULL;
- agent = malloc(sizeof(struct ssh_agent_struct));
- if (agent == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(agent);
+ agent = calloc(1, sizeof(struct ssh_agent_struct));
+ if (agent == NULL) {
+ return NULL;
+ }
- agent->count = 0;
- agent->sock = ssh_socket_new(session);
- if (agent->sock == NULL) {
- SAFE_FREE(agent);
- return NULL;
- }
- agent->channel = NULL;
- return agent;
+ agent->count = 0;
+ agent->sock = ssh_socket_new(session);
+ if (agent->sock == NULL) {
+ SAFE_FREE(agent);
+ return NULL;
+ }
+ agent->channel = NULL;
+ return agent;
}
-static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channel){
- agent->channel = channel;
+static void agent_set_channel(struct ssh_agent_struct *agent,
+ ssh_channel channel)
+{
+ agent->channel = channel;
}
/**
@@ -168,15 +175,19 @@ static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channe
* @returns SSH_OK in case of success
* SSH_ERROR in case of an error
*/
-int ssh_set_agent_channel(ssh_session session, ssh_channel channel){
- if (!session)
- return SSH_ERROR;
- if (!session->agent){
- ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent");
- return SSH_ERROR;
- }
- agent_set_channel(session->agent, channel);
- return SSH_OK;
+int ssh_set_agent_channel(ssh_session session, ssh_channel channel)
+{
+ if (!session) {
+ return SSH_ERROR;
+ }
+ if (!session->agent) {
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Session has no active agent");
+ return SSH_ERROR;
+ }
+ agent_set_channel(session->agent, channel);
+ return SSH_OK;
}
/** @brief sets the SSH agent socket.
@@ -187,64 +198,71 @@ int ssh_set_agent_channel(ssh_session session, ssh_channel channel){
* @returns SSH_OK in case of success
* SSH_ERROR in case of an error
*/
-int ssh_set_agent_socket(ssh_session session, socket_t fd){
- if (!session)
- return SSH_ERROR;
- if (!session->agent){
- ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent");
- return SSH_ERROR;
- }
+int ssh_set_agent_socket(ssh_session session, socket_t fd)
+{
+ if (!session) {
+ return SSH_ERROR;
+ }
+ if (!session->agent) {
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Session has no active agent");
+ return SSH_ERROR;
+ }
- ssh_socket_set_fd(session->agent->sock, fd);
- return SSH_OK;
+ return ssh_socket_set_fd(session->agent->sock, fd);
}
/**
* @}
*/
-void ssh_agent_close(struct ssh_agent_struct *agent) {
- if (agent == NULL) {
- return;
- }
+void ssh_agent_close(struct ssh_agent_struct *agent)
+{
+ if (agent == NULL) {
+ return;
+ }
- ssh_socket_close(agent->sock);
+ ssh_socket_close(agent->sock);
}
-void ssh_agent_free(ssh_agent agent) {
- if (agent) {
- if (agent->ident) {
- SSH_BUFFER_FREE(agent->ident);
- }
- if (agent->sock) {
- ssh_agent_close(agent);
- ssh_socket_free(agent->sock);
+void ssh_agent_free(ssh_agent agent)
+{
+ if (agent) {
+ if (agent->ident) {
+ SSH_BUFFER_FREE(agent->ident);
+ }
+ if (agent->sock) {
+ ssh_agent_close(agent);
+ ssh_socket_free(agent->sock);
+ }
+ SAFE_FREE(agent);
}
- SAFE_FREE(agent);
- }
}
-static int agent_connect(ssh_session session) {
- const char *auth_sock = NULL;
+static int agent_connect(ssh_session session)
+{
+ const char *auth_sock = NULL;
- if (session == NULL || session->agent == NULL) {
- return -1;
- }
+ if (session == NULL || session->agent == NULL) {
+ return -1;
+ }
- if (session->agent->channel != NULL)
- return 0;
+ if (session->agent->channel != NULL) {
+ return 0;
+ }
- auth_sock = session->opts.agent_socket ?
- session->opts.agent_socket : getenv("SSH_AUTH_SOCK");
+ auth_sock = session->opts.agent_socket ? session->opts.agent_socket
+ : getenv("SSH_AUTH_SOCK");
- if (auth_sock && *auth_sock) {
- if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
- return -1;
+ if (auth_sock && *auth_sock) {
+ if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
+ return -1;
+ }
+ return 0;
}
- return 0;
- }
- return -1;
+ return -1;
}
#if 0
@@ -268,61 +286,66 @@ static int agent_decode_reply(struct ssh_session_struct *session, int type) {
#endif
static int agent_talk(struct ssh_session_struct *session,
- struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) {
- uint32_t len = 0;
- uint8_t tmpbuf[4];
- uint8_t *payload = tmpbuf;
- char err_msg[SSH_ERRNO_MSG_MAX] = {0};
-
- len = ssh_buffer_get_len(request);
- SSH_LOG(SSH_LOG_TRACE, "Request length: %" PRIu32, len);
- PUSH_BE_U32(payload, 0, len);
-
- /* send length and then the request packet */
- if (atomicio(session->agent, payload, 4, 0) == 4) {
- if (atomicio(session->agent, ssh_buffer_get(request), len, 0)
- != len) {
- SSH_LOG(SSH_LOG_TRACE, "atomicio sending request failed: %s",
- strerror(errno));
- return -1;
- }
- } else {
- SSH_LOG(SSH_LOG_TRACE,
- "atomicio sending request length failed: %s",
- ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
- return -1;
- }
+ struct ssh_buffer_struct *request,
+ struct ssh_buffer_struct *reply)
+{
+ uint32_t len = 0;
+ uint8_t tmpbuf[4];
+ uint8_t *payload = tmpbuf;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+
+ len = ssh_buffer_get_len(request);
+ SSH_LOG(SSH_LOG_TRACE, "Request length: %" PRIu32, len);
+ PUSH_BE_U32(payload, 0, len);
+
+ /* send length and then the request packet */
+ if (atomicio(session->agent, payload, 4, 0) == 4) {
+ if (atomicio(session->agent, ssh_buffer_get(request), len, 0) != len) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "atomicio sending request failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return -1;
+ }
+ } else {
+ SSH_LOG(SSH_LOG_TRACE,
+ "atomicio sending request length failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return -1;
+ }
- /* wait for response, read the length of the response packet */
- if (atomicio(session->agent, payload, 4, 1) != 4) {
- SSH_LOG(SSH_LOG_TRACE, "atomicio read response length failed: %s",
- strerror(errno));
- return -1;
- }
+ /* wait for response, read the length of the response packet */
+ if (atomicio(session->agent, payload, 4, 1) != 4) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "atomicio read response length failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return -1;
+ }
- len = PULL_BE_U32(payload, 0);
- if (len > 256 * 1024) {
- ssh_set_error(session, SSH_FATAL,
- "Authentication response too long: %" PRIu32, len);
- return -1;
- }
- SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
+ len = PULL_BE_U32(payload, 0);
+ if (len > 256 * 1024) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Authentication response too long: %" PRIu32,
+ len);
+ return -1;
+ }
+ SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
- payload = ssh_buffer_allocate(reply, len);
- if (payload == NULL) {
- SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
- return -1;
- }
+ payload = ssh_buffer_allocate(reply, len);
+ if (payload == NULL) {
+ SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
+ return -1;
+ }
- if (atomicio(session->agent, payload, len, 1) != len) {
- SSH_LOG(SSH_LOG_DEBUG,
- "Error reading response from authentication socket.");
- /* Rollback the unused space */
- ssh_buffer_pass_bytes_end(reply, len);
- return -1;
- }
+ if (atomicio(session->agent, payload, len, 1) != len) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Error reading response from authentication socket.");
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(reply, len);
+ return -1;
+ }
- return 0;
+ return 0;
}
uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
@@ -422,8 +445,9 @@ ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
/* caller has to free comment */
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
- char **comment) {
- struct ssh_key_struct *key;
+ char **comment)
+{
+ struct ssh_key_struct *key = NULL;
struct ssh_string_struct *blob = NULL;
struct ssh_string_struct *tmp = NULL;
int rc;
@@ -470,35 +494,37 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
return key;
}
-int ssh_agent_is_running(ssh_session session) {
- if (session == NULL || session->agent == NULL) {
- return 0;
- }
+int ssh_agent_is_running(ssh_session session)
+{
+ if (session == NULL || session->agent == NULL) {
+ return 0;
+ }
- if (ssh_socket_is_open(session->agent->sock)) {
- return 1;
- } else {
- if (agent_connect(session) < 0) {
- return 0;
+ if (ssh_socket_is_open(session->agent->sock)) {
+ return 1;
} else {
- return 1;
+ if (agent_connect(session) < 0) {
+ return 0;
+ } else {
+ return 1;
+ }
}
- }
- return 0;
+ return 0;
}
ssh_string ssh_agent_sign_data(ssh_session session,
const ssh_key pubkey,
struct ssh_buffer_struct *data)
{
- ssh_buffer request;
- ssh_buffer reply;
- ssh_string key_blob;
- ssh_string sig_blob;
+ ssh_buffer request = NULL;
+ ssh_buffer reply = NULL;
+ ssh_string key_blob = NULL;
+ ssh_string sig_blob = NULL;
unsigned int type = 0;
unsigned int flags = 0;
uint32_t dlen;
+ size_t request_len;
int rc;
request = ssh_buffer_new();
@@ -524,11 +550,14 @@ ssh_string ssh_agent_sign_data(ssh_session session,
* - 2 x uint32_t
* - 1 x ssh_string (uint8_t + data)
*/
- rc = ssh_buffer_allocate_size(request,
- sizeof(uint8_t) * 2 +
- sizeof(uint32_t) * 2 +
- ssh_string_len(key_blob));
+ request_len = sizeof(uint8_t) * 2 +
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(key_blob);
+ /* this can't overflow the uint32_t as the
+ * STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
+ rc = ssh_buffer_allocate_size(request, (uint32_t)request_len);
if (rc < 0) {
+ SSH_STRING_FREE(key_blob);
SSH_BUFFER_FREE(request);
return NULL;
}
diff --git a/src/auth.c b/src/auth.c
index c29a7106..8dae696d 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -32,19 +32,19 @@
#include
#endif
-#include "libssh/priv.h"
-#include "libssh/crypto.h"
-#include "libssh/ssh2.h"
-#include "libssh/buffer.h"
#include "libssh/agent.h"
-#include "libssh/misc.h"
-#include "libssh/packet.h"
-#include "libssh/session.h"
-#include "libssh/keys.h"
#include "libssh/auth.h"
-#include "libssh/pki.h"
+#include "libssh/buffer.h"
+#include "libssh/crypto.h"
#include "libssh/gssapi.h"
+#include "libssh/keys.h"
#include "libssh/legacy.h"
+#include "libssh/misc.h"
+#include "libssh/packet.h"
+#include "libssh/pki.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+#include "libssh/ssh2.h"
/**
* @defgroup libssh_auth The SSH authentication functions
@@ -88,6 +88,7 @@ static int ssh_auth_response_termination(void *user)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
+ case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -118,9 +119,14 @@ static const char *ssh_auth_get_current_method(ssh_session session)
case SSH_AUTH_METHOD_INTERACTIVE:
method = "keyboard interactive";
break;
+#ifdef WITH_GSSAPI
case SSH_AUTH_METHOD_GSSAPI_MIC:
method = "gssapi";
break;
+ case SSH_AUTH_METHOD_GSSAPI_KEYEX:
+ method = "gssapi-keyex";
+ break;
+#endif
default:
break;
}
@@ -175,6 +181,7 @@ static int ssh_userauth_get_response(ssh_session session)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
+ case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -195,8 +202,9 @@ static int ssh_userauth_get_response(ssh_session session)
*
* This banner should be shown to user prior to authentication
*/
-SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) {
- ssh_string banner;
+SSH_PACKET_CALLBACK(ssh_packet_userauth_banner)
+{
+ ssh_string banner = NULL;
(void)type;
(void)user;
@@ -268,9 +276,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (strstr(auth_methods, "hostbased") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_HOSTBASED;
}
+#ifdef WITH_GSSAPI
if (strstr(auth_methods, "gssapi-with-mic") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC;
}
+ if (strstr(auth_methods, "gssapi-keyex") != NULL) {
+ session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_KEYEX;
+ }
+#endif
end:
session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
@@ -462,6 +475,110 @@ int ssh_userauth_none(ssh_session session, const char *username)
return SSH_AUTH_ERROR;
}
+/**
+ * @internal
+ *
+ * @brief Adds the server's public key to the authentication request.
+ *
+ * This function is used internally when the hostbound public key authentication
+ * extension is enabled. It export the server's public key and adds it to the
+ * authentication buffer.
+ *
+ * @param[in] session The SSH session.
+ *
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
+ */
+static int add_hostbound_pubkey(ssh_session session)
+{
+ int rc;
+ ssh_string server_pubkey_s = NULL;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (session->current_crypto == NULL ||
+ session->current_crypto->server_pubkey == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Invalid session or server public key");
+ return SSH_ERROR;
+ }
+
+ rc = ssh_pki_export_pubkey_blob(session->current_crypto->server_pubkey,
+ &server_pubkey_s);
+ if (rc < 0) {
+ goto error;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_s);
+ if (rc < 0) {
+ goto error;
+ }
+
+error:
+ SSH_STRING_FREE(server_pubkey_s);
+ return rc;
+}
+
+/**
+ * @internal
+ *
+ * @brief Build a public key authentication request.
+ *
+ * This helper function creates a SSH2_MSG_USERAUTH_REQUEST message for public
+ * key authentication and adds the server's public key if the hostbound
+ * extension is enabled.
+ *
+ * @param[in] session The SSH session.
+ * @param[in] username The username, may be NULL.
+ * @param[in] auth_type Authentication type (0 for key offer, 1 for actual
+ * auth).
+ * @param[in] sig_type_c The signature algorithm name.
+ * @param[in] pubkey_s The public key string.
+ *
+ * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ */
+static int build_pubkey_auth_request(ssh_session session,
+ const char *username,
+ int has_signature,
+ const char *sig_type_c,
+ ssh_string pubkey_s)
+{
+ int rc;
+ const char *auth_method = "publickey";
+
+ if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
+ session->current_crypto->server_pubkey != NULL) {
+ auth_method = "publickey-hostbound-v00@openssh.com";
+ }
+
+ /* request */
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bsssbsS",
+ SSH2_MSG_USERAUTH_REQUEST,
+ username ? username : session->opts.username,
+ "ssh-connection",
+ auth_method,
+ has_signature, /* private key? */
+ sig_type_c, /* algo */
+ pubkey_s /* public key */
+ );
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
+ session->current_crypto->server_pubkey != NULL) {
+ rc = add_hostbound_pubkey(session);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
/**
* @brief Try to authenticate with the given public key.
*
@@ -508,29 +625,42 @@ int ssh_userauth_try_publickey(ssh_session session,
return SSH_AUTH_ERROR;
}
- switch(session->pending_call_state) {
- case SSH_PENDING_CALL_NONE:
- break;
- case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY:
- goto pending;
- default:
- ssh_set_error(session,
- SSH_FATAL,
- "Wrong state (%d) during pending SSH call",
- session->pending_call_state);
- return SSH_AUTH_ERROR;
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY:
+ goto pending;
+ default:
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Wrong state (%d) during pending SSH call",
+ session->pending_call_state);
+ return SSH_AUTH_ERROR;
+ }
+
+ /* Note, that this is intentionally before checking the signature type
+ * compatibility to make sure the possible EXT_INFO packet is processed,
+ * extensions recorded and the right signature type is used below
+ */
+ rc = ssh_userauth_request_service(session);
+ if (rc == SSH_AGAIN) {
+ return SSH_AUTH_AGAIN;
+ } else if (rc == SSH_ERROR) {
+ return SSH_AUTH_ERROR;
}
/* Check if the given public key algorithm is allowed */
sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
if (sig_type_c == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
rc = ssh_key_algorithm_allowed(session, sig_type_c);
if (!rc) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
@@ -538,39 +668,26 @@ int ssh_userauth_try_publickey(ssh_session session,
}
allowed = ssh_key_size_allowed(session, pubkey);
if (!allowed) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
- "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ "RSA_MIN_SIZE",
+ sig_type_c,
+ ssh_key_size(pubkey));
return SSH_AUTH_DENIED;
}
- rc = ssh_userauth_request_service(session);
- if (rc == SSH_AGAIN) {
- return SSH_AUTH_AGAIN;
- } else if (rc == SSH_ERROR) {
- return SSH_AUTH_ERROR;
- }
-
/* public key */
rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s);
if (rc < 0) {
goto fail;
}
- /* request */
- rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 0, /* private key ? */
- sig_type_c, /* algo */
- pubkey_s /* public key */
- );
+ SSH_LOG(SSH_LOG_TRACE, "Trying signature type %s", sig_type_c);
+ rc = build_pubkey_auth_request(session, username, 0, sig_type_c, pubkey_s);
if (rc < 0) {
goto fail;
}
-
SSH_STRING_FREE(pubkey_s);
session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
@@ -639,16 +756,28 @@ int ssh_userauth_publickey(ssh_session session,
return SSH_AUTH_ERROR;
}
- switch(session->pending_call_state) {
- case SSH_PENDING_CALL_NONE:
- break;
- case SSH_PENDING_CALL_AUTH_PUBKEY:
- goto pending;
- default:
- ssh_set_error(session,
- SSH_FATAL,
- "Bad call during pending SSH call in ssh_userauth_try_publickey");
- return SSH_AUTH_ERROR;
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_PUBKEY:
+ goto pending;
+ default:
+ ssh_set_error(
+ session,
+ SSH_FATAL,
+ "Bad call during pending SSH call in ssh_userauth_try_publickey");
+ return SSH_AUTH_ERROR;
+ }
+
+ /* Note, that this is intentionally before checking the signature type
+ * compatibility to make sure the possible EXT_INFO packet is processed,
+ * extensions recorded and the right signature type is used below
+ */
+ rc = ssh_userauth_request_service(session);
+ if (rc == SSH_AGAIN) {
+ return SSH_AUTH_AGAIN;
+ } else if (rc == SSH_ERROR) {
+ return SSH_AUTH_ERROR;
}
/* Cert auth requires presenting the cert type name (*-cert@openssh.com) */
@@ -657,13 +786,15 @@ int ssh_userauth_publickey(ssh_session session,
/* Check if the given public key algorithm is allowed */
sig_type_c = ssh_key_get_signature_algorithm(session, key_type);
if (sig_type_c == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
rc = ssh_key_algorithm_allowed(session, sig_type_c);
if (!rc) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
@@ -671,35 +802,23 @@ int ssh_userauth_publickey(ssh_session session,
}
allowed = ssh_key_size_allowed(session, privkey);
if (!allowed) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
- "RSA_MIN_SIZE", sig_type_c, ssh_key_size(privkey));
+ "RSA_MIN_SIZE",
+ sig_type_c,
+ ssh_key_size(privkey));
return SSH_AUTH_DENIED;
}
- rc = ssh_userauth_request_service(session);
- if (rc == SSH_AGAIN) {
- return SSH_AUTH_AGAIN;
- } else if (rc == SSH_ERROR) {
- return SSH_AUTH_ERROR;
- }
-
/* get public key or cert */
rc = ssh_pki_export_pubkey_blob(privkey, &str);
if (rc < 0) {
goto fail;
}
- /* request */
- rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 1, /* private key */
- sig_type_c, /* algo */
- str /* public key or cert */
- );
+ SSH_LOG(SSH_LOG_TRACE, "Sending signature type %s", sig_type_c);
+ rc = build_pubkey_auth_request(session, username, 1, sig_type_c, str);
if (rc < 0) {
goto fail;
}
@@ -754,18 +873,23 @@ static int ssh_userauth_agent_publickey(ssh_session session,
bool allowed;
int rc;
- switch(session->pending_call_state) {
- case SSH_PENDING_CALL_NONE:
- break;
- case SSH_PENDING_CALL_AUTH_AGENT:
- goto pending;
- default:
- ssh_set_error(session,
- SSH_FATAL,
- "Bad call during pending SSH call in ssh_userauth_try_publickey");
- return SSH_ERROR;
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_AGENT:
+ goto pending;
+ default:
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Bad call during pending SSH call in %s",
+ __func__);
+ return SSH_ERROR;
}
+ /* Note, that this is intentionally before checking the signature type
+ * compatibility to make sure the possible EXT_INFO packet is processed,
+ * extensions recorded and the right signature type is used below
+ */
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
return SSH_AUTH_AGAIN;
@@ -782,14 +906,16 @@ static int ssh_userauth_agent_publickey(ssh_session session,
/* Check if the given public key algorithm is allowed */
sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
if (sig_type_c == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"Invalid key type (unknown)");
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
rc = ssh_key_algorithm_allowed(session, sig_type_c);
if (!rc) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
@@ -798,27 +924,21 @@ static int ssh_userauth_agent_publickey(ssh_session session,
}
allowed = ssh_key_size_allowed(session, pubkey);
if (!allowed) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
- "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ "RSA_MIN_SIZE",
+ sig_type_c,
+ ssh_key_size(pubkey));
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
- /* request */
- rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 1, /* private key */
- sig_type_c, /* algo */
- pubkey_s /* public key */
- );
- SSH_STRING_FREE(pubkey_s);
+ rc = build_pubkey_auth_request(session, username, 1, sig_type_c, pubkey_s);
if (rc < 0) {
goto fail;
}
+ SSH_STRING_FREE(pubkey_s);
/* sign the buffer with the private key */
sig_blob = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey);
@@ -858,6 +978,7 @@ static int ssh_userauth_agent_publickey(ssh_session session,
enum ssh_agent_state_e {
SSH_AGENT_STATE_NONE = 0,
SSH_AGENT_STATE_PUBKEY,
+ SSH_AGENT_STATE_CERT,
SSH_AGENT_STATE_AUTH
};
@@ -875,7 +996,7 @@ void ssh_agent_state_free(void *data)
if (state) {
SSH_STRING_FREE_CHAR(state->comment);
ssh_key_free(state->pubkey);
- free (state);
+ free(state);
}
}
@@ -901,13 +1022,14 @@ void ssh_agent_state_free(void *data)
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
*/
-int ssh_userauth_agent(ssh_session session,
- const char *username)
+int ssh_userauth_agent(ssh_session session, const char *username)
{
int rc = SSH_AUTH_ERROR;
- struct ssh_agent_state_struct *state;
+ struct ssh_agent_state_struct *state = NULL;
ssh_key *configKeys = NULL;
+ ssh_key *configCerts = NULL;
size_t configKeysCount = 0;
+ size_t configCertsCount = 0;
size_t i;
if (session == NULL) {
@@ -919,13 +1041,12 @@ int ssh_userauth_agent(ssh_session session,
}
if (!session->agent_state) {
- session->agent_state = malloc(sizeof(struct ssh_agent_state_struct));
+ session->agent_state = calloc(1, sizeof(struct ssh_agent_state_struct));
if (!session->agent_state) {
ssh_set_error_oom(session);
return SSH_AUTH_ERROR;
}
- ZERO_STRUCTP(session->agent_state);
- session->agent_state->state=SSH_AGENT_STATE_NONE;
+ session->agent_state->state = SSH_AGENT_STATE_NONE;
}
state = session->agent_state;
@@ -943,22 +1064,29 @@ int ssh_userauth_agent(ssh_session session,
* is in there.
*/
size_t identityLen = ssh_list_count(session->opts.identity);
+ size_t certsLen = ssh_list_count(session->opts.certificate);
struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
- configKeys = malloc(identityLen * sizeof(configKeys[0]));
- if (!configKeys) {
+ configKeys = malloc(identityLen * sizeof(ssh_key));
+ configCerts = malloc((certsLen + identityLen) * sizeof(ssh_key));
+ if (configKeys == NULL || configCerts == NULL) {
+ free(configKeys);
+ free(configCerts);
ssh_set_error_oom(session);
return SSH_AUTH_ERROR;
}
while (it != NULL && configKeysCount < identityLen) {
const char *privkeyFile = it->data;
+ size_t certPathLen;
+ char *certFile = NULL;
+ ssh_key pubkey = NULL;
+ ssh_key cert = NULL;
/*
* Read the private key file listed in the config, but we're only
* interested in the public key. Don't try to decrypt private key.
*/
- ssh_key pubkey = NULL;
rc = ssh_pki_import_pubkey_file(privkeyFile, &pubkey);
if (rc == SSH_OK) {
configKeys[configKeysCount++] = pubkey;
@@ -966,9 +1094,7 @@ int ssh_userauth_agent(ssh_session session,
char *pubkeyFile = NULL;
size_t pubkeyPathLen = strlen(privkeyFile) + sizeof(".pub");
- if (pubkey) {
- SSH_KEY_FREE(pubkey);
- }
+ SSH_KEY_FREE(pubkey);
/*
* If we couldn't get the public key from the private key file,
@@ -982,13 +1108,47 @@ int ssh_userauth_agent(ssh_session session,
}
snprintf(pubkeyFile, pubkeyPathLen, "%s.pub", privkeyFile);
rc = ssh_pki_import_pubkey_file(pubkeyFile, &pubkey);
+ free(pubkeyFile);
if (rc == SSH_OK) {
configKeys[configKeysCount++] = pubkey;
} else if (pubkey) {
SSH_KEY_FREE(pubkey);
}
- free(pubkeyFile);
}
+ /* Now try to see if there is a certificate with default name
+ * do not merge it yet with the key as we need to try first the
+ * non-certified key */
+ certPathLen = strlen(privkeyFile) + sizeof("-cert.pub");
+ certFile = malloc(certPathLen);
+ if (!certFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(certFile, certPathLen, "%s-cert.pub", privkeyFile);
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ free(certFile);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ /* And now load separately-listed certificates. */
+ it = ssh_list_get_iterator(session->opts.certificate);
+ while (it != NULL && configCertsCount < certsLen + identityLen) {
+ const char *certFile = it->data;
+ ssh_key cert = NULL;
+
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
it = it->next;
}
}
@@ -996,13 +1156,26 @@ int ssh_userauth_agent(ssh_session session,
while (state->pubkey != NULL) {
if (state->state == SSH_AGENT_STATE_NONE) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying identity %s", state->comment);
+ "Trying identity %s",
+ state->comment);
if (session->opts.identities_only) {
/* Check if this key is one of the keys listed in the config */
bool found_key = false;
for (i = 0; i < configKeysCount; i++) {
- if (ssh_key_cmp(state->pubkey, configKeys[i],
- SSH_KEY_CMP_PUBLIC) == 0) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configKeys[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+ /* or in separate certificates */
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
found_key = true;
break;
}
@@ -1011,7 +1184,8 @@ int ssh_userauth_agent(ssh_session session,
if (!found_key) {
SSH_LOG(SSH_LOG_DEBUG,
"Identities only is enabled and identity %s was "
- "not listed in config, skipping", state->comment);
+ "not listed in config, skipping",
+ state->comment);
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
SSH_KEY_FREE(state->pubkey);
@@ -1026,28 +1200,48 @@ int ssh_userauth_agent(ssh_session session,
}
}
if (state->state == SSH_AGENT_STATE_NONE ||
- state->state == SSH_AGENT_STATE_PUBKEY) {
+ state->state == SSH_AGENT_STATE_PUBKEY ||
+ state->state == SSH_AGENT_STATE_CERT) {
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
if (rc == SSH_AUTH_ERROR) {
- ssh_agent_state_free (state);
+ ssh_agent_state_free(state);
session->agent_state = NULL;
goto done;
} else if (rc == SSH_AUTH_AGAIN) {
- state->state = SSH_AGENT_STATE_PUBKEY;
+ state->state = (state->state == SSH_AGENT_STATE_NONE ?
+ SSH_AGENT_STATE_PUBKEY : state->state);
goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s refused by server", state->comment);
+ "Public key of %s refused by server",
+ state->comment);
+ if (state->state == SSH_AGENT_STATE_PUBKEY) {
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Retry with matching certificate");
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_key_dup(configCerts[i]);
+ state->state = SSH_AGENT_STATE_CERT;
+ continue;
+ }
+ }
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
SSH_KEY_FREE(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s accepted by server", state->comment);
+ "Public key of %s accepted by server",
+ state->comment);
state->state = SSH_AGENT_STATE_AUTH;
}
if (state->state == SSH_AGENT_STATE_AUTH) {
@@ -1058,14 +1252,15 @@ int ssh_userauth_agent(ssh_session session,
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_PARTIAL) {
- ssh_agent_state_free (session->agent_state);
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
"Server accepted public key but refused the signature");
SSH_KEY_FREE(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
@@ -1083,6 +1278,10 @@ int ssh_userauth_agent(ssh_session session,
ssh_key_free(configKeys[i]);
}
free(configKeys);
+ for (i = 0; i < configCertsCount; i++) {
+ ssh_key_free(configCerts[i]);
+ }
+ free(configCerts);
return rc;
}
@@ -1090,6 +1289,8 @@ enum ssh_auth_auto_state_e {
SSH_AUTH_AUTO_STATE_NONE = 0,
SSH_AUTH_AUTO_STATE_PUBKEY,
SSH_AUTH_AUTO_STATE_KEY_IMPORTED,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION,
SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
};
@@ -1098,6 +1299,8 @@ struct ssh_auth_auto_state_struct {
struct ssh_iterator *it;
ssh_key privkey;
ssh_key pubkey;
+ ssh_key cert;
+ struct ssh_iterator *cert_it;
};
/**
@@ -1132,7 +1335,8 @@ int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
return SSH_ERROR;
}
- if (session->auth.auto_state != NULL && session->auth.auto_state->it != NULL) {
+ if (session->auth.auto_state != NULL &&
+ session->auth.auto_state->it != NULL) {
id = session->auth.auto_state->it->data;
}
@@ -1176,6 +1380,9 @@ int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
* @note Most server implementations do not permit changing the username during
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
+ *
+ * The OpenSSH iterates over the identities and first try the plain public key
+ * and then the certificate if it is in place.
*/
int ssh_userauth_publickey_auto(ssh_session session,
const char *username,
@@ -1183,12 +1390,17 @@ int ssh_userauth_publickey_auto(ssh_session session,
{
ssh_auth_callback auth_fn = NULL;
void *auth_data = NULL;
- struct ssh_auth_auto_state_struct *state;
+ struct ssh_auth_auto_state_struct *state = NULL;
int rc;
if (session == NULL) {
return SSH_AUTH_ERROR;
}
+
+ SSH_LOG(SSH_LOG_INFO,
+ "Starting authentication as a user %s",
+ username ? username : session->opts.username);
+
if (! (session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH)) {
session->auth.supported_methods &= ~SSH_AUTH_METHOD_PUBLICKEY;
return SSH_AUTH_DENIED;
@@ -1214,7 +1426,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
rc = ssh_userauth_agent(session, username);
if (rc == SSH_AUTH_SUCCESS ||
rc == SSH_AUTH_PARTIAL ||
- rc == SSH_AUTH_AGAIN ) {
+ rc == SSH_AUTH_AGAIN) {
return rc;
}
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
@@ -1229,7 +1441,9 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying to authenticate with %s", privkey_file);
+ "Trying to authenticate with %s",
+ privkey_file);
+ state->cert = NULL;
state->privkey = NULL;
state->pubkey = NULL;
@@ -1242,50 +1456,56 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (pub_uri_from_priv == NULL) {
return SSH_ERROR;
} else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s",
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s",
pub_uri_from_priv);
SAFE_FREE(pub_uri_from_priv);
}
} else
#endif /* WITH_PKCS11_URI */
{
- snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file);
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s.pub",
+ privkey_file);
}
rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to import public key: %s",
- pubkey_file);
+ SSH_FATAL,
+ "Failed to import public key: %s",
+ pubkey_file);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
} else if (rc == SSH_EOF) {
/* Read the private key and save the public key to file */
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
- state->it=state->it->next;
+ state->it = state->it->next;
continue;
}
- rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey);
+ rc = ssh_pki_export_privkey_to_pubkey(state->privkey,
+ &state->pubkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->privkey);
+ SSH_KEY_FREE(state->privkey);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
}
@@ -1299,29 +1519,102 @@ int ssh_userauth_publickey_auto(ssh_session session,
}
state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED;
}
- if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
- rc = ssh_userauth_try_publickey(session, username, state->pubkey);
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ ssh_key k = state->pubkey;
+ if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ k = state->cert;
+ }
+ rc = ssh_userauth_try_publickey(session, username, k);
if (rc == SSH_AUTH_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Public key authentication error for %s",
privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
return rc;
} else if (rc == SSH_AUTH_AGAIN) {
return rc;
} else if (rc != SSH_AUTH_SUCCESS) {
+ int r; /* do not reuse `rc` as it is used to return from here */
+ SSH_KEY_FREE(state->cert);
SSH_LOG(SSH_LOG_DEBUG,
- "Public key for %s refused by server",
- privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
- state->it=state->it->next;
+ "Public key for %s%s refused by server",
+ privkey_file,
+ (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED
+ ? " (with certificate)" : ""));
+ /* Try certificate file by appending -cert.pub (if present) */
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ char cert_file[PATH_MAX] = {0};
+ ssh_key cert = NULL;
+
+ snprintf(cert_file,
+ sizeof(cert_file),
+ "%s-cert.pub",
+ privkey_file);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (default path)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ /* TODO check the pubkey and certs match */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Certificate loaded %s. Retry the authentication.",
+ cert_file);
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE;
+ SSH_KEY_FREE(state->cert);
+ state->cert = cert;
+ /* try to authenticate with this certificate */
+ continue;
+ }
+ /* if the file does not exists, try configuration options */
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ }
+ /* Try certificate files loaded through options */
+ if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ SSH_KEY_FREE(state->cert);
+ if (state->cert_it == NULL) {
+ state->cert_it = ssh_list_get_iterator(session->opts.certificate);
+ }
+ while (state->cert_it != NULL) {
+ const char *cert_file = state->cert_it->data;
+ ssh_key cert = NULL;
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (options)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ int cmp = ssh_key_cmp(cert,
+ state->pubkey,
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ state->cert_it = state->cert_it->next;
+ SSH_KEY_FREE(cert);
+ continue; /* with next cert */
+ }
+ SSH_LOG(SSH_LOG_TRACE,
+ "Found matching certificate %s in options. Retry the authentication.",
+ cert_file);
+ state->cert = cert;
+ cert = NULL;
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ /* try to authenticate with this identity */
+ break; /* try this cert */
+ }
+ /* continue with next identity */
+ }
+ if (state->cert != NULL) {
+ continue; /* retry with the certificate */
+ }
+ }
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
}
@@ -1331,24 +1624,24 @@ int ssh_userauth_publickey_auto(ssh_session session,
/* Public key has been accepted by the server */
if (state->privkey == NULL) {
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->pubkey);
- state->pubkey=NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
@@ -1357,16 +1650,33 @@ int ssh_userauth_publickey_auto(ssh_session session,
continue;
}
}
+ if (state->cert != NULL && !is_cert_type(state->privkey->cert_type)) {
+ rc = ssh_pki_copy_cert_to_privkey(state->cert, state->privkey);
+ if (rc != SSH_OK) {
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to copy cert to private key");
+ state->it = state->it->next;
+ state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
+ continue;
+ }
+ }
rc = ssh_userauth_publickey(session, username, state->privkey);
if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) {
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ bool cert_used = (state->cert != NULL);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
if (rc == SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
- "Successfully authenticated using %s",
- privkey_file);
+ "Successfully authenticated using %s%s",
+ privkey_file,
+ (cert_used ? " and certificate" : ""));
}
return rc;
}
@@ -1374,8 +1684,9 @@ int ssh_userauth_publickey_auto(ssh_session session,
return rc;
}
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SSH_LOG(SSH_LOG_DEBUG,
"The server accepted the public key but refused the signature");
@@ -1491,7 +1802,7 @@ int ssh_userauth_agent_pubkey(ssh_session session,
const char *username,
ssh_public_key publickey)
{
- ssh_key key;
+ ssh_key key = NULL;
int rc;
key = ssh_key_new();
@@ -1502,18 +1813,22 @@ int ssh_userauth_agent_pubkey(ssh_session session,
key->type = publickey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PUBLIC;
-#ifndef HAVE_LIBCRYPTO
- key->rsa = publickey->rsa_pub;
-#else
+#if defined(HAVE_LIBMBEDCRYPTO)
+ key->pk = publickey->rsa_pub;
+#elif defined(HAVE_LIBCRYPTO)
key->key = publickey->key_pub;
+#else
+ key->rsa = publickey->rsa_pub;
#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_agent_publickey(session, username, key);
-#ifndef HAVE_LIBCRYPTO
- key->rsa = NULL;
-#else
+#if defined(HAVE_LIBMBEDCRYPTO)
+ key->pk = NULL;
+#elif defined(HAVE_LIBCRYPTO)
key->key = NULL;
+#else
+ key->rsa = NULL;
#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
@@ -1549,7 +1864,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
if (kbd->prompts) {
for (i = 0; i < n; i++) {
if (kbd->prompts[i] != NULL) {
- explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
+ ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
}
SAFE_FREE(kbd->prompts[i]);
}
@@ -1560,7 +1875,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
if (kbd->answers) {
for (i = 0; i < n; i++) {
if (kbd->answers[i] != NULL) {
- explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
+ ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
}
SAFE_FREE(kbd->answers[i]);
}
@@ -1585,7 +1900,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
n = kbd->nprompts;
if (kbd->prompts) {
for (i = 0; i < n; i++) {
- explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
+ ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
SAFE_FREE(kbd->prompts[i]);
}
SAFE_FREE(kbd->prompts);
@@ -1595,7 +1910,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
if (kbd->answers) {
for (i = 0; i < n; i++) {
- explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
+ ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
SAFE_FREE(kbd->answers[i]);
}
SAFE_FREE(kbd->answers);
@@ -2019,7 +2334,8 @@ int ssh_userauth_kbdint_getnanswers(ssh_session session)
*
* @param[in] i index The number of the ith answer.
*
- * @return 0 on success, < 0 on error.
+ * @return The answer string, or NULL if the answer is not
+ * available. Do not free the string.
*/
const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i)
{
@@ -2075,8 +2391,8 @@ ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
}
if (session->kbdint->answers[i]) {
- explicit_bzero(session->kbdint->answers[i],
- strlen(session->kbdint->answers[i]));
+ ssh_burn(session->kbdint->answers[i],
+ strlen(session->kbdint->answers[i]));
SAFE_FREE(session->kbdint->answers[i]);
}
@@ -2149,4 +2465,111 @@ int ssh_userauth_gssapi(ssh_session session)
return rc;
}
+/**
+ * @brief Try to authenticate through the "gssapi-keyex" method.
+ *
+ * @param[in] session The ssh session to use.
+ *
+ * @returns
+ * - `SSH_AUTH_ERROR`: A serious error happened.
+ * - `SSH_AUTH_DENIED`: Authentication failed : use another method.
+ * - `SSH_AUTH_PARTIAL`: You've been partially authenticated, you still
+ * have to use another method.
+ * - `SSH_AUTH_SUCCESS`: Authentication success.
+ * - `SSH_AUTH_AGAIN`: In nonblocking mode, you've got to call this again
+ * later.
+ */
+int ssh_userauth_gssapi_keyex(ssh_session session)
+{
+ int rc = SSH_AUTH_DENIED;
+#ifdef WITH_GSSAPI
+ OM_uint32 min_stat;
+ gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
+
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX:
+ goto pending;
+ default:
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Wrong state (%d) during pending SSH call",
+ session->pending_call_state);
+ return SSH_ERROR;
+ }
+
+ /* Check if GSSAPI Key exchange was performed */
+ if (!ssh_kex_is_gss(session->current_crypto)) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Attempt to authenticate with gssapi-keyex without "
+ "doing GSSAPI Key exchange.");
+ return SSH_ERROR;
+ }
+
+ if (session->gssapi == NULL) {
+ ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
+ return SSH_ERROR;
+ }
+
+ rc = ssh_userauth_request_service(session);
+ if (rc == SSH_AGAIN) {
+ return SSH_AUTH_AGAIN;
+ } else if (rc == SSH_ERROR) {
+ return SSH_AUTH_ERROR;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-keyex");
+
+ session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_KEYEX;
+ session->auth.state = SSH_AUTH_STATE_NONE;
+ session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX;
+
+ SAFE_FREE(session->gssapi->user);
+ session->gssapi->user = strdup(session->opts.username);
+ if (session->gssapi->user == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ rc = ssh_gssapi_auth_keyex_mic(session, &mic_token_buf);
+ if (rc != SSH_OK) {
+ session->auth.state = SSH_AUTH_STATE_NONE;
+ session->pending_call_state = SSH_PENDING_CALL_NONE;
+ return rc;
+ }
+
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bsssdP",
+ SSH2_MSG_USERAUTH_REQUEST,
+ session->opts.username,
+ "ssh-connection",
+ "gssapi-keyex",
+ mic_token_buf.length,
+ (size_t)mic_token_buf.length,
+ mic_token_buf.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ session->auth.state = SSH_AUTH_STATE_NONE;
+ session->pending_call_state = SSH_PENDING_CALL_NONE;
+ gss_release_buffer(&min_stat, &mic_token_buf);
+ return rc;
+ }
+
+ gss_release_buffer(&min_stat, &mic_token_buf);
+
+ session->auth.state = SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT;
+
+ ssh_packet_send(session);
+
+pending:
+ rc = ssh_userauth_get_response(session);
+ if (rc != SSH_AUTH_AGAIN) {
+ session->pending_call_state = SSH_PENDING_CALL_NONE;
+ }
+#else
+ (void)session; /* unused */
+#endif
+ return rc;
+}
+
/** @} */
diff --git a/src/base64.c b/src/base64.c
index 4148f49c..73dd0f77 100644
--- a/src/base64.c
+++ b/src/base64.c
@@ -29,6 +29,9 @@
#include "libssh/priv.h"
#include "libssh/buffer.h"
+/* Do not allow encoding more than 256MB of data */
+#define BASE64_MAX_INPUT_LEN 256 * 1024 * 1024
+
static
const uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
@@ -57,119 +60,120 @@ static int get_equals(char *string);
* @returns A buffer containing the decoded string, NULL if something went
* wrong (e.g. incorrect char).
*/
-ssh_buffer base64_to_bin(const char *source) {
- ssh_buffer buffer = NULL;
- unsigned char block[3];
- char *base64;
- char *ptr;
- size_t len;
- int equals;
-
- base64 = strdup(source);
- if (base64 == NULL) {
- return NULL;
- }
- ptr = base64;
+ssh_buffer base64_to_bin(const char *source)
+{
+ ssh_buffer buffer = NULL;
+ unsigned char block[3];
+ char *base64 = NULL;
+ char *ptr = NULL;
+ size_t len;
+ int equals;
+
+ base64 = strdup(source);
+ if (base64 == NULL) {
+ return NULL;
+ }
+ ptr = base64;
- /* Get the number of equals signs, which mirrors the padding */
- equals = get_equals(ptr);
- if (equals > 2) {
- SAFE_FREE(base64);
- return NULL;
- }
+ /* Get the number of equals signs, which mirrors the padding */
+ equals = get_equals(ptr);
+ if (equals > 2) {
+ SAFE_FREE(base64);
+ return NULL;
+ }
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- SAFE_FREE(base64);
- return NULL;
- }
- /*
- * The base64 buffer often contains sensitive data. Make sure we don't leak
- * sensitive data
- */
- ssh_buffer_set_secure(buffer);
-
- len = strlen(ptr);
- while (len > 4) {
- if (_base64_to_bin(block, ptr, 3) < 0) {
- goto error;
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ SAFE_FREE(base64);
+ return NULL;
}
- if (ssh_buffer_add_data(buffer, block, 3) < 0) {
- goto error;
+ /*
+ * The base64 buffer often contains sensitive data. Make sure we don't leak
+ * sensitive data
+ */
+ ssh_buffer_set_secure(buffer);
+
+ len = strlen(ptr);
+ while (len > 4) {
+ if (_base64_to_bin(block, ptr, 3) < 0) {
+ goto error;
+ }
+ if (ssh_buffer_add_data(buffer, block, 3) < 0) {
+ goto error;
+ }
+ len -= 4;
+ ptr += 4;
}
- len -= 4;
- ptr += 4;
- }
-
- /*
- * Depending on the number of bytes resting, there are 3 possibilities
- * from the RFC.
- */
- switch (len) {
+
+ /*
+ * Depending on the number of bytes resting, there are 3 possibilities
+ * from the RFC.
+ */
+ switch (len) {
/*
* (1) The final quantum of encoding input is an integral multiple of
* 24 bits. Here, the final unit of encoded output will be an integral
* multiple of 4 characters with no "=" padding
*/
case 4:
- if (equals != 0) {
- goto error;
- }
- if (_base64_to_bin(block, ptr, 3) < 0) {
- goto error;
- }
- if (ssh_buffer_add_data(buffer, block, 3) < 0) {
- goto error;
- }
- SAFE_FREE(base64);
+ if (equals != 0) {
+ goto error;
+ }
+ if (_base64_to_bin(block, ptr, 3) < 0) {
+ goto error;
+ }
+ if (ssh_buffer_add_data(buffer, block, 3) < 0) {
+ goto error;
+ }
+ SAFE_FREE(base64);
- return buffer;
+ return buffer;
/*
* (2) The final quantum of encoding input is exactly 8 bits; here, the
* final unit of encoded output will be two characters followed by
* two "=" padding characters.
*/
case 2:
- if (equals != 2){
- goto error;
- }
+ if (equals != 2) {
+ goto error;
+ }
- if (_base64_to_bin(block, ptr, 1) < 0) {
- goto error;
- }
- if (ssh_buffer_add_data(buffer, block, 1) < 0) {
- goto error;
- }
- SAFE_FREE(base64);
+ if (_base64_to_bin(block, ptr, 1) < 0) {
+ goto error;
+ }
+ if (ssh_buffer_add_data(buffer, block, 1) < 0) {
+ goto error;
+ }
+ SAFE_FREE(base64);
- return buffer;
+ return buffer;
/*
* The final quantum of encoding input is exactly 16 bits. Here, the final
* unit of encoded output will be three characters followed by one "="
* padding character.
*/
case 3:
- if (equals != 1) {
- goto error;
- }
- if (_base64_to_bin(block, ptr, 2) < 0) {
- goto error;
- }
- if (ssh_buffer_add_data(buffer,block,2) < 0) {
- goto error;
- }
- SAFE_FREE(base64);
+ if (equals != 1) {
+ goto error;
+ }
+ if (_base64_to_bin(block, ptr, 2) < 0) {
+ goto error;
+ }
+ if (ssh_buffer_add_data(buffer, block, 2) < 0) {
+ goto error;
+ }
+ SAFE_FREE(base64);
- return buffer;
+ return buffer;
default:
- /* 4,3,2 are the only padding size allowed */
- goto error;
- }
+ /* 4,3,2 are the only padding size allowed */
+ goto error;
+ }
error:
- SAFE_FREE(base64);
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ SAFE_FREE(base64);
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
}
#define BLOCK(letter, n) do {ptr = strchr((const char *)alphabet, source[n]); \
@@ -179,59 +183,62 @@ ssh_buffer base64_to_bin(const char *source) {
} while(0)
/* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */
-static int to_block4(unsigned long *block, const char *source, int num) {
- const char *ptr = NULL;
- unsigned int i;
+static int to_block4(unsigned long *block, const char *source, int num)
+{
+ const char *ptr = NULL;
+ size_t i;
- *block = 0;
- if (num < 1) {
- return 0;
- }
+ *block = 0;
+ if (num < 1) {
+ return 0;
+ }
- BLOCK(A, 0); /* 6 bit */
- BLOCK(B,1); /* 12 bit */
+ BLOCK(A, 0); /* 6 bit */
+ BLOCK(B, 1); /* 12 bit */
- if (num < 2) {
- return 0;
- }
+ if (num < 2) {
+ return 0;
+ }
- BLOCK(C, 2); /* 18 bit */
+ BLOCK(C, 2); /* 18 bit */
- if (num < 3) {
- return 0;
- }
+ if (num < 3) {
+ return 0;
+ }
- BLOCK(D, 3); /* 24 bit */
+ BLOCK(D, 3); /* 24 bit */
- return 0;
+ return 0;
}
/* num = numbers of final bytes to be decoded */
-static int _base64_to_bin(unsigned char dest[3], const char *source, int num) {
- unsigned long block;
+static int _base64_to_bin(unsigned char dest[3], const char *source, int num)
+{
+ unsigned long block;
- if (to_block4(&block, source, num) < 0) {
- return -1;
- }
- dest[0] = GET_A(block);
- dest[1] = GET_B(block);
- dest[2] = GET_C(block);
+ if (to_block4(&block, source, num) < 0) {
+ return -1;
+ }
+ dest[0] = GET_A(block);
+ dest[1] = GET_B(block);
+ dest[2] = GET_C(block);
- return 0;
+ return 0;
}
/* Count the number of "=" signs and replace them by zeroes */
-static int get_equals(char *string) {
- char *ptr = string;
- int num = 0;
+static int get_equals(char *string)
+{
+ char *ptr = string;
+ int num = 0;
- while ((ptr=strchr(ptr,'=')) != NULL) {
- num++;
- *ptr = '\0';
- ptr++;
- }
+ while ((ptr = strchr(ptr, '=')) != NULL) {
+ num++;
+ *ptr = '\0';
+ ptr++;
+ }
- return num;
+ return num;
}
/* thanks sysk for debugging my mess :) */
@@ -274,7 +281,15 @@ uint8_t *bin_to_base64(const uint8_t *source, size_t len)
{
uint8_t *base64 = NULL;
uint8_t *ptr = NULL;
- size_t flen = len + (3 - (len % 3)); /* round to upper 3 multiple */
+ size_t flen = 0;
+
+ /* Set the artificial upper limit for the input. Otherwise on 32b arch, the
+ * following line could overflow for sizes larger than SIZE_MAX / 4 */
+ if (len > BASE64_MAX_INPUT_LEN) {
+ return NULL;
+ }
+
+ flen = len + (3 - (len % 3)); /* round to upper 3 multiple */
flen = (4 * flen) / 3 + 1;
base64 = malloc(flen);
@@ -283,7 +298,7 @@ uint8_t *bin_to_base64(const uint8_t *source, size_t len)
}
ptr = base64;
- while(len > 0){
+ while (len > 0) {
_bin_to_base64(ptr, source, len > 3 ? 3 : len);
ptr += 4;
if (len < 3) {
diff --git a/src/bignum.c b/src/bignum.c
index bee55d67..b18c1162 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -27,40 +27,52 @@
#include "libssh/bignum.h"
#include "libssh/string.h"
-ssh_string ssh_make_bignum_string(bignum num) {
- ssh_string ptr = NULL;
- size_t pad = 0;
- size_t len = bignum_num_bytes(num);
- size_t bits = bignum_num_bits(num);
-
- if (len == 0) {
- return NULL;
- }
-
- /* If the first bit is set we have a negative number */
- if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) {
- pad++;
- }
+static ssh_string make_bignum_string(bignum num, size_t pad_to_len)
+{
+ ssh_string ptr = NULL;
+ size_t pad = 0;
+ size_t len = bignum_num_bytes(num);
+ size_t bits = bignum_num_bits(num);
+
+ if (pad_to_len == 0) {
+ /* If the first bit is set we have a negative number */
+ if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) {
+ pad++;
+ }
+ } else {
+ if (len > pad_to_len) {
+ return NULL;
+ }
+ pad = pad_to_len - len;
+ }
#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_TRACE,
- "%zu bits, %zu bytes, %zu padding",
- bits, len, pad);
+ SSH_LOG(SSH_LOG_TRACE, "%zu bits, %zu bytes, %zu padding", bits, len, pad);
#endif /* DEBUG_CRYPTO */
- ptr = ssh_string_new(len + pad);
- if (ptr == NULL) {
- return NULL;
- }
+ ptr = ssh_string_new(len + pad);
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ /* We have a negative number so we need a leading zero */
+ if (pad) {
+ memset(ptr->data, 0, pad);
+ }
+
+ bignum_bn2bin(num, len, ptr->data + pad);
- /* We have a negative number so we need a leading zero */
- if (pad) {
- ptr->data[0] = 0;
- }
+ return ptr;
+}
- bignum_bn2bin(num, len, ptr->data + pad);
+ssh_string ssh_make_bignum_string(bignum num)
+{
+ return make_bignum_string(num, 0);
+}
- return ptr;
+ssh_string ssh_make_padded_bignum_string(bignum num, size_t pad_len)
+{
+ return make_bignum_string(num, pad_len);
}
bignum ssh_make_string_bn(ssh_string string)
@@ -70,11 +82,12 @@ bignum ssh_make_string_bn(ssh_string string)
#ifdef DEBUG_CRYPTO
SSH_LOG(SSH_LOG_TRACE,
- "Importing a %zu bits, %zu bytes object ...\n",
- len * 8, len);
+ "Importing a %zu bits, %zu bytes object ...",
+ len * 8,
+ len);
#endif /* DEBUG_CRYPTO */
- bignum_bin2bn(string->data, len, &bn);
+ bignum_bin2bn(string->data, (int)len, &bn);
return bn;
}
@@ -86,7 +99,9 @@ void ssh_print_bignum(const char *name, const_bignum num)
if (num != NULL) {
bignum_bn2hex(num, &hex);
}
- SSH_LOG(SSH_LOG_DEBUG, "%s value: %s", name,
+ SSH_LOG(SSH_LOG_DEBUG,
+ "%s value: %s",
+ name,
(hex == NULL) ? "(null)" : (char *)hex);
ssh_crypto_free(hex);
}
diff --git a/src/bind.c b/src/bind.c
index d150933c..97feaac2 100644
--- a/src/bind.c
+++ b/src/bind.c
@@ -74,7 +74,7 @@
static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
int port) {
char port_c[6];
- struct addrinfo *ai;
+ struct addrinfo *ai = NULL;
struct addrinfo hints;
int opt = 1;
socket_t s;
@@ -132,8 +132,9 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
return s;
}
-ssh_bind ssh_bind_new(void) {
- ssh_bind ptr;
+ssh_bind ssh_bind_new(void)
+{
+ ssh_bind ptr = NULL;
ptr = calloc(1, sizeof(struct ssh_bind_struct));
if (ptr == NULL) {
@@ -149,14 +150,6 @@ ssh_bind ssh_bind_new(void) {
static int ssh_bind_import_keys(ssh_bind sshbind) {
int rc;
- if (sshbind->ecdsakey == NULL &&
- sshbind->rsakey == NULL &&
- sshbind->ed25519key == NULL) {
- ssh_set_error(sshbind, SSH_FATAL,
- "ECDSA, ED25519, or RSA host key file must be set");
- return SSH_ERROR;
- }
-
#ifdef HAVE_ECC
if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
@@ -225,90 +218,110 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
return SSH_OK;
}
-int ssh_bind_listen(ssh_bind sshbind) {
- const char *host;
- socket_t fd;
- int rc;
+int ssh_bind_listen(ssh_bind sshbind)
+{
+ const char *host = NULL;
+ socket_t fd;
+ int rc;
- if (sshbind->rsa == NULL &&
- sshbind->ecdsa == NULL &&
- sshbind->ed25519 == NULL) {
- rc = ssh_bind_import_keys(sshbind);
- if (rc != SSH_OK) {
- return SSH_ERROR;
- }
- }
+ /* Apply global bind configurations, if it hasn't been applied before */
+ rc = ssh_bind_options_parse_config(sshbind, NULL);
+ if (rc != 0) {
+ ssh_set_error(sshbind, SSH_FATAL, "Could not parse global config");
+ return SSH_ERROR;
+ }
- if (sshbind->bindfd == SSH_INVALID_SOCKET) {
- host = sshbind->bindaddr;
- if (host == NULL) {
- host = "0.0.0.0";
- }
+ /* Set default hostkey paths if no hostkey was found before */
+ if (sshbind->ecdsakey == NULL &&
+ sshbind->rsakey == NULL &&
+ sshbind->ed25519key == NULL) {
- fd = bind_socket(sshbind, host, sshbind->bindport);
- if (fd == SSH_INVALID_SOCKET) {
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
+ sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
+ sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
+ }
- if (listen(fd, 10) < 0) {
- char err_msg[SSH_ERRNO_MSG_MAX] = {0};
- ssh_set_error(sshbind, SSH_FATAL,
- "Listening to socket %d: %s",
- fd, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
- CLOSE_SOCKET(fd);
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ if (sshbind->rsa == NULL &&
+ sshbind->ecdsa == NULL &&
+ sshbind->ed25519 == NULL) {
+ rc = ssh_bind_import_keys(sshbind);
+ if (rc == SSH_ERROR) {
+ if (!sshbind->gssapi_key_exchange) {
+ ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
+ return SSH_ERROR;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "No usable hostkeys found: Using \"null\" hostkey algorithm");
+ }
+ }
- sshbind->bindfd = fd;
- } else {
- SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
- }
- return 0;
+ if (sshbind->bindfd == SSH_INVALID_SOCKET) {
+ host = sshbind->bindaddr;
+ if (host == NULL) {
+ host = "0.0.0.0";
+ }
+
+ fd = bind_socket(sshbind, host, sshbind->bindport);
+ if (fd == SSH_INVALID_SOCKET) {
+ return SSH_ERROR;
+ }
+
+ if (listen(fd, 10) < 0) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Listening to socket %d: %s",
+ fd,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
+ return SSH_ERROR;
+ }
+
+ sshbind->bindfd = fd;
+ } else {
+ SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
+ }
+ return 0;
}
-int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
- void *userdata){
- if (sshbind == NULL) {
- return SSH_ERROR;
- }
- if (callbacks == NULL) {
- ssh_set_error_invalid(sshbind);
- return SSH_ERROR;
- }
- if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
- ssh_set_error(sshbind,SSH_FATAL,
- "Invalid callback passed in (badly initialized)");
- return SSH_ERROR;
- }
- sshbind->bind_callbacks = callbacks;
- sshbind->bind_callbacks_userdata=userdata;
- return 0;
+int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata)
+{
+ if (sshbind == NULL) {
+ return SSH_ERROR;
+ }
+ if (callbacks == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return SSH_ERROR;
+ }
+ if (callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Invalid callback passed in (badly initialized)");
+ return SSH_ERROR;
+ }
+ sshbind->bind_callbacks = callbacks;
+ sshbind->bind_callbacks_userdata = userdata;
+ return 0;
}
/** @internal
* @brief callback being called by poll when an event happens
*
*/
-static int ssh_bind_poll_callback(ssh_poll_handle sshpoll,
- socket_t fd, int revents, void *user){
- ssh_bind sshbind=(ssh_bind)user;
- (void)sshpoll;
- (void)fd;
-
- if(revents & POLLIN){
- /* new incoming connection */
- if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){
- sshbind->bind_callbacks->incoming_connection(sshbind,
- sshbind->bind_callbacks_userdata);
+static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int revents, void *user)
+{
+ ssh_bind sshbind = (ssh_bind)user;
+ (void)sshpoll;
+ (void)fd;
+
+ if (revents & POLLIN) {
+ /* new incoming connection */
+ if (ssh_callbacks_exists(sshbind->bind_callbacks, incoming_connection)) {
+ sshbind->bind_callbacks->incoming_connection(sshbind,
+ sshbind->bind_callbacks_userdata);
+ }
}
- }
- return 0;
+ return 0;
}
/** @internal
@@ -336,20 +349,24 @@ ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
return sshbind->poll;
}
-void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) {
- sshbind->blocking = blocking ? 1 : 0;
+void ssh_bind_set_blocking(ssh_bind sshbind, int blocking)
+{
+ sshbind->blocking = blocking ? 1 : 0;
}
-socket_t ssh_bind_get_fd(ssh_bind sshbind) {
- return sshbind->bindfd;
+socket_t ssh_bind_get_fd(ssh_bind sshbind)
+{
+ return sshbind->bindfd;
}
-void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
- sshbind->bindfd = fd;
+void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd)
+{
+ sshbind->bindfd = fd;
}
-void ssh_bind_fd_toaccept(ssh_bind sshbind) {
- sshbind->toaccept = 1;
+void ssh_bind_fd_toaccept(ssh_bind sshbind)
+{
+ sshbind->toaccept = 1;
}
void ssh_bind_free(ssh_bind sshbind){
@@ -374,6 +391,7 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->ecdsakey);
SAFE_FREE(sshbind->ed25519key);
+ SAFE_FREE(sshbind->gssapi_key_exchange_algs);
ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
@@ -405,13 +423,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
return SSH_ERROR;
}
- /* Apply global bind configurations, if it hasn't been applied before */
- rc = ssh_bind_options_parse_config(sshbind, NULL);
- if (rc != 0) {
- ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
- return SSH_ERROR;
- }
-
session->server = 1;
/* Copy options from bind to session */
@@ -442,7 +453,7 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
return SSH_ERROR;
}
} else {
- char *p;
+ char *p = NULL;
/* If something was set to the session prior to calling this
* function, keep only what is allowed by the options set in
* sshbind */
@@ -458,18 +469,29 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
}
session->common.log_verbosity = sshbind->common.log_verbosity;
+ session->opts.gssapi_key_exchange = sshbind->gssapi_key_exchange;
+
+ if (sshbind->gssapi_key_exchange_algs != NULL) {
+ SAFE_FREE(session->opts.gssapi_key_exchange_algs);
+ session->opts.gssapi_key_exchange_algs =
+ strdup(sshbind->gssapi_key_exchange_algs);
+ if (session->opts.gssapi_key_exchange_algs == NULL) {
+ ssh_set_error_oom(sshbind);
+ return SSH_ERROR;
+ }
+ }
if (sshbind->banner != NULL) {
- session->opts.custombanner = strdup(sshbind->banner);
- if (session->opts.custombanner == NULL) {
+ session->server_opts.custombanner = strdup(sshbind->banner);
+ if (session->server_opts.custombanner == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
if (sshbind->moduli_file != NULL) {
- session->opts.moduli_file = strdup(sshbind->moduli_file);
- if (session->opts.moduli_file == NULL) {
+ session->server_opts.moduli_file = strdup(sshbind->moduli_file);
+ if (session->server_opts.moduli_file == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
@@ -484,7 +506,10 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
- ssh_socket_set_fd(session->socket, fd);
+ rc = ssh_socket_set_fd(session->socket, fd);
+ if (rc != SSH_OK) {
+ return rc;
+ }
handle = ssh_socket_get_poll_handle(session->socket);
if (handle == NULL) {
ssh_set_error_oom(sshbind);
@@ -501,8 +526,13 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
sshbind->ecdsa == NULL &&
sshbind->ed25519 == NULL) {
rc = ssh_bind_import_keys(sshbind);
- if (rc != SSH_OK) {
- return SSH_ERROR;
+ if (rc == SSH_ERROR) {
+ if (!sshbind->gssapi_key_exchange) {
+ ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
+ return SSH_ERROR;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "No usable hostkeys found: Using \"null\" hostkey algorithm");
}
}
diff --git a/src/bind_config.c b/src/bind_config.c
index a4c7a8d7..4f2b1c9b 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -104,6 +104,11 @@ ssh_bind_config_keyword_table[] = {
.opcode = BIND_CFG_HOSTKEY_ALGORITHMS,
.allowed_in_match = true
},
+ {
+ .name = "requiredrsasize",
+ .opcode = BIND_CFG_REQUIRED_RSA_SIZE,
+ .allowed_in_match = true
+ },
{
.opcode = BIND_CFG_UNKNOWN,
}
@@ -200,7 +205,7 @@ local_parse_file(ssh_bind bind,
uint8_t *seen,
unsigned int depth)
{
- FILE *f;
+ FILE *f = NULL;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
@@ -212,7 +217,7 @@ local_parse_file(ssh_bind bind,
return;
}
- f = fopen(filename, "r");
+ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
filename);
@@ -242,9 +247,14 @@ static void local_parse_glob(ssh_bind bind,
uint8_t *seen,
unsigned int depth)
{
+#ifdef __FILC__
+ /* FilC's libc provides glob_t without the non-standard gl_flags member. */
+ glob_t globbuf = {0};
+#else
glob_t globbuf = {
.gl_flags = 0,
};
+#endif
int rt;
u_int i;
@@ -293,6 +303,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *p = NULL;
char *s = NULL, *x = NULL;
char *keyword = NULL;
+ long l;
size_t len;
int rc = 0;
@@ -594,6 +605,19 @@ ssh_bind_config_parse_line(ssh_bind bind,
}
}
break;
+ case BIND_CFG_REQUIRED_RSA_SIZE:
+ l = ssh_config_get_long(&s, -1);
+ if (l >= 0 && l <= INT_MAX && (*parser_flags & PARSING)) {
+ int i = (int)l;
+ rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &i);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "line %d: Failed to set RequiredRSASize value '%ld'",
+ count,
+ l);
+ }
+ }
+ break;
case BIND_CFG_NOT_ALLOWED_IN_MATCH:
SSH_LOG(SSH_LOG_DEBUG, "Option not allowed in Match block: %s, line: %d",
keyword, count);
@@ -626,7 +650,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
{
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
- FILE *f;
+ FILE *f = NULL;
uint32_t parser_flags;
int rv;
@@ -636,7 +660,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
* option to be redefined later by another file. */
uint8_t seen[BIND_CFG_MAX] = {0};
- f = fopen(filename, "r");
+ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) {
return 0;
}
@@ -669,7 +693,8 @@ int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
{
char line[MAX_LINE_SIZE] = {0};
const char *c = input, *line_start = input;
- unsigned int line_num = 0, line_len;
+ unsigned int line_num = 0;
+ size_t line_len;
uint32_t parser_flags;
int rv;
@@ -698,8 +723,10 @@ int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
}
line_len = c - line_start;
if (line_len > MAX_LINE_SIZE - 1) {
- SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters",
- line_num, line_len);
+ SSH_LOG(SSH_LOG_WARN,
+ "Line %u too long: %zu characters",
+ line_num,
+ line_len);
return SSH_ERROR;
}
memcpy(line, line_start, line_len);
diff --git a/src/buffer.c b/src/buffer.c
index 299eac54..60119175 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -156,10 +156,10 @@ void ssh_buffer_free(struct ssh_buffer_struct *buffer)
if (buffer->secure && buffer->allocated > 0) {
/* burn the data */
- explicit_bzero(buffer->data, buffer->allocated);
+ ssh_burn(buffer->data, buffer->allocated);
SAFE_FREE(buffer->data);
- explicit_bzero(buffer, sizeof(struct ssh_buffer_struct));
+ ssh_burn(buffer, sizeof(struct ssh_buffer_struct));
} else {
SAFE_FREE(buffer->data);
}
@@ -205,7 +205,7 @@ static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
return -1;
}
memcpy(new, buffer->data, buffer->used);
- explicit_bzero(buffer->data, buffer->used);
+ ssh_burn(buffer->data, buffer->used);
SAFE_FREE(buffer->data);
} else {
new = realloc(buffer->data, needed);
@@ -241,7 +241,7 @@ static void buffer_shift(ssh_buffer buffer)
if (buffer->secure) {
void *ptr = buffer->data + buffer->used;
- explicit_bzero(ptr, burn_pos);
+ ssh_burn(ptr, burn_pos);
}
buffer_verify(buffer);
@@ -266,7 +266,7 @@ int ssh_buffer_reinit(struct ssh_buffer_struct *buffer)
buffer_verify(buffer);
if (buffer->secure && buffer->allocated > 0) {
- explicit_bzero(buffer->data, buffer->allocated);
+ ssh_burn(buffer->data, buffer->allocated);
}
buffer->used = 0;
buffer->pos = 0;
@@ -371,7 +371,8 @@ int ssh_buffer_allocate_size(struct ssh_buffer_struct *buffer,
*/
void *ssh_buffer_allocate(struct ssh_buffer_struct *buffer, uint32_t len)
{
- void *ptr;
+ void *ptr = NULL;
+
buffer_verify(buffer);
if (buffer->used + len < len) {
@@ -406,20 +407,26 @@ void *ssh_buffer_allocate(struct ssh_buffer_struct *buffer, uint32_t len)
*
* @return 0 on success, < 0 on error.
*/
-int ssh_buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
- struct ssh_string_struct *string) {
- uint32_t len = 0;
+int
+ssh_buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
+ struct ssh_string_struct *string)
+{
+ size_t len;
+ int rc;
- if (string == NULL) {
- return -1;
- }
+ if (string == NULL) {
+ return -1;
+ }
- len = ssh_string_len(string);
- if (ssh_buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) {
- return -1;
- }
+ len = ssh_string_len(string) + sizeof(uint32_t);
+ /* this can't overflow the uint32_t as the
+ * STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
+ rc = ssh_buffer_add_data(buffer, string, (uint32_t)len);
+ if (rc < 0) {
+ return -1;
+ }
- return 0;
+ return 0;
}
/**
@@ -610,6 +617,53 @@ uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
return buffer->used - buffer->pos;
}
+/**
+ * @internal
+ *
+ * @brief Duplicate an existing buffer.
+ *
+ * Creates a new ssh_buffer and copies all data from the source buffer.
+ * The new buffer preserves the secure flag setting of the source.
+ *
+ * @param[in] buffer The buffer to duplicate. Can be NULL.
+ *
+ * @return A new buffer containing a copy of the data on success,
+ * NULL on failure or if buffer is NULL.
+ *
+ * @see ssh_buffer_free()
+ */
+ssh_buffer ssh_buffer_dup(const ssh_buffer buffer)
+{
+ ssh_buffer new_buffer = NULL;
+ int rc;
+
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ buffer_verify(buffer);
+
+ new_buffer = ssh_buffer_new();
+ if (new_buffer == NULL) {
+ return NULL;
+ }
+
+ new_buffer->secure = buffer->secure;
+
+ if (ssh_buffer_get_len(buffer) > 0) {
+ rc = ssh_buffer_add_data(new_buffer,
+ ssh_buffer_get(buffer),
+ ssh_buffer_get_len(buffer));
+ if (rc != SSH_OK) {
+ ssh_buffer_free(new_buffer);
+ return NULL;
+ }
+ }
+
+ buffer_verify(new_buffer);
+ return new_buffer;
+}
+
/**
* @internal
*
@@ -822,6 +876,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
const char *p = NULL;
ssh_string string = NULL;
char *cstring = NULL;
+ bignum b = NULL;
size_t needed_size = 0;
size_t len;
size_t count;
@@ -852,7 +907,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
break;
case 'S':
string = va_arg(ap, ssh_string);
- needed_size += 4 + ssh_string_len(string);
+ needed_size += sizeof(uint32_t) + ssh_string_len(string);
string = NULL;
break;
case 's':
@@ -866,13 +921,18 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
va_arg(ap, void *);
count++; /* increase argument count */
break;
+ case 'F':
case 'B':
- va_arg(ap, bignum);
- /*
- * Use a fixed size for a bignum
- * (they should normally be around 32)
- */
- needed_size += 64;
+ b = va_arg(ap, bignum);
+ if (*p == 'F') {
+ /* For padded bignum, we know the exact length */
+ len = va_arg(ap, size_t);
+ count++; /* increase argument count */
+ needed_size += sizeof(uint32_t) + len;
+ } else {
+ /* The bignum bytes + 1 for possible padding */
+ needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
+ }
break;
case 't':
cstring = va_arg(ap, char *);
@@ -919,13 +979,14 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
* SSH_ERROR on error
* @see ssh_buffer_add_format() for format list values.
*/
-int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
- const char *format,
- size_t argc,
- va_list ap)
+static int
+ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
+ const char *format,
+ size_t argc,
+ va_list ap)
{
int rc = SSH_ERROR;
- const char *p;
+ const char *p = NULL;
union {
uint8_t byte;
uint16_t word;
@@ -934,7 +995,7 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
ssh_string string;
void *data;
} o;
- char *cstring;
+ char *cstring = NULL;
bignum b;
size_t len;
size_t count;
@@ -977,24 +1038,40 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
case 's':
cstring = va_arg(ap, char *);
len = strlen(cstring);
- rc = ssh_buffer_add_u32(buffer, htonl(len));
+ if (len > UINT32_MAX) {
+ rc = SSH_ERROR;
+ break;
+ }
+ o.dword = (uint32_t)len;
+ rc = ssh_buffer_add_u32(buffer, htonl(o.dword));
if (rc == SSH_OK){
- rc = ssh_buffer_add_data(buffer, cstring, len);
+ rc = ssh_buffer_add_data(buffer, cstring, o.dword);
}
cstring = NULL;
break;
case 'P':
len = va_arg(ap, size_t);
+ if (len > UINT32_MAX) {
+ rc = SSH_ERROR;
+ break;
+ }
o.data = va_arg(ap, void *);
count++; /* increase argument count */
- rc = ssh_buffer_add_data(buffer, o.data, len);
+ rc = ssh_buffer_add_data(buffer, o.data, (uint32_t)len);
o.data = NULL;
break;
+ case 'F':
case 'B':
b = va_arg(ap, bignum);
- o.string = ssh_make_bignum_string(b);
+ if (*p == 'F') {
+ len = va_arg(ap, size_t);
+ count++; /* increase argument count */
+ o.string = ssh_make_padded_bignum_string(b, len);
+ } else {
+ o.string = ssh_make_bignum_string(b);
+ }
if(o.string == NULL){
rc = SSH_ERROR;
break;
@@ -1005,7 +1082,11 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
case 't':
cstring = va_arg(ap, char *);
len = strlen(cstring);
- rc = ssh_buffer_add_data(buffer, cstring, len);
+ if (len > UINT32_MAX) {
+ rc = SSH_ERROR;
+ break;
+ }
+ rc = ssh_buffer_add_data(buffer, cstring, (uint32_t)len);
cstring = NULL;
break;
default:
@@ -1046,6 +1127,8 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
* 'P': size_t, void * (len of data, pointer to data)
* only pushes data.
* 'B': bignum (pushed as SSH string)
+ * 'F': bignum, size_t (bignum, padded to fixed length,
+ * pushed as SSH string)
* @returns SSH_OK on success
* SSH_ERROR on error
* @warning when using 'P' with a constant size (e.g. 8), do not
@@ -1093,7 +1176,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
va_list ap)
{
int rc = SSH_ERROR;
- const char *p = format, *last;
+ const char *p = format, *last = NULL;
union {
uint8_t *byte;
uint16_t *word;
@@ -1185,28 +1268,28 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
if (rlen != 4){
break;
}
- len = ntohl(u32len);
- if (len > max_len - 1) {
+ u32len = ntohl(u32len);
+ if (u32len > max_len - 1) {
break;
}
- rc = ssh_buffer_validate_length(buffer, len);
+ rc = ssh_buffer_validate_length(buffer, u32len);
if (rc != SSH_OK) {
break;
}
- *o.cstring = malloc(len + 1);
+ *o.cstring = malloc(u32len + 1);
if (*o.cstring == NULL){
rc = SSH_ERROR;
break;
}
- rlen = ssh_buffer_get_data(buffer, *o.cstring, len);
- if (rlen != len){
+ rlen = ssh_buffer_get_data(buffer, *o.cstring, u32len);
+ if (rlen != u32len) {
SAFE_FREE(*o.cstring);
rc = SSH_ERROR;
break;
}
- (*o.cstring)[len] = '\0';
+ (*o.cstring)[u32len] = '\0';
o.cstring = NULL;
rc = SSH_OK;
break;
@@ -1231,7 +1314,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
rc = SSH_ERROR;
break;
}
- rlen = ssh_buffer_get_data(buffer, *o.data, len);
+ rlen = ssh_buffer_get_data(buffer, *o.data, (uint32_t)len);
if (rlen != len){
SAFE_FREE(*o.data);
rc = SSH_ERROR;
@@ -1269,28 +1352,28 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
case 'b':
o.byte = va_arg(ap_copy, uint8_t *);
if (buffer->secure) {
- explicit_bzero(o.byte, sizeof(uint8_t));
+ ssh_burn(o.byte, sizeof(uint8_t));
break;
}
break;
case 'w':
o.word = va_arg(ap_copy, uint16_t *);
if (buffer->secure) {
- explicit_bzero(o.word, sizeof(uint16_t));
+ ssh_burn(o.word, sizeof(uint16_t));
break;
}
break;
case 'd':
o.dword = va_arg(ap_copy, uint32_t *);
if (buffer->secure) {
- explicit_bzero(o.dword, sizeof(uint32_t));
+ ssh_burn(o.dword, sizeof(uint32_t));
break;
}
break;
case 'q':
o.qword = va_arg(ap_copy, uint64_t *);
if (buffer->secure) {
- explicit_bzero(o.qword, sizeof(uint64_t));
+ ssh_burn(o.qword, sizeof(uint64_t));
break;
}
break;
@@ -1308,7 +1391,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
case 's':
o.cstring = va_arg(ap_copy, char **);
if (buffer->secure) {
- explicit_bzero(*o.cstring, strlen(*o.cstring));
+ ssh_burn(*o.cstring, strlen(*o.cstring));
}
SAFE_FREE(*o.cstring);
break;
@@ -1316,7 +1399,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
len = va_arg(ap_copy, size_t);
o.data = va_arg(ap_copy, void **);
if (buffer->secure) {
- explicit_bzero(*o.data, len);
+ ssh_burn(*o.data, len);
}
SAFE_FREE(*o.data);
break;
diff --git a/src/callbacks.c b/src/callbacks.c
index 3ed2f11c..94ea729e 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -24,11 +24,11 @@
#include "config.h"
#include "libssh/callbacks.h"
-#include "libssh/session.h"
#include "libssh/misc.h"
+#include "libssh/session.h"
#define is_callback_valid(session, cb) \
- (cb->size <= 0 || cb->size > 1024 * sizeof(void *))
+ (cb->size > 0 || cb->size <= 1024 * sizeof(void *))
/* LEGACY */
static void ssh_legacy_log_callback(int priority,
@@ -45,26 +45,35 @@ static void ssh_legacy_log_callback(int priority,
log_fn(session, priority, buffer, log_data);
}
-int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) {
- if (session == NULL || cb == NULL) {
- return SSH_ERROR;
- }
-
- if (is_callback_valid(session, cb)) {
- ssh_set_error(session,
- SSH_FATAL,
- "Invalid callback passed in (badly initialized)");
- return SSH_ERROR;
- };
- session->common.callbacks = cb;
-
- /* LEGACY */
- if (ssh_get_log_callback() == NULL && cb->log_function) {
- ssh_set_log_callback(ssh_legacy_log_callback);
- ssh_set_log_userdata(session);
- }
-
- return 0;
+void _ssh_remove_legacy_log_cb(void)
+{
+ if (ssh_get_log_callback() == ssh_legacy_log_callback) {
+ _ssh_reset_log_cb();
+ ssh_set_log_userdata(NULL);
+ }
+}
+
+int ssh_set_callbacks(ssh_session session, ssh_callbacks cb)
+{
+ if (session == NULL || cb == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (!is_callback_valid(session, cb)) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Invalid callback passed in (badly initialized)");
+ return SSH_ERROR;
+ };
+ session->common.callbacks = cb;
+
+ /* LEGACY */
+ if (ssh_get_log_callback() == NULL && cb->log_function) {
+ ssh_set_log_callback(ssh_legacy_log_callback);
+ ssh_set_log_userdata(session);
+ }
+
+ return 0;
}
static int ssh_add_set_channel_callbacks(ssh_channel channel,
@@ -75,11 +84,11 @@ static int ssh_add_set_channel_callbacks(ssh_channel channel,
int rc;
if (channel == NULL || cb == NULL) {
- return SSH_ERROR;
+ return SSH_ERROR;
}
session = channel->session;
- if (is_callback_valid(session, cb)) {
+ if (!is_callback_valid(session, cb)) {
ssh_set_error(session,
SSH_FATAL,
"Invalid callback passed in (badly initialized)");
@@ -87,7 +96,7 @@ static int ssh_add_set_channel_callbacks(ssh_channel channel,
};
if (channel->callbacks == NULL) {
channel->callbacks = ssh_list_new();
- if (channel->callbacks == NULL){
+ if (channel->callbacks == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
@@ -113,14 +122,14 @@ int ssh_add_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
int ssh_remove_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
{
- struct ssh_iterator *it;
+ struct ssh_iterator *it = NULL;
- if (channel == NULL || channel->callbacks == NULL){
+ if (channel == NULL || channel->callbacks == NULL) {
return SSH_ERROR;
}
it = ssh_list_find(channel->callbacks, cb);
- if (it == NULL){
+ if (it == NULL) {
return SSH_ERROR;
}
@@ -129,19 +138,19 @@ int ssh_remove_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
return SSH_OK;
}
+int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb)
+{
+ if (session == NULL || cb == NULL) {
+ return SSH_ERROR;
+ }
-int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){
- if (session == NULL || cb == NULL) {
- return SSH_ERROR;
- }
-
- if (is_callback_valid(session, cb)) {
+ if (!is_callback_valid(session, cb)) {
ssh_set_error(session,
SSH_FATAL,
"Invalid callback passed in (badly initialized)");
return SSH_ERROR;
};
- session->server_callbacks = cb;
+ session->server_callbacks = cb;
- return 0;
+ return 0;
}
diff --git a/src/chachapoly.c b/src/chachapoly.c
index 2cd23854..354a0d26 100644
--- a/src/chachapoly.c
+++ b/src/chachapoly.c
@@ -42,7 +42,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
void *key,
void *IV)
{
- struct chacha20_poly1305_keysched *sched;
+ struct chacha20_poly1305_keysched *sched = NULL;
uint8_t *u8key = key;
(void)IV;
diff --git a/src/channels.c b/src/channels.c
index b54af3bb..e9f57f88 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -52,16 +52,19 @@
#include "libssh/server.h"
#endif
-#define WINDOWBASE 1280000
-#define WINDOWLIMIT (WINDOWBASE/2)
-
/*
* All implementations MUST be able to process packets with an
* uncompressed payload length of 32768 bytes or less and a total packet
* size of 35000 bytes or less.
*/
#define CHANNEL_MAX_PACKET 32768
-#define CHANNEL_INITIAL_WINDOW 64000
+
+/*
+ * WINDOW_DEFAULT matches the default OpenSSH session window size.
+ * This controls how much data the peer can send before needing to receive
+ * a round-trip SSH2_MSG_CHANNEL_WINDOW_ADJUST message that increases the window.
+ */
+#define WINDOW_DEFAULT (64*CHANNEL_MAX_PACKET)
/**
* @defgroup libssh_channel The SSH channel functions
@@ -119,7 +122,7 @@ ssh_channel ssh_channel_new(ssh_session session)
}
channel->session = session;
- channel->exit_status = -1;
+ channel->exit.code = (uint32_t)-1;
channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND;
if (session->channels == NULL) {
@@ -163,68 +166,72 @@ uint32_t ssh_channel_new_id(ssh_session session)
*
* Constructs the channel object.
*/
-SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
- uint32_t channelid=0;
- ssh_channel channel;
- int rc;
- (void)type;
- (void)user;
+SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf)
+{
+ uint32_t channelid = 0;
+ ssh_channel channel = NULL;
+ int rc;
+ (void)type;
+ (void)user;
- SSH_LOG(SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");
- rc = ssh_buffer_unpack(packet, "d", &channelid);
- if (rc != SSH_OK)
- goto error;
- channel=ssh_channel_from_local(session,channelid);
- if(channel==NULL){
- ssh_set_error(session, SSH_FATAL,
- "Unknown channel id %" PRIu32,
- (uint32_t) channelid);
- /* TODO: Set error marking in channel object */
+ rc = ssh_buffer_unpack(packet, "d", &channelid);
+ if (rc != SSH_OK)
+ goto error;
+ channel = ssh_channel_from_local(session, channelid);
+ if (channel == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Unknown channel id %" PRIu32,
+ (uint32_t)channelid);
+ /* TODO: Set error marking in channel object */
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- rc = ssh_buffer_unpack(packet, "ddd",
- &channel->remote_channel,
- &channel->remote_window,
- &channel->remote_maxpacket);
- if (rc != SSH_OK)
- goto error;
+ rc = ssh_buffer_unpack(packet,
+ "ddd",
+ &channel->remote_channel,
+ &channel->remote_window,
+ &channel->remote_maxpacket);
+ if (rc != SSH_OK)
+ goto error;
- SSH_LOG(SSH_LOG_DEBUG,
- "Received a CHANNEL_OPEN_CONFIRMATION for channel %" PRIu32 ":%" PRIu32,
- channel->local_channel,
- channel->remote_channel);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Received a CHANNEL_OPEN_CONFIRMATION for channel %" PRIu32
+ ":%" PRIu32,
+ channel->local_channel,
+ channel->remote_channel);
- if (channel->state != SSH_CHANNEL_STATE_OPENING) {
- SSH_LOG(SSH_LOG_RARE,
- "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION received in incorrect "
- "channel state %d",
- channel->state);
- goto error;
- }
+ if (channel->state != SSH_CHANNEL_STATE_OPENING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION received in incorrect "
+ "channel state %d",
+ channel->state);
+ goto error;
+ }
- SSH_LOG(SSH_LOG_DEBUG,
- "Remote window : %" PRIu32 ", maxpacket : %" PRIu32,
- channel->remote_window,
- channel->remote_maxpacket);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window : %" PRIu32 ", maxpacket : %" PRIu32,
+ channel->remote_window,
+ channel->remote_maxpacket);
- channel->state = SSH_CHANNEL_STATE_OPEN;
- channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND;
+ channel->state = SSH_CHANNEL_STATE_OPEN;
+ channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND;
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_open_response_function,
- channel->session,
- channel,
- true /* is_success */);
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ true /* is_success */);
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
error:
- ssh_set_error(session, SSH_FATAL, "Invalid packet");
- return SSH_PACKET_USED;
+ ssh_set_error(session, SSH_FATAL, "Invalid packet");
+ return SSH_PACKET_USED;
}
/**
@@ -232,52 +239,54 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
*
* @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel.
*/
-SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
+SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail)
+{
+ ssh_channel channel = NULL;
+ char *error = NULL;
+ uint32_t code;
+ int rc;
+ (void)user;
+ (void)type;
- ssh_channel channel;
- char *error = NULL;
- uint32_t code;
- int rc;
- (void)user;
- (void)type;
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_RARE, "Invalid channel in packet");
+ return SSH_PACKET_USED;
+ }
- channel=channel_from_msg(session,packet);
- if(channel==NULL){
- SSH_LOG(SSH_LOG_RARE,"Invalid channel in packet");
- return SSH_PACKET_USED;
- }
+ rc = ssh_buffer_unpack(packet, "ds", &code, &error);
+ if (rc != SSH_OK) {
+ ssh_set_error(session, SSH_FATAL, "Invalid packet");
+ return SSH_PACKET_USED;
+ }
- rc = ssh_buffer_unpack(packet, "ds", &code, &error);
- if (rc != SSH_OK){
- ssh_set_error(session, SSH_FATAL, "Invalid packet");
- return SSH_PACKET_USED;
- }
+ if (channel->state != SSH_CHANNEL_STATE_OPENING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
+ "state %d",
+ channel->state);
+ SAFE_FREE(error);
+ goto error;
+ }
- if (channel->state != SSH_CHANNEL_STATE_OPENING) {
- SSH_LOG(SSH_LOG_RARE,
- "SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
- "state %d",
- channel->state);
- SAFE_FREE(error);
- goto error;
- }
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Channel opening failure: channel %" PRIu32 " error (%" PRIu32
+ ") %s",
+ channel->local_channel,
+ code,
+ error);
+ SAFE_FREE(error);
+ channel->state = SSH_CHANNEL_STATE_OPEN_DENIED;
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Channel opening failure: channel %" PRIu32 " error (%" PRIu32 ") %s",
- channel->local_channel,
- code,
- error);
- SAFE_FREE(error);
- channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
-
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_open_response_function,
- channel->session,
- channel,
- false /* is_success */);
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ false /* is_success */);
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
error:
ssh_set_error(session, SSH_FATAL, "Invalid packet");
@@ -312,7 +321,7 @@ static int ssh_channel_open_termination(void *c)
*
* @param[in] payload The buffer containing additional payload for the query.
*
- * @return SSH_OK if successful; SSH_ERROR otherwise.
+ * @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
*/
static int
channel_open(ssh_channel channel,
@@ -400,21 +409,23 @@ channel_open(ssh_channel channel,
}
/* return channel with corresponding local id, or NULL if not found */
-ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
- struct ssh_iterator *it;
- ssh_channel channel;
+ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id)
+{
+ struct ssh_iterator *it = NULL;
+ ssh_channel channel = NULL;
- for (it = ssh_list_get_iterator(session->channels); it != NULL ; it=it->next) {
- channel = ssh_iterator_value(ssh_channel, it);
- if (channel == NULL) {
- continue;
- }
- if (channel->local_channel == id) {
- return channel;
+ for (it = ssh_list_get_iterator(session->channels); it != NULL;
+ it = it->next) {
+ channel = ssh_iterator_value(ssh_channel, it);
+ if (channel == NULL) {
+ continue;
+ }
+ if (channel->local_channel == id) {
+ return channel;
+ }
}
- }
- return NULL;
+ return NULL;
}
/**
@@ -422,32 +433,43 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
* @brief grows the local window and sends a packet to the other party
* @param session SSH session
* @param channel SSH channel
- * @param minimumsize The minimum acceptable size for the new window.
- * @return SSH_OK if successful; SSH_ERROR otherwise.
+ * @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
*/
static int grow_window(ssh_session session,
- ssh_channel channel,
- uint32_t minimumsize)
+ ssh_channel channel)
{
- uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;
+ uint32_t used;
+ uint32_t increment;
int rc;
- if (new_window <= channel->local_window) {
+ /* Calculate the increment taking into account what the peer may still send
+ * (local_window) and what we've already buffered (stdout_buffer and
+ * stderr_buffer).
+ */
+ used = channel->local_window;
+ if (channel->stdout_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stdout_buffer);
+ }
+ if (channel->stderr_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stderr_buffer);
+ }
+ /* Avoid a negative increment in case the peer sent more than the window allowed */
+ increment = WINDOW_DEFAULT > used ? WINDOW_DEFAULT - used : 0;
+ /* Don't grow until we can request at least half a window */
+ if (increment < (WINDOW_DEFAULT / 2)) {
SSH_LOG(SSH_LOG_DEBUG,
"growing window (channel %" PRIu32 ":%" PRIu32 ") to %" PRIu32 " bytes : not needed (%" PRIu32 " bytes)",
- channel->local_channel, channel->remote_channel, new_window,
+ channel->local_channel, channel->remote_channel, WINDOW_DEFAULT,
channel->local_window);
return SSH_OK;
}
- /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
- * value, so we give here the missing bytes needed to reach new_window
- */
+
rc = ssh_buffer_pack(session->out_buffer,
"bdd",
SSH2_MSG_CHANNEL_WINDOW_ADJUST,
channel->remote_channel,
- new_window - channel->local_window);
+ increment);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto error;
@@ -458,12 +480,12 @@ static int grow_window(ssh_session session,
}
SSH_LOG(SSH_LOG_DEBUG,
- "growing window (channel %" PRIu32 ":%" PRIu32 ") to %" PRIu32 " bytes",
+ "growing window (channel %" PRIu32 ":%" PRIu32 ") by %" PRIu32 " bytes",
channel->local_channel,
channel->remote_channel,
- new_window);
+ increment);
- channel->local_window = new_window;
+ channel->local_window += increment;
return SSH_OK;
error:
@@ -487,72 +509,77 @@ static int grow_window(ssh_session session,
*/
static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet)
{
- ssh_channel channel;
- uint32_t chan;
- int rc;
+ ssh_channel channel = NULL;
+ uint32_t chan;
+ int rc;
- rc = ssh_buffer_unpack(packet,"d",&chan);
- if (rc != SSH_OK) {
- ssh_set_error(session, SSH_FATAL,
- "Getting channel from message: short read");
- return NULL;
- }
+ rc = ssh_buffer_unpack(packet, "d", &chan);
+ if (rc != SSH_OK) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Getting channel from message: short read");
+ return NULL;
+ }
- channel = ssh_channel_from_local(session, chan);
- if (channel == NULL) {
- ssh_set_error(session, SSH_FATAL,
- "Server specified invalid channel %" PRIu32,
- (uint32_t) chan);
- }
+ channel = ssh_channel_from_local(session, chan);
+ if (channel == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Server specified invalid channel %" PRIu32,
+ (uint32_t)chan);
+ }
- return channel;
+ return channel;
}
-SSH_PACKET_CALLBACK(channel_rcv_change_window) {
- ssh_channel channel;
- uint32_t bytes;
- int rc;
- bool was_empty;
+SSH_PACKET_CALLBACK(channel_rcv_change_window)
+{
+ ssh_channel channel = NULL;
+ uint32_t bytes;
+ int rc;
+ bool was_empty;
- (void)user;
- (void)type;
+ (void)user;
+ (void)type;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- }
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ }
- rc = ssh_buffer_unpack(packet, "d", &bytes);
- if (channel == NULL || rc != SSH_OK) {
- SSH_LOG(SSH_LOG_PACKET,
- "Error getting a window adjust message: invalid packet");
+ rc = ssh_buffer_unpack(packet, "d", &bytes);
+ if (channel == NULL || rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Error getting a window adjust message: invalid packet");
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_DEBUG,
- "Adding %" PRIu32 " bytes to channel (%" PRIu32 ":%" PRIu32 ") (from %" PRIu32 " bytes)",
- bytes,
- channel->local_channel,
- channel->remote_channel,
- channel->remote_window);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Adding %" PRIu32 " bytes to channel (%" PRIu32 ":%" PRIu32
+ ") (from %" PRIu32 " bytes)",
+ bytes,
+ channel->local_channel,
+ channel->remote_channel,
+ channel->remote_window);
- was_empty = channel->remote_window == 0;
+ was_empty = channel->remote_window == 0;
- channel->remote_window += bytes;
+ channel->remote_window += bytes;
- /* Writing to the channel is non-blocking until the receive window is empty.
- When the receive window becomes non-zero again, call channel_write_wontblock_function. */
- if (was_empty && bytes > 0) {
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_write_wontblock_function,
- session,
- channel,
- channel->remote_window);
- }
+ /* Writing to the channel is non-blocking until the receive window is empty.
+ * When the receive window becomes non-zero again, call
+ * channel_write_wontblock_function. */
+ if (was_empty && bytes > 0) {
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_write_wontblock_function,
+ session,
+ channel,
+ channel->remote_window);
+ }
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
/* is_stderr is set to 1 if the data are extended, ie stderr */
@@ -604,22 +631,30 @@ SSH_PACKET_CALLBACK(channel_rcv_data)
return SSH_PACKET_USED;
}
- len = ssh_string_len(str);
+ /* STRING_SIZE_MAX < UINT32_MAX */
+ len = (uint32_t)ssh_string_len(str);
SSH_LOG(SSH_LOG_PACKET,
"Channel receiving %" PRIu32 " bytes data%s (local win=%" PRIu32
- " remote win=%" PRIu32 ")",
+ " remote win=%" PRIu32 ") on channel %" PRIu32 ":%" PRIu32,
len,
- is_stderr ? " in stderr" : "",
+ is_stderr ? " in stderr" : "",
channel->local_window,
- channel->remote_window);
+ channel->remote_window,
+ channel->local_channel,
+ channel->remote_channel);
- /* What shall we do in this case? Let's accept it anyway */
if (len > channel->local_window) {
SSH_LOG(SSH_LOG_RARE,
"Data packet too big for our window(%" PRIu32 " vs %" PRIu32 ")",
len,
channel->local_window);
+
+ SSH_STRING_FREE(str);
+
+ ssh_set_error(session, SSH_FATAL, "Window exceeded");
+
+ return SSH_PACKET_USED;
}
data = ssh_string_data(str);
@@ -629,11 +664,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data)
return SSH_PACKET_USED;
}
- if (len <= channel->local_window) {
- channel->local_window -= len;
- } else {
- channel->local_window = 0; /* buggy remote */
- }
+ channel->local_window -= len;
SSH_LOG(SSH_LOG_PACKET,
"Channel windows are now (local win=%" PRIu32 " remote win=%" PRIu32 ")",
@@ -661,48 +692,50 @@ SSH_PACKET_CALLBACK(channel_rcv_data)
ssh_buffer_get_len(buf),
is_stderr);
if (rest > 0) {
+ int rc;
if (channel->counter != NULL) {
channel->counter->in_bytes += rest;
}
ssh_buffer_pass_bytes(buf, rest);
+
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
+ }
}
}
ssh_callbacks_iterate_end();
- if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
- }
- }
return SSH_PACKET_USED;
}
-SSH_PACKET_CALLBACK(channel_rcv_eof) {
- ssh_channel channel;
- (void)user;
- (void)type;
+SSH_PACKET_CALLBACK(channel_rcv_eof)
+{
+ ssh_channel channel = NULL;
+ (void)user;
+ (void)type;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Received eof on channel (%" PRIu32 ":%" PRIu32 ")",
- channel->local_channel,
- channel->remote_channel);
- /* channel->remote_window = 0; */
- channel->remote_eof = 1;
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received eof on channel (%" PRIu32 ":%" PRIu32 ")",
+ channel->local_channel,
+ channel->remote_channel);
+ /* channel->remote_window = 0; */
+ channel->remote_eof = 1;
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_eof_function,
- channel->session,
- channel);
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_eof_function,
+ channel->session,
+ channel);
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
static bool ssh_channel_has_unread_data(ssh_channel channel)
@@ -722,8 +755,9 @@ static bool ssh_channel_has_unread_data(ssh_channel channel)
return false;
}
-SSH_PACKET_CALLBACK(channel_rcv_close) {
- ssh_channel channel;
+SSH_PACKET_CALLBACK(channel_rcv_close)
+{
+ ssh_channel channel = NULL;
(void)user;
(void)type;
@@ -768,98 +802,105 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
return SSH_PACKET_USED;
}
-SSH_PACKET_CALLBACK(channel_rcv_request) {
- ssh_channel channel;
- char *request=NULL;
+SSH_PACKET_CALLBACK(channel_rcv_request)
+{
+ ssh_channel channel = NULL;
+ char *request = NULL;
uint8_t want_reply;
int rc;
- (void)user;
- (void)type;
-
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
-
- rc = ssh_buffer_unpack(packet, "sb",
- &request,
- &want_reply);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
- return SSH_PACKET_USED;
- }
-
- if (strcmp(request,"exit-status") == 0) {
+ (void)user;
+ (void)type;
+
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ return SSH_PACKET_USED;
+ }
+
+ rc = ssh_buffer_unpack(packet, "sb", &request, &want_reply);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
+ return SSH_PACKET_USED;
+ }
+
+ if (strcmp(request, "exit-status") == 0) {
SAFE_FREE(request);
- rc = ssh_buffer_unpack(packet, "d", &channel->exit_status);
+ rc = ssh_buffer_unpack(packet, "d", &channel->exit.code);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_PACKET, "Invalid exit-status packet");
return SSH_PACKET_USED;
}
- SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status);
+ channel->exit.status = true;
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "received exit-status %u on channel %" PRIu32 ":%" PRIu32,
+ channel->exit.code,
+ channel->local_channel,
+ channel->remote_channel);
ssh_callbacks_execute_list(channel->callbacks,
ssh_channel_callbacks,
channel_exit_status_function,
channel->session,
channel,
- channel->exit_status);
+ channel->exit.code);
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- if (strcmp(request,"signal") == 0) {
+ if (strcmp(request, "signal") == 0) {
char *sig = NULL;
- SAFE_FREE(request);
- SSH_LOG(SSH_LOG_PACKET, "received signal");
+ SAFE_FREE(request);
+ SSH_LOG(SSH_LOG_PACKET, "received signal");
- rc = ssh_buffer_unpack(packet, "s", &sig);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
- return SSH_PACKET_USED;
- }
+ rc = ssh_buffer_unpack(packet, "s", &sig);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Remote connection sent a signal SIG %s", sig);
+ SSH_LOG(SSH_LOG_PACKET, "Remote connection sent a signal SIG %s", sig);
ssh_callbacks_execute_list(channel->callbacks,
ssh_channel_callbacks,
channel_signal_function,
channel->session,
channel,
sig);
- SAFE_FREE(sig);
-
- return SSH_PACKET_USED;
- }
-
- if (strcmp(request, "exit-signal") == 0) {
- const char *core = "(core dumped)";
- char *sig = NULL;
- char *errmsg = NULL;
- char *lang = NULL;
- uint8_t core_dumped;
-
- SAFE_FREE(request);
-
- rc = ssh_buffer_unpack(packet, "sbss",
- &sig, /* signal name */
- &core_dumped, /* core dumped */
- &errmsg, /* error message */
- &lang);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
- return SSH_PACKET_USED;
- }
-
- if (core_dumped == 0) {
- core = "";
- }
-
- SSH_LOG(SSH_LOG_PACKET,
- "Remote connection closed by signal SIG %s %s", sig, core);
- ssh_callbacks_execute_list(channel->callbacks,
+ SAFE_FREE(sig);
+
+ return SSH_PACKET_USED;
+ }
+
+ if (strcmp(request, "exit-signal") == 0) {
+ const char *core = "(core dumped)";
+ char *sig = NULL;
+ char *errmsg = NULL;
+ char *lang = NULL;
+ uint8_t core_dumped;
+
+ SAFE_FREE(request);
+
+ rc = ssh_buffer_unpack(packet,
+ "sbss",
+ &sig, /* signal name */
+ &core_dumped, /* core dumped */
+ &errmsg, /* error message */
+ &lang);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
+ return SSH_PACKET_USED;
+ }
+
+ if (core_dumped == 0) {
+ core = "";
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Remote connection closed by signal SIG %s %s",
+ sig,
+ core);
+ ssh_callbacks_execute_list(channel->callbacks,
ssh_channel_callbacks,
channel_exit_signal_function,
channel->session,
@@ -869,74 +910,85 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
errmsg,
lang);
+ channel->exit.core_dumped = core_dumped;
+ if (sig != NULL) {
+ SAFE_FREE(channel->exit.signal);
+ channel->exit.signal = sig;
+ }
+ channel->exit.status = true;
+
SAFE_FREE(lang);
SAFE_FREE(errmsg);
- SAFE_FREE(sig);
-
- return SSH_PACKET_USED;
- }
- if(strcmp(request,"keepalive@openssh.com")==0){
- SAFE_FREE(request);
- SSH_LOG(SSH_LOG_DEBUG,"Responding to Openssh's keepalive");
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bd",
- SSH2_MSG_CHANNEL_FAILURE,
- channel->remote_channel);
- if (rc != SSH_OK) {
- return SSH_PACKET_USED;
- }
- ssh_packet_send(session);
-
- return SSH_PACKET_USED;
- }
-
- if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
- int status;
- SAFE_FREE(request);
- SSH_LOG(SSH_LOG_DEBUG, "Received an auth-agent-req request");
-
- status = SSH2_MSG_CHANNEL_FAILURE;
- ssh_callbacks_iterate(channel->callbacks,
- ssh_channel_callbacks,
- channel_auth_agent_req_function) {
- ssh_callbacks_iterate_exec(channel_auth_agent_req_function,
- channel->session,
- channel);
- /* in lieu of a return value, if the callback exists it's supported */
- status = SSH2_MSG_CHANNEL_SUCCESS;
- break;
+ return SSH_PACKET_USED;
}
- ssh_callbacks_iterate_end();
+ if (strcmp(request, "keepalive@openssh.com") == 0) {
+ SAFE_FREE(request);
+ SSH_LOG(SSH_LOG_DEBUG, "Responding to Openssh's keepalive");
- if (want_reply) {
rc = ssh_buffer_pack(session->out_buffer,
"bd",
- status,
+ SSH2_MSG_CHANNEL_FAILURE,
channel->remote_channel);
if (rc != SSH_OK) {
return SSH_PACKET_USED;
}
ssh_packet_send(session);
+
+ return SSH_PACKET_USED;
}
- return SSH_PACKET_USED;
- }
-#ifdef WITH_SERVER
- /* If we are here, that means we have a request that is not in the understood
- * client requests. That means we need to create a ssh message to be passed
- * to the user code handling ssh messages
- */
- ssh_message_handle_channel_request(session,channel,packet,request,want_reply);
-#else
- SSH_LOG(SSH_LOG_DEBUG, "Unhandled channel request %s", request);
-#endif
+ if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
+ int status;
- SAFE_FREE(request);
+ SAFE_FREE(request);
+ SSH_LOG(SSH_LOG_DEBUG, "Received an auth-agent-req request");
- return SSH_PACKET_USED;
-}
+ status = SSH2_MSG_CHANNEL_FAILURE;
+ ssh_callbacks_iterate (channel->callbacks,
+ ssh_channel_callbacks,
+ channel_auth_agent_req_function) {
+ ssh_callbacks_iterate_exec(channel_auth_agent_req_function,
+ channel->session,
+ channel);
+ /* in lieu of a return value, if the callback exists it's supported
+ */
+ status = SSH2_MSG_CHANNEL_SUCCESS;
+ break;
+ }
+ ssh_callbacks_iterate_end();
+
+ if (want_reply) {
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ status,
+ channel->remote_channel);
+ if (rc != SSH_OK) {
+ return SSH_PACKET_USED;
+ }
+ ssh_packet_send(session);
+ }
+
+ return SSH_PACKET_USED;
+ }
+#ifdef WITH_SERVER
+ /* If we are here, that means we have a request that is not in the
+ * understood client requests. That means we need to create a ssh message to
+ * be passed to the user code handling ssh messages
+ */
+ ssh_message_handle_channel_request(session,
+ channel,
+ packet,
+ request,
+ want_reply);
+#else
+ SSH_LOG(SSH_LOG_DEBUG, "Unhandled channel request %s", request);
+#endif
+
+ SAFE_FREE(request);
+
+ return SSH_PACKET_USED;
+}
/*
* When data has been received from the ssh server, it can be applied to the
@@ -948,58 +1000,58 @@ int channel_default_bufferize(ssh_channel channel,
void *data, uint32_t len,
bool is_stderr)
{
- ssh_session session;
-
- if(channel == NULL) {
- return -1;
- }
-
- session = channel->session;
-
- if(data == NULL) {
- ssh_set_error_invalid(session);
- return -1;
- }
+ ssh_session session = NULL;
- SSH_LOG(SSH_LOG_PACKET,
- "placing %" PRIu32 " bytes into channel buffer (%s)",
- len,
- is_stderr ? "stderr" : "stdout");
- if (!is_stderr) {
- /* stdout */
- if (channel->stdout_buffer == NULL) {
- channel->stdout_buffer = ssh_buffer_new();
- if (channel->stdout_buffer == NULL) {
- ssh_set_error_oom(session);
+ if (channel == NULL) {
return -1;
- }
}
- if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) {
- ssh_set_error_oom(session);
- SSH_BUFFER_FREE(channel->stdout_buffer);
- channel->stdout_buffer = NULL;
- return -1;
- }
- } else {
- /* stderr */
- if (channel->stderr_buffer == NULL) {
- channel->stderr_buffer = ssh_buffer_new();
- if (channel->stderr_buffer == NULL) {
- ssh_set_error_oom(session);
+ session = channel->session;
+
+ if (data == NULL) {
+ ssh_set_error_invalid(session);
return -1;
- }
}
- if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) {
- ssh_set_error_oom(session);
- SSH_BUFFER_FREE(channel->stderr_buffer);
- channel->stderr_buffer = NULL;
- return -1;
+ SSH_LOG(SSH_LOG_PACKET,
+ "placing %" PRIu32 " bytes into channel buffer (%s)",
+ len,
+ is_stderr ? "stderr" : "stdout");
+ if (!is_stderr) {
+ /* stdout */
+ if (channel->stdout_buffer == NULL) {
+ channel->stdout_buffer = ssh_buffer_new();
+ if (channel->stdout_buffer == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ }
+
+ if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(channel->stdout_buffer);
+ channel->stdout_buffer = NULL;
+ return -1;
+ }
+ } else {
+ /* stderr */
+ if (channel->stderr_buffer == NULL) {
+ channel->stderr_buffer = ssh_buffer_new();
+ if (channel->stderr_buffer == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ }
+
+ if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(channel->stderr_buffer);
+ channel->stderr_buffer = NULL;
+ return -1;
+ }
}
- }
- return 0;
+ return 0;
}
/**
@@ -1007,9 +1059,9 @@ int channel_default_bufferize(ssh_channel channel,
*
* @param[in] channel An allocated channel.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_open_forward()
@@ -1025,22 +1077,22 @@ int ssh_channel_open_session(ssh_channel channel)
return channel_open(channel,
"session",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
/**
* @brief Open an agent authentication forwarding channel. This type of channel
- * can be opened by a server towards a client in order to provide SSH-Agent services
- * to the server-side process. This channel can only be opened if the client
- * claimed support by sending a channel request beforehand.
+ * can be opened by a server towards a client in order to provide SSH-Agent
+ * services to the server-side process. This channel can only be opened if the
+ * client claimed support by sending a channel request beforehand.
*
* @param[in] channel An allocated channel.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_open_forward()
@@ -1053,12 +1105,11 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
return channel_open(channel,
"auth-agent@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
-
/**
* @brief Open a TCP/IP forwarding channel.
*
@@ -1075,62 +1126,62 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
* @param[in] localport The port on the host from where the connection
* originated. This is mostly for logging purposes.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
- * @warning This function does not bind the local port and does not automatically
- * forward the content of a socket to the channel. You still have to
- * use ssh_channel_read and ssh_channel_write for this.
+ * @warning This function does not bind the local port and does not
+ * automatically forward the content of a socket to the channel. You still have
+ * to use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport)
{
- ssh_session session;
- ssh_buffer payload = NULL;
- ssh_string str = NULL;
- int rc = SSH_ERROR;
+ ssh_session session = NULL;
+ ssh_buffer payload = NULL;
+ ssh_string str = NULL;
+ int rc = SSH_ERROR;
- if (channel == NULL) {
- return rc;
- }
+ if (channel == NULL) {
+ return rc;
+ }
- session = channel->session;
+ session = channel->session;
- if(remotehost == NULL || sourcehost == NULL) {
- ssh_set_error_invalid(session);
- return rc;
- }
+ if (remotehost == NULL || sourcehost == NULL) {
+ ssh_set_error_invalid(session);
+ return rc;
+ }
- payload = ssh_buffer_new();
- if (payload == NULL) {
- ssh_set_error_oom(session);
- goto error;
- }
+ payload = ssh_buffer_new();
+ if (payload == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- rc = ssh_buffer_pack(payload,
- "sdsd",
- remotehost,
- remoteport,
- sourcehost,
- localport);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
+ rc = ssh_buffer_pack(payload,
+ "sdsd",
+ remotehost,
+ remoteport,
+ sourcehost,
+ localport);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- rc = channel_open(channel,
- "direct-tcpip",
- CHANNEL_INITIAL_WINDOW,
- CHANNEL_MAX_PACKET,
- payload);
+ rc = channel_open(channel,
+ "direct-tcpip",
+ WINDOW_DEFAULT,
+ CHANNEL_MAX_PACKET,
+ payload);
error:
- SSH_BUFFER_FREE(payload);
- SSH_STRING_FREE(str);
+ SSH_BUFFER_FREE(payload);
+ SSH_STRING_FREE(str);
- return rc;
+ return rc;
}
/**
@@ -1147,16 +1198,16 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
* @param[in] localport The port on the host from where the connection
* originated. This is mostly for logging purposes.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK on` success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning This function does not bind the local port and does not
- * automatically forward the content of a socket to the channel.
- * You still have to use ssh_channel_read and ssh_channel_write for this.
+ * automatically forward the content of a socket to the channel.
+ * You still have to use ssh_channel_read and ssh_channel_write for this.
* @warning Requires support of OpenSSH for UNIX domain socket forwarding.
- */
+ */
int ssh_channel_open_forward_unix(ssh_channel channel,
const char *remotepath,
const char *sourcehost,
@@ -1164,7 +1215,6 @@ int ssh_channel_open_forward_unix(ssh_channel channel,
{
ssh_session session = NULL;
ssh_buffer payload = NULL;
- ssh_string str = NULL;
int rc = SSH_ERROR;
int version;
@@ -1205,13 +1255,12 @@ int ssh_channel_open_forward_unix(ssh_channel channel,
rc = channel_open(channel,
"direct-streamlocal@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
error:
SSH_BUFFER_FREE(payload);
- SSH_STRING_FREE(str);
return rc;
}
@@ -1225,7 +1274,7 @@ int ssh_channel_open_forward_unix(ssh_channel channel,
*/
void ssh_channel_free(ssh_channel channel)
{
- ssh_session session;
+ ssh_session session = NULL;
if (channel == NULL) {
return;
@@ -1258,6 +1307,11 @@ void ssh_channel_free(ssh_channel channel)
}
channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL;
+ if (channel->callbacks != NULL) {
+ ssh_list_free(channel->callbacks);
+ channel->callbacks = NULL;
+ }
+
/* The idea behind the flags is the following : it is well possible
* that a client closes a channel that still exists on the server side.
* We definitively close the channel when we receive a close message *and*
@@ -1291,6 +1345,7 @@ void ssh_channel_do_free(ssh_channel channel)
ssh_list_free(channel->callbacks);
channel->callbacks = NULL;
}
+ SAFE_FREE(channel->exit.signal);
channel->session = NULL;
SAFE_FREE(channel);
@@ -1303,7 +1358,7 @@ void ssh_channel_do_free(ssh_channel channel)
*
* @param[in] channel The channel to send the eof to.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* Example:
@code
@@ -1326,7 +1381,7 @@ void ssh_channel_do_free(ssh_channel channel)
*/
int ssh_channel_send_eof(ssh_channel channel)
{
- ssh_session session;
+ ssh_session session = NULL;
int rc = SSH_ERROR;
int err;
@@ -1380,14 +1435,14 @@ int ssh_channel_send_eof(ssh_channel channel)
*
* @param[in] channel The channel to close.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @see ssh_channel_free()
* @see ssh_channel_is_eof()
*/
int ssh_channel_close(ssh_channel channel)
{
- ssh_session session;
+ ssh_session session = NULL;
int rc = 0;
if(channel == NULL) {
@@ -1468,11 +1523,11 @@ static int ssh_waitsession_unblocked(void *s)
/**
* @internal
* @brief Flushes a channel (and its session) until the output buffer
- * is empty, or timeout elapsed.
+ * is empty, or timeout elapsed.
* @param channel SSH channel
- * @return SSH_OK On success,
- * SSH_ERROR On error.
- * SSH_AGAIN Timeout elapsed (or in nonblocking mode).
+ * @return `SSH_OK` On success,
+ * `SSH_ERROR` On error.
+ * `SSH_AGAIN` Timeout elapsed (or in nonblocking mode).
*/
int ssh_channel_flush(ssh_channel channel)
{
@@ -1483,144 +1538,153 @@ static int channel_write_common(ssh_channel channel,
const void *data,
uint32_t len, int is_stderr)
{
- ssh_session session;
- uint32_t origlen = len;
- size_t effectivelen;
- size_t maxpacketlen;
- int rc;
-
- if(channel == NULL) {
- return -1;
- }
- session = channel->session;
- if(data == NULL) {
- ssh_set_error_invalid(session);
- return -1;
- }
+ ssh_session session = NULL;
+ uint32_t origlen = len;
+ uint32_t effectivelen;
+ int rc;
- if (len > INT_MAX) {
- SSH_LOG(SSH_LOG_TRACE,
- "Length (%" PRIu32 ") is bigger than INT_MAX", len);
- return SSH_ERROR;
- }
+ if (channel == NULL) {
+ return -1;
+ }
+ session = channel->session;
+ if (data == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
- /*
- * Handle the max packet len from remote side, be nice
- * 10 bytes for the headers
- */
- maxpacketlen = channel->remote_maxpacket - 10;
+ if (len > INT_MAX) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Length (%" PRIu32 ") is bigger than INT_MAX",
+ len);
+ return SSH_ERROR;
+ }
- if (channel->local_eof) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Can't write to channel %" PRIu32 ":%" PRIu32 " after EOF was sent",
- channel->local_channel,
- channel->remote_channel);
- return -1;
- }
+ if (channel->local_eof) {
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Can't write to channel %" PRIu32 ":%" PRIu32
+ " after EOF was sent",
+ channel->local_channel,
+ channel->remote_channel);
+ return -1;
+ }
- if (channel->state != SSH_CHANNEL_STATE_OPEN || channel->delayed_close != 0) {
- ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed");
+ if (channel->state != SSH_CHANNEL_STATE_OPEN ||
+ channel->delayed_close != 0) {
+ ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed");
- return -1;
- }
+ return -1;
+ }
- if (session->session_state == SSH_SESSION_STATE_ERROR) {
- return SSH_ERROR;
- }
+ if (session->session_state == SSH_SESSION_STATE_ERROR) {
+ return SSH_ERROR;
+ }
- if (ssh_waitsession_unblocked(session) == 0){
- rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
- ssh_waitsession_unblocked, session);
- if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session))
- goto out;
- }
- while (len > 0) {
- if (channel->remote_window < len) {
- SSH_LOG(SSH_LOG_DEBUG,
- "Remote window is %" PRIu32 " bytes. going to write %" PRIu32 " bytes",
- channel->remote_window,
- len);
- /* When the window is zero, wait for it to grow */
- if(channel->remote_window == 0) {
- /* nothing can be written */
- SSH_LOG(SSH_LOG_DEBUG,
- "Wait for a growing window message...");
- rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
- ssh_channel_waitwindow_termination,channel);
- if (rc == SSH_ERROR ||
- !ssh_channel_waitwindow_termination(channel) ||
- session->session_state == SSH_SESSION_STATE_ERROR ||
- channel->state == SSH_CHANNEL_STATE_CLOSED)
+ if (ssh_waitsession_unblocked(session) == 0) {
+ rc = ssh_handle_packets_termination(session,
+ SSH_TIMEOUT_DEFAULT,
+ ssh_waitsession_unblocked,
+ session);
+ if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session))
goto out;
- continue;
- }
- /* When the window is non-zero, accept data up to the window size */
- effectivelen = MIN(len, channel->remote_window);
- } else {
- effectivelen = len;
}
+ while (len > 0) {
+ if (channel->remote_window < len) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window is %" PRIu32
+ " bytes. going to write %" PRIu32 " bytes",
+ channel->remote_window,
+ len);
+ /* When the window is zero, wait for it to grow */
+ if (channel->remote_window == 0) {
+ /* nothing can be written */
+ SSH_LOG(SSH_LOG_DEBUG, "Wait for a growing window message...");
+ rc = ssh_handle_packets_termination(
+ session,
+ SSH_TIMEOUT_DEFAULT,
+ ssh_channel_waitwindow_termination,
+ channel);
+ if (rc == SSH_ERROR ||
+ !ssh_channel_waitwindow_termination(channel) ||
+ session->session_state == SSH_SESSION_STATE_ERROR ||
+ channel->state == SSH_CHANNEL_STATE_CLOSED)
+ goto out;
+ continue;
+ }
+ /* When the window is non-zero, accept data up to the window size */
+ effectivelen = MIN(len, channel->remote_window);
+ } else {
+ effectivelen = len;
+ }
- effectivelen = MIN(effectivelen, maxpacketlen);
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bd",
- is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA,
- channel->remote_channel);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
+ /*
+ * Like OpenSSH, don't subtract bytes for the header fields
+ * and allow to send a payload of remote_maxpacket length.
+ */
+ effectivelen = MIN(effectivelen, channel->remote_maxpacket);
- /* stderr message has an extra field */
- if (is_stderr) {
rc = ssh_buffer_pack(session->out_buffer,
- "d",
- SSH2_EXTENDED_DATA_STDERR);
+ "bd",
+ is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA
+ : SSH2_MSG_CHANNEL_DATA,
+ channel->remote_channel);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto error;
}
- }
- /* append payload data */
- rc = ssh_buffer_pack(session->out_buffer,
- "dP",
- effectivelen,
- (size_t)effectivelen, data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
+ /* stderr message has an extra field */
+ if (is_stderr) {
+ rc = ssh_buffer_pack(session->out_buffer,
+ "d",
+ SSH2_EXTENDED_DATA_STDERR);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ }
- rc = ssh_packet_send(session);
- if (rc == SSH_ERROR) {
- return SSH_ERROR;
- }
+ /* append payload data */
+ rc = ssh_buffer_pack(session->out_buffer,
+ "dP",
+ effectivelen,
+ (size_t)effectivelen,
+ data);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "ssh_channel_write wrote %ld bytes", (long int) effectivelen);
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return SSH_ERROR;
+ }
- channel->remote_window -= effectivelen;
- len -= effectivelen;
- data = ((uint8_t*)data + effectivelen);
- if (channel->counter != NULL) {
- channel->counter->out_bytes += effectivelen;
+ SSH_LOG(SSH_LOG_PACKET,
+ "ssh_channel_write wrote %" PRIu32 " bytes",
+ effectivelen);
+
+ channel->remote_window -= effectivelen;
+ len -= effectivelen;
+ data = ((uint8_t *)data + effectivelen);
+ if (channel->counter != NULL) {
+ channel->counter->out_bytes += effectivelen;
+ }
}
- }
- /* it's a good idea to flush the socket now */
- rc = ssh_channel_flush(channel);
- if (rc == SSH_ERROR) {
- goto error;
- }
+ /* it's a good idea to flush the socket now */
+ rc = ssh_channel_flush(channel);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
out:
- return (int)(origlen - len);
+ return (int)(origlen - len);
error:
- ssh_buffer_reinit(session->out_buffer);
+ ssh_buffer_reinit(session->out_buffer);
- return SSH_ERROR;
+ return SSH_ERROR;
}
/**
@@ -1654,7 +1718,7 @@ uint32_t ssh_channel_window_size(ssh_channel channel)
*
* @param[in] len The length of the buffer to write to.
*
- * @return The number of bytes written, SSH_ERROR on error.
+ * @return The number of bytes written, `SSH_ERROR` on error.
*
* @see ssh_channel_read()
*/
@@ -1691,7 +1755,7 @@ int ssh_channel_is_open(ssh_channel channel)
*/
int ssh_channel_is_closed(ssh_channel channel)
{
- if (channel == NULL) {
+ if (channel == NULL || channel->session == NULL) {
return SSH_ERROR;
}
return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0);
@@ -1740,35 +1804,37 @@ void ssh_channel_set_blocking(ssh_channel channel, int blocking)
*
* @brief handle a SSH_CHANNEL_SUCCESS packet and set the channel state.
*/
-SSH_PACKET_CALLBACK(ssh_packet_channel_success){
- ssh_channel channel;
- (void)type;
- (void)user;
+SSH_PACKET_CALLBACK(ssh_packet_channel_success)
+{
+ ssh_channel channel = NULL;
+ (void)type;
+ (void)user;
- channel=channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_SUCCESS on channel (%" PRIu32 ":%" PRIu32 ")",
- channel->local_channel,
- channel->remote_channel);
- if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
- SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d",
- channel->request_state);
- } else {
- channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED;
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received SSH_CHANNEL_SUCCESS on channel (%" PRIu32 ":%" PRIu32 ")",
+ channel->local_channel,
+ channel->remote_channel);
+ if (channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH_CHANNEL_SUCCESS received in incorrect state %d",
+ channel->request_state);
+ } else {
+ channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED;
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_request_response_function,
- channel->session,
- channel);
- }
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
+ }
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
/**
@@ -1776,36 +1842,38 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){
*
* @brief Handle a SSH_CHANNEL_FAILURE packet and set the channel state.
*/
-SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
- ssh_channel channel;
- (void)type;
- (void)user;
+SSH_PACKET_CALLBACK(ssh_packet_channel_failure)
+{
+ ssh_channel channel = NULL;
+ (void)type;
+ (void)user;
- channel=channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_FAILURE on channel (%" PRIu32 ":%" PRIu32 ")",
- channel->local_channel,
- channel->remote_channel);
- if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
- SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d",
- channel->request_state);
- } else {
- channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED;
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received SSH_CHANNEL_FAILURE on channel (%" PRIu32 ":%" PRIu32 ")",
+ channel->local_channel,
+ channel->remote_channel);
+ if (channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH_CHANNEL_FAILURE received in incorrect state %d",
+ channel->request_state);
+ } else {
+ channel->request_state = SSH_CHANNEL_REQ_STATE_DENIED;
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_request_response_function,
- channel->session,
- channel);
- }
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
+ }
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
static int ssh_channel_request_termination(void *c)
@@ -1856,7 +1924,10 @@ static int channel_request(ssh_channel channel, const char *request,
}
SSH_LOG(SSH_LOG_PACKET,
- "Sent a SSH_MSG_CHANNEL_REQUEST %s", request);
+ "Sent a SSH_MSG_CHANNEL_REQUEST %s on channel %" PRIu32 ":%" PRIu32,
+ request,
+ channel->local_channel,
+ channel->remote_channel);
if (reply == 0) {
channel->request_state = SSH_CHANNEL_REQ_STATE_NONE;
return SSH_OK;
@@ -1876,13 +1947,20 @@ static int channel_request(ssh_channel channel, const char *request,
rc=SSH_ERROR;
break;
case SSH_CHANNEL_REQ_STATE_DENIED:
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Channel request %s failed", request);
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Channel request %s failed on channel %" PRIu32 ":%" PRIu32,
+ request,
+ channel->local_channel,
+ channel->remote_channel);
rc=SSH_ERROR;
break;
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
SSH_LOG(SSH_LOG_DEBUG,
- "Channel request %s success",request);
+ "Channel request %s success on channel %" PRIu32 ":%" PRIu32,
+ request,
+ channel->local_channel,
+ channel->remote_channel);
rc=SSH_OK;
break;
case SSH_CHANNEL_REQ_STATE_PENDING:
@@ -1914,61 +1992,96 @@ static int channel_request(ssh_channel channel, const char *request,
*
* @param[in] row The number of rows.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @param[in] modes Encoded SSH terminal modes for the PTY
+ *
+ * @param[in] modes_len Number of bytes in 'modes'
+ *
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
-int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
- int col, int row)
+int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal,
+ int col, int row, const unsigned char* modes, size_t modes_len)
{
- ssh_session session;
- ssh_buffer buffer = NULL;
- int rc = SSH_ERROR;
+ ssh_session session = NULL;
+ ssh_buffer buffer = NULL;
+ int rc = SSH_ERROR;
- if(channel == NULL) {
- return SSH_ERROR;
- }
- session = channel->session;
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+ session = channel->session;
- if(terminal == NULL) {
- ssh_set_error_invalid(channel->session);
- return rc;
- }
+ if (terminal == NULL) {
+ ssh_set_error_invalid(channel->session);
+ return rc;
+ }
- switch(channel->request_state){
- case SSH_CHANNEL_REQ_STATE_NONE:
- break;
- default:
- goto pending;
- }
+ switch (channel->request_state) {
+ case SSH_CHANNEL_REQ_STATE_NONE:
+ break;
+ default:
+ goto pending;
+ }
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- ssh_set_error_oom(session);
- goto error;
- }
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- rc = ssh_buffer_pack(buffer,
- "sdddddb",
- terminal,
- col,
- row,
- 0, /* pix */
- 0, /* pix */
- 1, /* add a 0byte string */
- 0);
+ rc = ssh_buffer_pack(buffer,
+ "sdddddP",
+ terminal,
+ col,
+ row,
+ 0, /* pix */
+ 0, /* pix */
+ (uint32_t)modes_len,
+ (size_t)modes_len,
+ modes);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
pending:
- rc = channel_request(channel, "pty-req", buffer, 1);
+ rc = channel_request(channel, "pty-req", buffer, 1);
error:
- SSH_BUFFER_FREE(buffer);
+ SSH_BUFFER_FREE(buffer);
- return rc;
+ return rc;
+}
+
+/**
+ * @brief Request a PTY with a specific size using current TTY modes.
+ *
+ * Encodes @p terminal modes from the current TTY and sends a PTY request
+ * for the given channel, terminal type, and size in columns/rows.
+ *
+ * @param[in] channel The channel to send the request on.
+ * @param[in] terminal The terminal type (e.g. "xterm").
+ * @param[in] col Number of columns.
+ * @param[in] row Number of rows.
+ *
+ * @return `SSH_OK` on success; `SSH_ERROR` on failure.
+ */
+int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
+ int col, int row)
+{
+ /* use modes from the current TTY */
+ unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
+ int rc = encode_current_tty_opts(modes_buf, sizeof(modes_buf));
+ if (rc < 0) {
+ return rc;
+ }
+ return ssh_channel_request_pty_size_modes(channel,
+ terminal,
+ col,
+ row,
+ modes_buf,
+ (size_t)rc);
}
/**
@@ -1976,9 +2089,9 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
*
* @param[in] channel The channel to send the request.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_request_pty_size()
@@ -1997,7 +2110,7 @@ int ssh_channel_request_pty(ssh_channel channel)
*
* @param[in] rows The new number of rows.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @warning Do not call it from a signal handler if you are not sure any other
* libssh function using the same channel/session is running at the
@@ -2038,9 +2151,9 @@ int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows)
*
* @param[in] channel The channel to send the request.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_request_shell(ssh_channel channel)
@@ -2059,9 +2172,9 @@ int ssh_channel_request_shell(ssh_channel channel)
*
* @param[in] subsys The subsystem to request (for example "sftp").
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning You normally don't have to call it for sftp, see sftp_new().
@@ -2109,9 +2222,9 @@ int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys)
*
* @param[in] channel The channel to request the sftp subsystem.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @note You should use sftp_new() which does this for you.
@@ -2165,9 +2278,9 @@ static char *generate_cookie(void)
*
* @param[in] screen_number The screen number.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
@@ -2233,7 +2346,7 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
#endif
ssh_message msg = NULL;
ssh_channel channel = NULL;
- struct ssh_iterator *iterator;
+ struct ssh_iterator *iterator = NULL;
int t;
/*
@@ -2300,15 +2413,16 @@ ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms)
}
/**
- * @brief Send an "auth-agent-req" channel request over an existing session channel.
+ * @brief Send an "auth-agent-req" channel request over an existing session
+ * channel.
*
- * This client-side request will enable forwarding the agent over an secure tunnel.
- * When the server is ready to open one authentication agent channel, an
+ * This client-side request will enable forwarding the agent over an secure
+ * tunnel. When the server is ready to open one authentication agent channel, an
* ssh_channel_open_request_auth_agent_callback event will be generated.
*
* @param[in] channel The channel to send signal.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_auth_agent(ssh_channel channel) {
if (channel == NULL) {
@@ -2389,9 +2503,9 @@ static int ssh_global_request_termination(void *s)
*
* @param[in] reply Set if you expect a reply from server.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_global_request(ssh_session session,
@@ -2483,7 +2597,7 @@ int ssh_global_request(ssh_session session,
/**
* @brief Sends the "tcpip-forward" global request to ask the server to begin
- * listening for inbound connections.
+ * listening for inbound connections.
*
* @param[in] session The ssh session to send the request.
*
@@ -2498,9 +2612,9 @@ int ssh_global_request(ssh_session session,
* @param[in] bound_port The pointer to get actual bound port. Pass NULL to
* ignore.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
**/
int ssh_channel_listen_forward(ssh_session session,
@@ -2607,9 +2721,9 @@ ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, i
*
* @param[in] port The bound port on the server.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_cancel_forward(ssh_session session,
@@ -2658,9 +2772,9 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port)
*
* @param[in] value The value to set.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
* @warning Some environment variables may be refused by security reasons.
*/
@@ -2714,9 +2828,9 @@ int ssh_channel_request_env(ssh_channel channel, const char *name, const char *v
* @param[in] cmd The command to execute
* (e.g. "ls ~/ -al | grep -i reports").
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* Example:
@@ -2732,6 +2846,12 @@ int ssh_channel_request_env(ssh_channel channel, const char *name, const char *v
}
}
@endcode
+ *
+ * @warning In a single channel, only ONE command can be executed!
+ * If you want to executed multiple commands, allocate separate channels for
+ * them or consider opening interactive shell.
+ * Attempting to run multiple consecutive commands in one channel will fail.
+ * See RFC 4254 Section 6.5.
*
* @see ssh_channel_request_shell()
*/
@@ -2773,9 +2893,9 @@ int ssh_channel_request_exec(ssh_channel channel, const char *cmd)
return rc;
}
-
/**
- * @brief Send a signal to remote process (as described in RFC 4254, section 6.9).
+ * @brief Send a signal to remote process (as described in RFC 4254,
+ * section 6.9).
*
* Sends a signal 'sig' to the remote process.
* Note, that remote system may not support signals concept.
@@ -2799,7 +2919,7 @@ int ssh_channel_request_exec(ssh_channel channel, const char *cmd)
* SIGUSR1 -> USR1 \n
* SIGUSR2 -> USR2 \n
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*/
int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
{
@@ -2832,7 +2952,6 @@ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
return rc;
}
-
/**
* @brief Send a break signal to the server (as described in RFC 4335).
*
@@ -2844,7 +2963,7 @@ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
*
* @param[in] length The break-length in milliseconds to send.
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
{
@@ -2874,7 +2993,6 @@ int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
return rc;
}
-
/**
* @brief Read data from a channel into a buffer.
*
@@ -2883,13 +3001,14 @@ int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
* @param[out] buffer The buffer which will get the data.
*
* @param[in] count The count of bytes to be read. If it is bigger than 0,
- * the exact size will be read, else (bytes=0) it will
- * return once anything is available.
+ * the exact size will be read, else (bytes=0) it will return once anything is
+ * available.
*
* @param is_stderr A boolean value to mark reading from the stderr stream.
*
- * @return The number of bytes read, 0 on end of file or SSH_ERROR
- * on error.
+ * @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
+ * on timeout and `SSH_ERROR` on error.
+ *
* @deprecated Please use ssh_channel_read instead
* @warning This function doesn't work in nonblocking/timeout mode
* @see ssh_channel_read
@@ -2897,84 +3016,83 @@ int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
int is_stderr)
{
- ssh_session session;
- char *buffer_tmp = NULL;
- int r;
- uint32_t total=0;
-
- if(channel == NULL) {
- return SSH_ERROR;
- }
- session = channel->session;
+ ssh_session session = NULL;
+ char *buffer_tmp = NULL;
+ int r;
+ uint32_t total = 0;
- if(buffer == NULL) {
- ssh_set_error_invalid(channel->session);
- return SSH_ERROR;
- }
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+ session = channel->session;
- ssh_buffer_reinit(buffer);
- if(count==0){
- do {
- r=ssh_channel_poll(channel, is_stderr);
- if(r < 0){
- return r;
- }
- if(r > 0){
- count = r;
- buffer_tmp = ssh_buffer_allocate(buffer, count);
- if (buffer_tmp == NULL) {
- ssh_set_error_oom(session);
- return SSH_ERROR;
- }
- r=ssh_channel_read(channel, buffer_tmp, r, is_stderr);
- if(r < 0){
- ssh_buffer_pass_bytes_end(buffer, count);
- return r;
- }
- /* Rollback the unused space */
- ssh_buffer_pass_bytes_end(buffer, count - r);
+ if (buffer == NULL) {
+ ssh_set_error_invalid(channel->session);
+ return SSH_ERROR;
+ }
- return r;
- }
- if(ssh_channel_is_eof(channel)){
- return 0;
- }
- ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE);
- } while (r == 0);
- }
+ ssh_buffer_reinit(buffer);
+ if (count == 0) {
+ do {
+ r = ssh_channel_poll(channel, is_stderr);
+ if (r < 0) {
+ return r;
+ }
+ if (r > 0) {
+ count = r;
+ buffer_tmp = ssh_buffer_allocate(buffer, count);
+ if (buffer_tmp == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ r = ssh_channel_read(channel, buffer_tmp, r, is_stderr);
+ if (r < 0) {
+ ssh_buffer_pass_bytes_end(buffer, count);
+ return r;
+ }
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(buffer, count - r);
+
+ return r;
+ }
+ if (ssh_channel_is_eof(channel)) {
+ return 0;
+ }
+ ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE);
+ } while (r == 0);
+ }
- buffer_tmp = ssh_buffer_allocate(buffer, count);
- if (buffer_tmp == NULL) {
- ssh_set_error_oom(session);
- return SSH_ERROR;
- }
- while(total < count){
- r=ssh_channel_read(channel, buffer_tmp, count - total, is_stderr);
- if(r<0){
- ssh_buffer_pass_bytes_end(buffer, count);
- return r;
+ buffer_tmp = ssh_buffer_allocate(buffer, count);
+ if (buffer_tmp == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
}
- if(r==0){
- /* Rollback the unused space */
- ssh_buffer_pass_bytes_end(buffer, count - total);
- return total;
+ while (total < count) {
+ r = ssh_channel_read(channel, buffer_tmp, count - total, is_stderr);
+ if (r < 0) {
+ ssh_buffer_pass_bytes_end(buffer, count);
+ return r;
+ }
+ if (r == 0) {
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(buffer, count - total);
+ return total;
+ }
+ total += r;
}
- total += r;
- }
- return total;
+ return total;
}
struct ssh_channel_read_termination_struct {
ssh_channel channel;
- uint32_t count;
ssh_buffer buffer;
};
static int ssh_channel_read_termination(void *s)
{
struct ssh_channel_read_termination_struct *ctx = s;
- if (ssh_buffer_get_len(ctx->buffer) >= ctx->count ||
+ if (ssh_buffer_get_len(ctx->buffer) >= 1 ||
ctx->channel->remote_eof ||
ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR)
return 1;
@@ -2982,8 +3100,6 @@ static int ssh_channel_read_termination(void *s)
return 0;
}
-/* TODO: FIXME Fix the blocking behaviours */
-
/**
* @brief Reads data from a channel.
*
@@ -2995,12 +3111,11 @@ static int ssh_channel_read_termination(void *s)
*
* @param[in] is_stderr A boolean value to mark reading from the stderr flow.
*
- * @return The number of bytes read, 0 on end of file or SSH_ERROR
- * on error. In nonblocking mode it can return 0 if no data
- * is available or SSH_AGAIN.
+ * @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
+ * on timeout and `SSH_ERROR` on error.
*
* @warning This function may return less than count bytes of data, and won't
- * block until count bytes have been read.
+ * block until count bytes have been read.
*/
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
{
@@ -3025,9 +3140,8 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
* @param[in] timeout_ms A timeout in milliseconds. A value of -1 means
* infinite timeout.
*
- * @return The number of bytes read, 0 on end of file or SSH_ERROR
- * on error. In nonblocking mode it Can return 0 if no data
- * is available or SSH_AGAIN.
+ * @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
+ * on timeout, `SSH_ERROR` on error.
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
@@ -3038,109 +3152,96 @@ int ssh_channel_read_timeout(ssh_channel channel,
int is_stderr,
int timeout_ms)
{
- ssh_session session;
- ssh_buffer stdbuf;
- uint32_t len;
- struct ssh_channel_read_termination_struct ctx;
- int rc;
+ ssh_session session = NULL;
+ ssh_buffer stdbuf = NULL;
+ uint32_t len;
+ struct ssh_channel_read_termination_struct ctx;
+ int rc;
- if(channel == NULL) {
- return SSH_ERROR;
- }
- if(dest == NULL) {
- ssh_set_error_invalid(channel->session);
- return SSH_ERROR;
- }
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+ if (dest == NULL) {
+ ssh_set_error_invalid(channel->session);
+ return SSH_ERROR;
+ }
- session = channel->session;
- stdbuf = channel->stdout_buffer;
+ session = channel->session;
+ stdbuf = channel->stdout_buffer;
- if (count == 0) {
- return 0;
- }
+ if (count == 0) {
+ return 0;
+ }
- if (is_stderr) {
- stdbuf=channel->stderr_buffer;
- }
+ if (is_stderr) {
+ stdbuf = channel->stderr_buffer;
+ }
- /*
- * We may have problem if the window is too small to accept as much data
- * as asked
- */
- SSH_LOG(SSH_LOG_PACKET,
- "Read (%" PRIu32 ") buffered : %" PRIu32 " bytes. Window: %" PRIu32,
- count,
- ssh_buffer_get_len(stdbuf),
- channel->local_window);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Read (%" PRIu32 ") buffered : %" PRIu32 " bytes. Window: %" PRIu32,
+ count,
+ ssh_buffer_get_len(stdbuf),
+ channel->local_window);
- if (count > ssh_buffer_get_len(stdbuf) + channel->local_window) {
- if (grow_window(session, channel, count - ssh_buffer_get_len(stdbuf)) < 0) {
- return -1;
- }
- }
+ /* block reading until at least one byte has been read
+ * and ignore the trivial case count=0
+ */
+ ctx.channel = channel;
+ ctx.buffer = stdbuf;
- /* block reading until at least one byte has been read
- * and ignore the trivial case count=0
- */
- ctx.channel = channel;
- ctx.buffer = stdbuf;
- ctx.count = 1;
+ if (timeout_ms < SSH_TIMEOUT_DEFAULT) {
+ timeout_ms = SSH_TIMEOUT_INFINITE;
+ }
- if (timeout_ms < SSH_TIMEOUT_DEFAULT) {
- timeout_ms = SSH_TIMEOUT_INFINITE;
- }
+ rc = ssh_handle_packets_termination(session,
+ timeout_ms,
+ ssh_channel_read_termination,
+ &ctx);
+ if (rc == SSH_ERROR || rc == SSH_AGAIN) {
+ return rc;
+ }
- rc = ssh_handle_packets_termination(session,
- timeout_ms,
- ssh_channel_read_termination,
- &ctx);
- if (rc == SSH_ERROR){
- return rc;
- }
+ /*
+ * If the channel is closed or in an error state, reading from it is an
+ * error
+ */
+ if (session->session_state == SSH_SESSION_STATE_ERROR) {
+ return SSH_ERROR;
+ }
+ /* If the server closed the channel properly, there is nothing to do */
+ if (channel->remote_eof && ssh_buffer_get_len(stdbuf) == 0) {
+ return 0;
+ }
+ if (channel->state == SSH_CHANNEL_STATE_CLOSED) {
+ ssh_set_error(session, SSH_FATAL, "Remote channel is closed.");
+ return SSH_ERROR;
+ }
+ len = ssh_buffer_get_len(stdbuf);
+ /* Read count bytes if len is greater, everything otherwise */
+ len = (len > count ? count : len);
+ memcpy(dest, ssh_buffer_get(stdbuf), len);
+ ssh_buffer_pass_bytes(stdbuf, len);
+ if (channel->counter != NULL) {
+ channel->counter->in_bytes += len;
+ }
+ /* Try completing the delayed_close */
+ if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
+ channel->state = SSH_CHANNEL_STATE_CLOSED;
+ }
- /*
- * If the channel is closed or in an error state, reading from it is an error
- */
- if (session->session_state == SSH_SESSION_STATE_ERROR) {
- return SSH_ERROR;
- }
- /* If the server closed the channel properly, there is nothing to do */
- if (channel->remote_eof && ssh_buffer_get_len(stdbuf) == 0) {
- return 0;
- }
- if (channel->state == SSH_CHANNEL_STATE_CLOSED) {
- ssh_set_error(session,
- SSH_FATAL,
- "Remote channel is closed.");
- return SSH_ERROR;
- }
- len = ssh_buffer_get_len(stdbuf);
- /* Read count bytes if len is greater, everything otherwise */
- len = (len > count ? count : len);
- memcpy(dest, ssh_buffer_get(stdbuf), len);
- ssh_buffer_pass_bytes(stdbuf,len);
- if (channel->counter != NULL) {
- channel->counter->in_bytes += len;
- }
- /* Try completing the delayed_close */
- if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
- channel->state = SSH_CHANNEL_STATE_CLOSED;
- }
- /* Authorize some buffering while userapp is busy */
- if (channel->local_window < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
}
- }
- return len;
+ return len;
}
/**
* @brief Do a nonblocking read on the channel.
*
* A nonblocking read on the specified channel. it will return <= count bytes of
- * data read atomically.
+ * data read atomically. It will also trigger any callbacks set on the channel.
*
* @param[in] channel The channel to read from.
*
@@ -3150,10 +3251,8 @@ int ssh_channel_read_timeout(ssh_channel channel,
*
* @param[in] is_stderr A boolean to select the stderr stream.
*
- * @return The number of bytes read, 0 if nothing is available or
- * SSH_ERROR on error.
- *
- * @warning Don't forget to check for EOF as it would return 0 here.
+ * @return The number of bytes read, `SSH_AGAIN` if nothing is
+ * available, `SSH_ERROR` on error, and `SSH_EOF` if the channel is EOF.
*
* @see ssh_channel_is_eof()
*/
@@ -3162,7 +3261,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
uint32_t count,
int is_stderr)
{
- ssh_session session;
+ ssh_session session = NULL;
uint32_t to_read;
int rc;
int blocking;
@@ -3203,16 +3302,18 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
/**
* @brief Polls a channel for data to read.
*
+ * If callbacks are set on the channel, they will be called.
+ *
* @param[in] channel The channel to poll.
*
* @param[in] is_stderr A boolean to select the stderr stream.
*
* @return The number of bytes available for reading, 0 if nothing
- * is available or SSH_ERROR on error.
+ * is available or `SSH_ERROR` on error.
* When a channel is freed the function returns
- * SSH_ERROR immediately.
+ * `SSH_ERROR` immediately.
*
- * @warning When the channel is in EOF state, the function returns SSH_EOF.
+ * @warning When the channel is in EOF state, the function returns `SSH_EOF`.
*
* @see ssh_channel_is_eof()
*/
@@ -3230,7 +3331,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr)
stdbuf = channel->stderr_buffer;
}
- if (ssh_buffer_get_len(stdbuf) == 0 && channel->remote_eof == 0) {
+ if (channel->remote_eof == 0) {
if (channel->session->session_state == SSH_SESSION_STATE_ERROR){
return SSH_ERROR;
}
@@ -3255,25 +3356,26 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr)
*
* @param[in] channel The channel to poll.
* @param[in] timeout Set an upper limit on the time for which this function
- * will block, in milliseconds. Specifying a negative value
- * means an infinite timeout. This parameter is passed to
- * the poll() function.
+ * will block, in milliseconds. Specifying a negative
+ * value means an infinite timeout. This parameter is
+ * passed to the poll() function.
* @param[in] is_stderr A boolean to select the stderr stream.
*
* @return The number of bytes available for reading,
* 0 if nothing is available (timeout elapsed),
- * SSH_EOF on end of file,
- * SSH_ERROR on error.
+ * `SSH_EOF` on end of file,
+ * `SSH_ERROR` on error.
*
- * @warning When the channel is in EOF state, the function returns SSH_EOF.
- * When a channel is freed the function returns SSH_ERROR immediately.
+ * @warning When the channel is in EOF state, the function returns `SSH_EOF`.
+ * When a channel is freed the function returns `SSH_ERROR`
+ * immediately.
*
* @see ssh_channel_is_eof()
*/
int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
{
- ssh_session session;
- ssh_buffer stdbuf;
+ ssh_session session = NULL;
+ ssh_buffer stdbuf = NULL;
struct ssh_channel_read_termination_struct ctx;
size_t len;
int rc;
@@ -3290,7 +3392,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
}
ctx.buffer = stdbuf;
ctx.channel = channel;
- ctx.count = 1;
rc = ssh_handle_packets_termination(channel->session,
timeout,
ssh_channel_read_termination,
@@ -3341,17 +3442,95 @@ ssh_session ssh_channel_get_session(ssh_channel channel)
static int ssh_channel_exit_status_termination(void *c)
{
- ssh_channel channel = c;
- if(channel->exit_status != -1 ||
- /* When a channel is closed, no exit status message can
- * come anymore */
- (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
- channel->session->session_state == SSH_SESSION_STATE_ERROR)
- return 1;
- else
+ ssh_channel channel = c;
+ if (channel->exit.status ||
+ /* When a channel is closed, no exit status message can
+ * come anymore */
+ (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
+ channel->session->session_state == SSH_SESSION_STATE_ERROR)
+ {
+ return 1;
+ }
return 0;
}
+/**
+ * @brief Get the exit state of the channel (error code from the executed
+ * instruction or signal).
+ *
+ * @param[in] channel The channel to get the status from.
+ *
+ * @param[out] pexit_code A pointer to an uint32_t to store the exit status.
+ *
+ * @param[out] pexit_signal A pointer to store the exit signal as a string.
+ * The signal is without the SIG prefix, e.g. "TERM" or
+ * "KILL"). The caller has to free the memory.
+ *
+ * @param[out] pcore_dumped A pointer to store a boolean value if it dumped a
+ * core.
+ *
+ * @return `SSH_OK` on success, `SSH_AGAIN` if we don't have a
+ * status or an SSH error.
+ * @warning This function may block until a timeout (or never)
+ * if the other side is not willing to close the channel.
+ * When a channel is freed the function returns
+ * `SSH_ERROR` immediately.
+ *
+ * If you're looking for an async handling of this register a callback for the
+ * exit status!
+ *
+ * @see ssh_channel_exit_status_callback
+ * @see ssh_channel_exit_signal_callback
+ */
+int ssh_channel_get_exit_state(ssh_channel channel,
+ uint32_t *pexit_code,
+ char **pexit_signal,
+ int *pcore_dumped)
+{
+ ssh_session session = NULL;
+ int rc;
+
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
+ return SSH_ERROR;
+ }
+ session = channel->session;
+
+ rc = ssh_handle_packets_termination(channel->session,
+ SSH_TIMEOUT_DEFAULT,
+ ssh_channel_exit_status_termination,
+ channel);
+ if (rc == SSH_ERROR || channel->session->session_state ==
+ SSH_SESSION_STATE_ERROR) {
+ return SSH_ERROR;
+ }
+
+ /* If we don't have any kind of exit state, return SSH_AGAIN */
+ if (!channel->exit.status) {
+ return SSH_AGAIN;
+ }
+
+ if (pexit_code != NULL) {
+ *pexit_code = channel->exit.code;
+ }
+
+ if (pexit_signal != NULL) {
+ *pexit_signal = NULL;
+ if (channel->exit.signal != NULL) {
+ *pexit_signal = strdup(channel->exit.signal);
+ if (pexit_signal == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ }
+ }
+
+ if (pcore_dumped != NULL) {
+ *pcore_dumped = channel->exit.core_dumped;
+ }
+
+ return SSH_OK;
+}
+
/**
* @brief Get the exit status of the channel (error code from the executed
* instruction).
@@ -3359,31 +3538,29 @@ static int ssh_channel_exit_status_termination(void *c)
* @param[in] channel The channel to get the status from.
*
* @return The exit status, -1 if no exit status has been returned
- * (yet), or SSH_ERROR on error.
+ * (yet), or `SSH_ERROR` on error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
* When a channel is freed the function returns
- * SSH_ERROR immediately.
+ * `SSH_ERROR` immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status.
*
* @see ssh_channel_exit_status_callback
+ * @deprecated Please use ssh_channel_exit_state()
*/
int ssh_channel_get_exit_status(ssh_channel channel)
{
- int rc;
- if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
- return SSH_ERROR;
- }
- rc = ssh_handle_packets_termination(channel->session,
- SSH_TIMEOUT_DEFAULT,
- ssh_channel_exit_status_termination,
- channel);
- if (rc == SSH_ERROR || channel->session->session_state ==
- SSH_SESSION_STATE_ERROR)
- return SSH_ERROR;
- return channel->exit_status;
+ uint32_t exit_status = (uint32_t)-1;
+ int rc;
+
+ rc = ssh_channel_get_exit_state(channel, &exit_status, NULL, NULL);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+
+ return exit_status;
}
/*
@@ -3400,50 +3577,54 @@ channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
ssh_channel *echans, ssh_channel *rout,
ssh_channel *wout, ssh_channel *eout)
{
- ssh_channel chan;
- int i;
- int j = 0;
+ ssh_channel chan = NULL;
+ int i;
+ int j = 0;
- for (i = 0; rchans[i] != NULL; i++) {
- chan = rchans[i];
+ for (i = 0; rchans[i] != NULL; i++) {
+ chan = rchans[i];
- while (ssh_channel_is_open(chan) && ssh_socket_data_available(chan->session->socket)) {
- ssh_handle_packets(chan->session, SSH_TIMEOUT_NONBLOCKING);
- }
+ while (ssh_channel_is_open(chan) &&
+ ssh_socket_data_available(chan->session->socket)) {
+ ssh_handle_packets(chan->session, SSH_TIMEOUT_NONBLOCKING);
+ }
- if ((chan->stdout_buffer && ssh_buffer_get_len(chan->stdout_buffer) > 0) ||
- (chan->stderr_buffer && ssh_buffer_get_len(chan->stderr_buffer) > 0) ||
- chan->remote_eof) {
- rout[j] = chan;
- j++;
+ if ((chan->stdout_buffer &&
+ ssh_buffer_get_len(chan->stdout_buffer) > 0) ||
+ (chan->stderr_buffer &&
+ ssh_buffer_get_len(chan->stderr_buffer) > 0) ||
+ chan->remote_eof) {
+ rout[j] = chan;
+ j++;
+ }
}
- }
- rout[j] = NULL;
+ rout[j] = NULL;
- j = 0;
- for(i = 0; wchans[i] != NULL; i++) {
- chan = wchans[i];
- /* It's not our business to seek if the file descriptor is writable */
- if (ssh_socket_data_writable(chan->session->socket) &&
- ssh_channel_is_open(chan) && (chan->remote_window > 0)) {
- wout[j] = chan;
- j++;
+ j = 0;
+ for (i = 0; wchans[i] != NULL; i++) {
+ chan = wchans[i];
+ /* It's not our business to seek if the file descriptor is writable */
+ if (ssh_socket_data_writable(chan->session->socket) &&
+ ssh_channel_is_open(chan) && (chan->remote_window > 0)) {
+ wout[j] = chan;
+ j++;
+ }
}
- }
- wout[j] = NULL;
+ wout[j] = NULL;
- j = 0;
- for (i = 0; echans[i] != NULL; i++) {
- chan = echans[i];
+ j = 0;
+ for (i = 0; echans[i] != NULL; i++) {
+ chan = echans[i];
- if (!ssh_socket_is_open(chan->session->socket) || ssh_channel_is_closed(chan)) {
- eout[j] = chan;
- j++;
+ if (!ssh_socket_is_open(chan->session->socket) ||
+ ssh_channel_is_closed(chan)) {
+ eout[j] = chan;
+ j++;
+ }
}
- }
- eout[j] = NULL;
+ eout[j] = NULL;
- return 0;
+ return 0;
}
/* Just count number of pointers in the array */
@@ -3474,131 +3655,142 @@ static size_t count_ptrs(ssh_channel *ptrs)
*
* @param[in] timeout Timeout as defined by select(2).
*
- * @return SSH_OK on a successful operation, SSH_EINTR if the
+ * @return `SSH_OK` on a successful operation, `SSH_EINTR` if the
* select(2) syscall was interrupted, then relaunch the
- * function, or SSH_ERROR on error.
+ * function, or `SSH_ERROR` on error.
*/
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
ssh_channel *exceptchans, struct timeval * timeout)
{
- ssh_channel *rchans, *wchans, *echans;
- ssh_channel dummy = NULL;
- ssh_event event = NULL;
- int rc;
- int i;
- int tm, tm_base;
- int firstround=1;
- struct ssh_timestamp ts;
-
- if (timeout != NULL)
- tm_base = timeout->tv_sec * 1000 + timeout->tv_usec/1000;
- else
- tm_base = SSH_TIMEOUT_INFINITE;
- ssh_timestamp_init(&ts);
- tm = tm_base;
- /* don't allow NULL pointers */
- if (readchans == NULL) {
- readchans = &dummy;
- }
+ ssh_channel *rchans = NULL, *wchans = NULL, *echans = NULL;
+ ssh_channel dummy = NULL;
+ ssh_event event = NULL;
+ int rc;
+ int i;
+ int tm, tm_base;
+ int firstround = 1;
+ struct ssh_timestamp ts;
- if (writechans == NULL) {
- writechans = &dummy;
- }
+ if (timeout != NULL)
+ tm_base = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+ else
+ tm_base = SSH_TIMEOUT_INFINITE;
+ ssh_timestamp_init(&ts);
+ tm = tm_base;
+ /* don't allow NULL pointers */
+ if (readchans == NULL) {
+ readchans = &dummy;
+ }
- if (exceptchans == NULL) {
- exceptchans = &dummy;
- }
+ if (writechans == NULL) {
+ writechans = &dummy;
+ }
- if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NULL) {
- /* No channel to poll?? Go away! */
- return 0;
- }
+ if (exceptchans == NULL) {
+ exceptchans = &dummy;
+ }
- /* Prepare the outgoing temporary arrays */
- rchans = calloc(count_ptrs(readchans) + 1, sizeof(ssh_channel));
- if (rchans == NULL) {
- return SSH_ERROR;
- }
+ if (readchans[0] == NULL && writechans[0] == NULL &&
+ exceptchans[0] == NULL) {
+ /* No channel to poll?? Go away! */
+ return 0;
+ }
- wchans = calloc(count_ptrs(writechans) + 1, sizeof(ssh_channel));
- if (wchans == NULL) {
- SAFE_FREE(rchans);
- return SSH_ERROR;
- }
+ /* Prepare the outgoing temporary arrays */
+ rchans = calloc(count_ptrs(readchans) + 1, sizeof(ssh_channel));
+ if (rchans == NULL) {
+ return SSH_ERROR;
+ }
- echans = calloc(count_ptrs(exceptchans) + 1, sizeof(ssh_channel));
- if (echans == NULL) {
- SAFE_FREE(rchans);
- SAFE_FREE(wchans);
- return SSH_ERROR;
- }
+ wchans = calloc(count_ptrs(writechans) + 1, sizeof(ssh_channel));
+ if (wchans == NULL) {
+ SAFE_FREE(rchans);
+ return SSH_ERROR;
+ }
- /*
- * First, try without doing network stuff then, use the ssh_poll
- * infrastructure to poll on all sessions.
- */
- do {
- channel_protocol_select(readchans, writechans, exceptchans,
- rchans, wchans, echans);
- if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) {
- /* At least one channel has an event */
- break;
+ echans = calloc(count_ptrs(exceptchans) + 1, sizeof(ssh_channel));
+ if (echans == NULL) {
+ SAFE_FREE(rchans);
+ SAFE_FREE(wchans);
+ return SSH_ERROR;
}
- /* Add all channels' sessions right into an event object */
- if (event == NULL) {
- event = ssh_event_new();
- if (event == NULL) {
- SAFE_FREE(rchans);
- SAFE_FREE(wchans);
- SAFE_FREE(echans);
- return SSH_ERROR;
- }
- for (i = 0; readchans[i] != NULL; i++) {
- ssh_poll_get_default_ctx(readchans[i]->session);
- ssh_event_add_session(event, readchans[i]->session);
- }
- for (i = 0; writechans[i] != NULL; i++) {
- ssh_poll_get_default_ctx(writechans[i]->session);
- ssh_event_add_session(event, writechans[i]->session);
- }
- for (i = 0; exceptchans[i] != NULL; i++) {
- ssh_poll_get_default_ctx(exceptchans[i]->session);
- ssh_event_add_session(event, exceptchans[i]->session);
- }
+ /*
+ * First, try without doing network stuff then, use the ssh_poll
+ * infrastructure to poll on all sessions.
+ */
+ do {
+ channel_protocol_select(readchans,
+ writechans,
+ exceptchans,
+ rchans,
+ wchans,
+ echans);
+ if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) {
+ /* At least one channel has an event */
+ break;
+ }
+ /* Add all channels' sessions right into an event object */
+ if (event == NULL) {
+ event = ssh_event_new();
+ if (event == NULL) {
+ SAFE_FREE(rchans);
+ SAFE_FREE(wchans);
+ SAFE_FREE(echans);
+
+ return SSH_ERROR;
+ }
+ for (i = 0; readchans[i] != NULL; i++) {
+ ssh_poll_get_default_ctx(readchans[i]->session);
+ ssh_event_add_session(event, readchans[i]->session);
+ }
+ for (i = 0; writechans[i] != NULL; i++) {
+ ssh_poll_get_default_ctx(writechans[i]->session);
+ ssh_event_add_session(event, writechans[i]->session);
+ }
+ for (i = 0; exceptchans[i] != NULL; i++) {
+ ssh_poll_get_default_ctx(exceptchans[i]->session);
+ ssh_event_add_session(event, exceptchans[i]->session);
+ }
+ }
+ /* Get out if the timeout has elapsed */
+ if (!firstround && ssh_timeout_elapsed(&ts, tm_base)) {
+ break;
+ }
+ /* Here we go */
+ rc = ssh_event_dopoll(event, tm);
+ if (rc != SSH_OK) {
+ SAFE_FREE(rchans);
+ SAFE_FREE(wchans);
+ SAFE_FREE(echans);
+ ssh_event_free(event);
+ return rc;
+ }
+ tm = ssh_timeout_update(&ts, tm_base);
+ firstround = 0;
+ } while (1);
+
+ if (readchans != &dummy) {
+ memcpy(readchans,
+ rchans,
+ (count_ptrs(rchans) + 1) * sizeof(ssh_channel));
}
- /* Get out if the timeout has elapsed */
- if (!firstround && ssh_timeout_elapsed(&ts, tm_base)){
- break;
+ if (writechans != &dummy) {
+ memcpy(writechans,
+ wchans,
+ (count_ptrs(wchans) + 1) * sizeof(ssh_channel));
}
- /* Here we go */
- rc = ssh_event_dopoll(event,tm);
- if (rc != SSH_OK){
- SAFE_FREE(rchans);
- SAFE_FREE(wchans);
- SAFE_FREE(echans);
- ssh_event_free(event);
- return rc;
+ if (exceptchans != &dummy) {
+ memcpy(exceptchans,
+ echans,
+ (count_ptrs(echans) + 1) * sizeof(ssh_channel));
}
- tm = ssh_timeout_update(&ts, tm_base);
- firstround=0;
- } while(1);
-
- if (readchans != &dummy) {
- memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel));
- }
- if (writechans != &dummy) {
- memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel));
- }
- if (exceptchans != &dummy) {
- memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel));
- }
- SAFE_FREE(rchans);
- SAFE_FREE(wchans);
- SAFE_FREE(echans);
- if(event)
- ssh_event_free(event);
- return 0;
+ SAFE_FREE(rchans);
+ SAFE_FREE(wchans);
+ SAFE_FREE(echans);
+ if (event)
+ ssh_event_free(event);
+ return 0;
}
/**
@@ -3662,60 +3854,60 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len
* @param[in] localport The source port (your local computer). It's optional
* and for logging purpose.
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
- * @warning This function does not bind the local port and does not automatically
- * forward the content of a socket to the channel. You still have to
- * use ssh_channel_read and ssh_channel_write for this.
+ * @warning This function does not bind the local port and does not
+ * automatically forward the content of a socket to the channel. You
+ * still have to use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport)
{
- ssh_session session;
- ssh_buffer payload = NULL;
- int rc = SSH_ERROR;
+ ssh_session session = NULL;
+ ssh_buffer payload = NULL;
+ int rc = SSH_ERROR;
- if(channel == NULL) {
- return rc;
- }
- if(remotehost == NULL || sourcehost == NULL) {
- ssh_set_error_invalid(channel->session);
- return rc;
- }
+ if (channel == NULL) {
+ return rc;
+ }
+ if (remotehost == NULL || sourcehost == NULL) {
+ ssh_set_error_invalid(channel->session);
+ return rc;
+ }
- session = channel->session;
+ session = channel->session;
- if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
- goto pending;
- payload = ssh_buffer_new();
- if (payload == NULL) {
- ssh_set_error_oom(session);
- goto error;
- }
- rc = ssh_buffer_pack(payload,
- "sdsd",
- remotehost,
- remoteport,
- sourcehost,
- localport);
- if (rc != SSH_OK){
- ssh_set_error_oom(session);
- goto error;
- }
+ if (channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
+ goto pending;
+ payload = ssh_buffer_new();
+ if (payload == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ rc = ssh_buffer_pack(payload,
+ "sdsd",
+ remotehost,
+ remoteport,
+ sourcehost,
+ localport);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
pending:
- rc = channel_open(channel,
- "forwarded-tcpip",
- CHANNEL_INITIAL_WINDOW,
- CHANNEL_MAX_PACKET,
- payload);
+ rc = channel_open(channel,
+ "forwarded-tcpip",
+ WINDOW_DEFAULT,
+ CHANNEL_MAX_PACKET,
+ payload);
error:
- SSH_BUFFER_FREE(payload);
+ SSH_BUFFER_FREE(payload);
- return rc;
+ return rc;
}
/**
@@ -3727,58 +3919,55 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost
*
* @param[in] orig_port The source port (the local server).
*
- * @return SSH_OK on success,
- * SSH_ERROR if an error occurred,
- * SSH_AGAIN if in nonblocking mode and call has
+ * @return `SSH_OK` on success,
+ * `SSH_ERROR` if an error occurred,
+ * `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
- * @warning This function does not bind the local port and does not automatically
- * forward the content of a socket to the channel. You still have to
- * use shh_channel_read and ssh_channel_write for this.
+ * @warning This function does not bind the local port and does not
+ * automatically forward the content of a socket to the channel. You
+ * still have to use shh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_x11(ssh_channel channel,
const char *orig_addr, int orig_port)
{
- ssh_session session;
- ssh_buffer payload = NULL;
- int rc = SSH_ERROR;
+ ssh_session session = NULL;
+ ssh_buffer payload = NULL;
+ int rc = SSH_ERROR;
- if(channel == NULL) {
- return rc;
- }
- if(orig_addr == NULL) {
- ssh_set_error_invalid(channel->session);
- return rc;
- }
- session = channel->session;
+ if (channel == NULL) {
+ return rc;
+ }
+ if (orig_addr == NULL) {
+ ssh_set_error_invalid(channel->session);
+ return rc;
+ }
+ session = channel->session;
- if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
- goto pending;
+ if (channel->state != SSH_CHANNEL_STATE_NOT_OPEN)
+ goto pending;
- payload = ssh_buffer_new();
- if (payload == NULL) {
- ssh_set_error_oom(session);
- goto error;
- }
+ payload = ssh_buffer_new();
+ if (payload == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- rc = ssh_buffer_pack(payload,
- "sd",
- orig_addr,
- orig_port);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
+ rc = ssh_buffer_pack(payload, "sd", orig_addr, orig_port);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
pending:
- rc = channel_open(channel,
- "x11",
- CHANNEL_INITIAL_WINDOW,
- CHANNEL_MAX_PACKET,
- payload);
+ rc = channel_open(channel,
+ "x11",
+ WINDOW_DEFAULT,
+ CHANNEL_MAX_PACKET,
+ payload);
error:
- SSH_BUFFER_FREE(payload);
+ SSH_BUFFER_FREE(payload);
- return rc;
+ return rc;
}
/**
@@ -3791,7 +3980,7 @@ int ssh_channel_open_x11(ssh_channel channel,
*
* @param[in] exit_status The exit status to send
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred.
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*/
int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
{
@@ -3835,7 +4024,7 @@ int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
* @param[in] errmsg A CRLF explanation text about the error condition
* @param[in] lang The language used in the message (format: RFC 3066)
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred
+ * @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
int core, const char *errmsg, const char *lang)
diff --git a/src/client.c b/src/client.c
index a54bac60..0d01d373 100644
--- a/src/client.c
+++ b/src/client.c
@@ -30,14 +30,15 @@
#include
#endif
-#include "libssh/priv.h"
-#include "libssh/ssh2.h"
#include "libssh/buffer.h"
-#include "libssh/packet.h"
+#include "libssh/kex-gss.h"
+#include "libssh/dh.h"
#include "libssh/options.h"
-#include "libssh/socket.h"
+#include "libssh/packet.h"
+#include "libssh/priv.h"
#include "libssh/session.h"
-#include "libssh/dh.h"
+#include "libssh/socket.h"
+#include "libssh/ssh2.h"
#ifdef WITH_GEX
#include "libssh/dh-gex.h"
#endif /* WITH_GEX */
@@ -46,6 +47,13 @@
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/kex.h"
+#include "libssh/hybrid_mlkem.h"
+
+#ifndef _WIN32
+#ifdef HAVE_PTHREAD
+extern int proxy_disconnect;
+#endif /* HAVE_PTHREAD */
+#endif /* _WIN32 */
#define set_status(session, status) do {\
if (session->common.callbacks && session->common.callbacks->connect_status_function) \
@@ -185,13 +193,13 @@ int ssh_send_banner(ssh_session session, int server)
int rc = SSH_ERROR;
if (server == 1) {
- if (session->opts.custombanner == NULL){
+ if (session->server_opts.custombanner == NULL) {
session->serverbanner = strdup(banner);
if (session->serverbanner == NULL) {
goto end;
}
} else {
- len = strlen(session->opts.custombanner);
+ len = strlen(session->server_opts.custombanner);
session->serverbanner = malloc(len + 8 + 1);
if(session->serverbanner == NULL) {
goto end;
@@ -199,7 +207,7 @@ int ssh_send_banner(ssh_session session, int server)
snprintf(session->serverbanner,
len + 8 + 1,
"SSH-2.0-%s",
- session->opts.custombanner);
+ session->server_opts.custombanner);
}
snprintf(buffer,
@@ -220,7 +228,7 @@ int ssh_send_banner(ssh_session session, int server)
terminator);
}
- rc = ssh_socket_write(session->socket, buffer, strlen(buffer));
+ rc = ssh_socket_write(session->socket, buffer, (uint32_t)strlen(buffer));
if (rc == SSH_ERROR) {
goto end;
}
@@ -229,8 +237,8 @@ int ssh_send_banner(ssh_session session, int server)
ssh_pcap_context_write(session->pcap_ctx,
SSH_PCAP_DIR_OUT,
buffer,
- strlen(buffer),
- strlen(buffer));
+ (uint32_t)strlen(buffer),
+ (uint32_t)strlen(buffer));
}
#endif
@@ -248,45 +256,68 @@ int ssh_send_banner(ssh_session session, int server)
*/
int dh_handshake(ssh_session session)
{
- int rc = SSH_AGAIN;
+ int rc = SSH_AGAIN;
- SSH_LOG(SSH_LOG_TRACE, "dh_handshake_state = %d, kex_type = %d",
- session->dh_handshake_state, session->next_crypto->kex_type);
+ SSH_LOG(SSH_LOG_TRACE,
+ "dh_handshake_state = %d, kex_type = %d",
+ session->dh_handshake_state,
+ session->next_crypto->kex_type);
- switch (session->dh_handshake_state) {
+ switch (session->dh_handshake_state) {
case DH_STATE_INIT:
- switch(session->next_crypto->kex_type){
+ switch (session->next_crypto->kex_type) {
+#ifdef WITH_GSSAPI
+ case SSH_GSS_KEX_DH_GROUP14_SHA256:
+ case SSH_GSS_KEX_DH_GROUP16_SHA512:
+ case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
+ case SSH_GSS_KEX_CURVE25519_SHA256:
+ rc = ssh_client_gss_kex_init(session);
+ break;
+#endif
case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
case SSH_KEX_DH_GROUP16_SHA512:
case SSH_KEX_DH_GROUP18_SHA512:
- rc = ssh_client_dh_init(session);
- break;
+ rc = ssh_client_dh_init(session);
+ break;
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
case SSH_KEX_DH_GEX_SHA256:
- rc = ssh_client_dhgex_init(session);
- break;
+ rc = ssh_client_dhgex_init(session);
+ break;
#endif /* WITH_GEX */
#ifdef HAVE_ECDH
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_ECDH_SHA2_NISTP384:
case SSH_KEX_ECDH_SHA2_NISTP521:
- rc = ssh_client_ecdh_init(session);
- break;
+ rc = ssh_client_ecdh_init(session);
+ break;
#endif
#ifdef HAVE_CURVE25519
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
- rc = ssh_client_curve25519_init(session);
- break;
+ rc = ssh_client_curve25519_init(session);
+ break;
+#endif
+#ifdef HAVE_SNTRUP761
+ case SSH_KEX_SNTRUP761X25519_SHA512:
+ case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
+ rc = ssh_client_sntrup761x25519_init(session);
+ break;
#endif
+ case SSH_KEX_MLKEM768X25519_SHA256:
+ case SSH_KEX_MLKEM768NISTP256_SHA256:
+#ifdef HAVE_MLKEM1024
+ case SSH_KEX_MLKEM1024NISTP384_SHA384:
+#endif
+ rc = ssh_client_hybrid_mlkem_init(session);
+ break;
default:
- rc = SSH_ERROR;
- }
+ rc = SSH_ERROR;
+ }
- break;
+ break;
case DH_STATE_INIT_SENT:
/* wait until ssh_packet_dh_reply is called */
break;
@@ -294,15 +325,17 @@ int dh_handshake(ssh_session session)
/* wait until ssh_packet_newkeys is called */
break;
case DH_STATE_FINISHED:
- return SSH_OK;
+ return SSH_OK;
default:
- ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d",
- session->dh_handshake_state);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Invalid state in dh_handshake(): %d",
+ session->dh_handshake_state);
- return SSH_ERROR;
- }
+ return SSH_ERROR;
+ }
- return rc;
+ return rc;
}
static int ssh_service_request_termination(void *s)
@@ -486,9 +519,7 @@ static void ssh_client_connection_callback(ssh_session session)
return;
error:
- ssh_socket_close(session->socket);
- session->alive = 0;
- session->session_state = SSH_SESSION_STATE_ERROR;
+ ssh_session_socket_close(session);
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session));
}
@@ -589,13 +620,24 @@ int ssh_connect(ssh_session session)
if (session->opts.fd != SSH_INVALID_SOCKET) {
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
- ssh_socket_set_fd(session->socket, session->opts.fd);
- ret = SSH_OK;
+ ret = ssh_socket_set_fd(session->socket, session->opts.fd);
#ifndef _WIN32
+#ifdef HAVE_PTHREAD
+ } else if (ssh_libssh_proxy_jumps() &&
+ ssh_list_count(session->opts.proxy_jumps) != 0) {
+ ret = ssh_socket_connect_proxyjump(session->socket);
+#endif /* HAVE_PTHREAD */
+#endif /* _WIN32 */
} else if (session->opts.ProxyCommand != NULL) {
+#ifdef WITH_EXEC
ret = ssh_socket_connect_proxycommand(session->socket,
session->opts.ProxyCommand);
-#endif
+#else
+ ssh_set_error(session,
+ SSH_FATAL,
+ "The libssh is built without support for proxy commands.");
+ ret = SSH_ERROR;
+#endif /* WITH_EXEC */
} else {
ret = ssh_socket_connect(session->socket,
session->opts.host,
@@ -755,7 +797,6 @@ ssh_session_set_disconnect_message(ssh_session session, const char *message)
return SSH_OK;
}
-
/**
* @brief Disconnect from a session (client or server).
*
@@ -770,13 +811,23 @@ ssh_session_set_disconnect_message(ssh_session session, const char *message)
void
ssh_disconnect(ssh_session session)
{
- struct ssh_iterator *it;
+ struct ssh_iterator *it = NULL;
int rc;
if (session == NULL) {
return;
}
+#ifndef _WIN32
+#ifdef HAVE_PTHREAD
+ /* Only send the disconnect to all other threads when the root session calls
+ * ssh_disconnect() */
+ if (session->proxy_root) {
+ proxy_disconnect = 1;
+ }
+#endif /* HAVE_PTHREAD */
+#endif /* _WIN32 */
+
if (session->disconnect_message == NULL) {
session->disconnect_message = strdup("Bye Bye") ;
if (session->disconnect_message == NULL) {
@@ -798,10 +849,7 @@ ssh_disconnect(ssh_session session)
}
ssh_packet_send(session);
- /* Do not close the socket, if the fd was set via options. */
- if (session->opts.fd == SSH_INVALID_SOCKET) {
- ssh_socket_close(session->socket);
- }
+ ssh_session_socket_close(session);
}
error:
@@ -814,6 +862,7 @@ ssh_disconnect(ssh_session session)
session->opts.fd = SSH_INVALID_SOCKET;
session->session_state = SSH_SESSION_STATE_DISCONNECTED;
session->pending_call_state = SSH_PENDING_CALL_NONE;
+ session->packet_state = PACKET_STATE_INIT;
while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));
@@ -873,7 +922,7 @@ ssh_disconnect(ssh_session session)
*/
const char *ssh_copyright(void)
{
- return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2023 "
+ return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2026 "
"Aris Adamantiadis, Andreas Schneider "
"and libssh contributors. "
"Distributed under the LGPL, please refer to COPYING "
diff --git a/src/config.c b/src/config.c
index 156b146e..0af0f798 100644
--- a/src/config.c
+++ b/src/config.c
@@ -39,6 +39,11 @@
# include
# include
# include
+# include
+# include
+#endif
+#ifdef HAVE_IFADDRS_H
+#include
#endif
#include "libssh/config_parser.h"
@@ -55,100 +60,104 @@
struct ssh_config_keyword_table_s {
const char *name;
enum ssh_config_opcode_e opcode;
+ bool cli_supported;
};
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
- { "host", SOC_HOST },
- { "match", SOC_MATCH },
- { "hostname", SOC_HOSTNAME },
- { "port", SOC_PORT },
- { "user", SOC_USERNAME },
- { "identityfile", SOC_IDENTITY },
- { "ciphers", SOC_CIPHERS },
- { "macs", SOC_MACS },
- { "compression", SOC_COMPRESSION },
- { "connecttimeout", SOC_TIMEOUT },
- { "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
- { "userknownhostsfile", SOC_KNOWNHOSTS },
- { "proxycommand", SOC_PROXYCOMMAND },
- { "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY },
- { "gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY },
- { "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS },
- { "include", SOC_INCLUDE },
- { "bindaddress", SOC_BINDADDRESS},
- { "globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE},
- { "loglevel", SOC_LOGLEVEL},
- { "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
- { "kexalgorithms", SOC_KEXALGORITHMS},
- { "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
- { "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
- { "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
- { "pubkeyauthentication", SOC_PUBKEYAUTHENTICATION},
- { "addkeystoagent", SOC_UNSUPPORTED},
- { "addressfamily", SOC_UNSUPPORTED},
- { "batchmode", SOC_UNSUPPORTED},
- { "canonicaldomains", SOC_UNSUPPORTED},
- { "canonicalizefallbacklocal", SOC_UNSUPPORTED},
- { "canonicalizehostname", SOC_UNSUPPORTED},
- { "canonicalizemaxdots", SOC_UNSUPPORTED},
- { "canonicalizepermittedcnames", SOC_UNSUPPORTED},
- { "certificatefile", SOC_UNSUPPORTED},
- { "kbdinteractiveauthentication", SOC_UNSUPPORTED},
- { "checkhostip", SOC_UNSUPPORTED},
- { "connectionattempts", SOC_UNSUPPORTED},
- { "enablesshkeysign", SOC_UNSUPPORTED},
- { "fingerprinthash", SOC_UNSUPPORTED},
- { "forwardagent", SOC_UNSUPPORTED},
- { "hashknownhosts", SOC_UNSUPPORTED},
- { "hostbasedauthentication", SOC_UNSUPPORTED},
- { "hostbasedacceptedalgorithms", SOC_UNSUPPORTED},
- { "hostkeyalias", SOC_UNSUPPORTED},
- { "identitiesonly", SOC_IDENTITIESONLY},
- { "identityagent", SOC_IDENTITYAGENT},
- { "ipqos", SOC_UNSUPPORTED},
- { "kbdinteractivedevices", SOC_UNSUPPORTED},
- { "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
- { "numberofpasswordprompts", SOC_UNSUPPORTED},
- { "pkcs11provider", SOC_UNSUPPORTED},
- { "preferredauthentications", SOC_UNSUPPORTED},
- { "proxyjump", SOC_PROXYJUMP},
- { "proxyusefdpass", SOC_UNSUPPORTED},
- { "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES},
- { "rekeylimit", SOC_REKEYLIMIT},
- { "remotecommand", SOC_UNSUPPORTED},
- { "revokedhostkeys", SOC_UNSUPPORTED},
- { "serveralivecountmax", SOC_UNSUPPORTED},
- { "serveraliveinterval", SOC_UNSUPPORTED},
- { "streamlocalbindmask", SOC_UNSUPPORTED},
- { "streamlocalbindunlink", SOC_UNSUPPORTED},
- { "syslogfacility", SOC_UNSUPPORTED},
- { "tcpkeepalive", SOC_UNSUPPORTED},
- { "updatehostkeys", SOC_UNSUPPORTED},
- { "verifyhostkeydns", SOC_UNSUPPORTED},
- { "visualhostkey", SOC_UNSUPPORTED},
- { "clearallforwardings", SOC_NA},
- { "controlmaster", SOC_CONTROLMASTER},
- { "controlpersist", SOC_NA},
- { "controlpath", SOC_CONTROLPATH},
- { "dynamicforward", SOC_NA},
- { "escapechar", SOC_NA},
- { "exitonforwardfailure", SOC_NA},
- { "forwardx11", SOC_NA},
- { "forwardx11timeout", SOC_NA},
- { "forwardx11trusted", SOC_NA},
- { "gatewayports", SOC_NA},
- { "ignoreunknown", SOC_NA},
- { "localcommand", SOC_NA},
- { "localforward", SOC_NA},
- { "permitlocalcommand", SOC_NA},
- { "remoteforward", SOC_NA},
- { "requesttty", SOC_NA},
- { "sendenv", SOC_NA},
- { "tunnel", SOC_NA},
- { "tunneldevice", SOC_NA},
- { "xauthlocation", SOC_NA},
- { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
- { NULL, SOC_UNKNOWN }
+ {"host", SOC_HOST, true},
+ {"match", SOC_MATCH, false},
+ {"hostname", SOC_HOSTNAME, true},
+ {"port", SOC_PORT, true},
+ {"user", SOC_USERNAME, true},
+ {"identityfile", SOC_IDENTITY, true},
+ {"ciphers", SOC_CIPHERS, true},
+ {"macs", SOC_MACS, true},
+ {"compression", SOC_COMPRESSION, true},
+ {"connecttimeout", SOC_TIMEOUT, true},
+ {"stricthostkeychecking", SOC_STRICTHOSTKEYCHECK, true},
+ {"userknownhostsfile", SOC_KNOWNHOSTS, true},
+ {"proxycommand", SOC_PROXYCOMMAND, true},
+ {"gssapiserveridentity", SOC_GSSAPISERVERIDENTITY, false},
+ {"gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY, false},
+ {"gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS, true},
+ {"include", SOC_INCLUDE, true},
+ {"bindaddress", SOC_BINDADDRESS, true},
+ {"globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE, true},
+ {"loglevel", SOC_LOGLEVEL, true},
+ {"hostkeyalgorithms", SOC_HOSTKEYALGORITHMS, true},
+ {"kexalgorithms", SOC_KEXALGORITHMS, true},
+ {"gssapiauthentication", SOC_GSSAPIAUTHENTICATION, true},
+ {"kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION, true},
+ {"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
+ {"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
+ {"addkeystoagent", SOC_UNSUPPORTED, true},
+ {"addressfamily", SOC_ADDRESSFAMILY, true},
+ {"batchmode", SOC_UNSUPPORTED, true},
+ {"canonicaldomains", SOC_UNSUPPORTED, true},
+ {"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
+ {"canonicalizehostname", SOC_UNSUPPORTED, true},
+ {"canonicalizemaxdots", SOC_UNSUPPORTED, true},
+ {"canonicalizepermittedcnames", SOC_UNSUPPORTED, true},
+ {"certificatefile", SOC_CERTIFICATE, true},
+ {"kbdinteractiveauthentication", SOC_UNSUPPORTED, true},
+ {"checkhostip", SOC_UNSUPPORTED, true},
+ {"connectionattempts", SOC_UNSUPPORTED, true},
+ {"enablesshkeysign", SOC_UNSUPPORTED, true},
+ {"fingerprinthash", SOC_UNSUPPORTED, true},
+ {"forwardagent", SOC_UNSUPPORTED, true},
+ {"hashknownhosts", SOC_UNSUPPORTED, true},
+ {"hostbasedauthentication", SOC_UNSUPPORTED, true},
+ {"hostbasedacceptedalgorithms", SOC_UNSUPPORTED, true},
+ {"hostkeyalias", SOC_UNSUPPORTED, true},
+ {"identitiesonly", SOC_IDENTITIESONLY, true},
+ {"identityagent", SOC_IDENTITYAGENT, true},
+ {"ipqos", SOC_UNSUPPORTED, true},
+ {"kbdinteractivedevices", SOC_UNSUPPORTED, true},
+ {"nohostauthenticationforlocalhost", SOC_UNSUPPORTED, true},
+ {"numberofpasswordprompts", SOC_UNSUPPORTED, true},
+ {"pkcs11provider", SOC_UNSUPPORTED, true},
+ {"preferredauthentications", SOC_UNSUPPORTED, true},
+ {"proxyjump", SOC_PROXYJUMP, true},
+ {"proxyusefdpass", SOC_UNSUPPORTED, true},
+ {"pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES, true},
+ {"rekeylimit", SOC_REKEYLIMIT, true},
+ {"remotecommand", SOC_UNSUPPORTED, true},
+ {"revokedhostkeys", SOC_UNSUPPORTED, true},
+ {"serveralivecountmax", SOC_UNSUPPORTED, true},
+ {"serveraliveinterval", SOC_UNSUPPORTED, true},
+ {"streamlocalbindmask", SOC_UNSUPPORTED, true},
+ {"streamlocalbindunlink", SOC_UNSUPPORTED, true},
+ {"syslogfacility", SOC_UNSUPPORTED, true},
+ {"tcpkeepalive", SOC_UNSUPPORTED, true},
+ {"updatehostkeys", SOC_UNSUPPORTED, true},
+ {"verifyhostkeydns", SOC_UNSUPPORTED, true},
+ {"visualhostkey", SOC_UNSUPPORTED, true},
+ {"clearallforwardings", SOC_NA, true},
+ {"controlmaster", SOC_NA, true},
+ {"controlpersist", SOC_NA, true},
+ {"controlpath", SOC_NA, true},
+ {"dynamicforward", SOC_NA, true},
+ {"escapechar", SOC_NA, true},
+ {"exitonforwardfailure", SOC_NA, true},
+ {"forwardx11", SOC_NA, true},
+ {"forwardx11timeout", SOC_NA, true},
+ {"forwardx11trusted", SOC_NA, true},
+ {"gatewayports", SOC_NA, true},
+ {"ignoreunknown", SOC_NA, true},
+ {"localcommand", SOC_NA, true},
+ {"localforward", SOC_NA, true},
+ {"permitlocalcommand", SOC_NA, true},
+ {"remoteforward", SOC_NA, true},
+ {"requesttty", SOC_NA, true},
+ {"sendenv", SOC_NA, true},
+ {"tunnel", SOC_NA, true},
+ {"tunneldevice", SOC_NA, true},
+ {"xauthlocation", SOC_NA, true},
+ {"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true},
+ {"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true},
+ {"gssapikeyexchange", SOC_GSSAPIKEYEXCHANGE, true},
+ {"gssapikexalgorithms", SOC_GSSAPIKEXALGORITHMS, true},
+ {NULL, SOC_UNKNOWN, false},
};
enum ssh_config_match_e {
@@ -160,7 +169,8 @@ enum ssh_config_match_e {
MATCH_HOST,
MATCH_ORIGINALHOST,
MATCH_USER,
- MATCH_LOCALUSER
+ MATCH_LOCALUSER,
+ MATCH_LOCALNETWORK
};
struct ssh_config_match_keyword_table_s {
@@ -168,31 +178,62 @@ struct ssh_config_match_keyword_table_s {
enum ssh_config_match_e opcode;
};
-static struct ssh_config_match_keyword_table_s ssh_config_match_keyword_table[] = {
- { "all", MATCH_ALL },
- { "canonical", MATCH_CANONICAL },
- { "final", MATCH_FINAL },
- { "exec", MATCH_EXEC },
- { "host", MATCH_HOST },
- { "originalhost", MATCH_ORIGINALHOST },
- { "user", MATCH_USER },
- { "localuser", MATCH_LOCALUSER },
- { NULL, MATCH_UNKNOWN },
+static struct ssh_config_match_keyword_table_s
+ ssh_config_match_keyword_table[] = {
+ {"all", MATCH_ALL},
+ {"canonical", MATCH_CANONICAL},
+ {"final", MATCH_FINAL},
+ {"exec", MATCH_EXEC},
+ {"host", MATCH_HOST},
+ {"originalhost", MATCH_ORIGINALHOST},
+ {"user", MATCH_USER},
+ {"localuser", MATCH_LOCALUSER},
+ {"localnetwork", MATCH_LOCALNETWORK},
+ {NULL, MATCH_UNKNOWN},
};
-static int ssh_config_parse_line(ssh_session session, const char *line,
- unsigned int count, int *parsing, unsigned int depth, bool global);
+int ssh_config_parse_line(ssh_session session,
+ const char *line,
+ unsigned int count,
+ int *parsing,
+ unsigned int depth,
+ bool global);
+
+static int ssh_config_parse_line_internal(ssh_session session,
+ const char *line,
+ unsigned int count,
+ int *parsing,
+ unsigned int depth,
+ bool global,
+ bool is_cli,
+ bool fail_on_unknown);
+
+int ssh_config_parse_line_cli(ssh_session session, const char *line);
+
+enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword)
+{
+ int i;
+
+ for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
+ if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
+ return ssh_config_keyword_table[i].opcode;
+ }
+ }
+
+ return SOC_UNKNOWN;
+}
-static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
- int i;
+static bool ssh_config_is_cli_supported(enum ssh_config_opcode_e opcode)
+{
+ int i;
- for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
- if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
- return ssh_config_keyword_table[i].opcode;
+ for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
+ if (opcode == ssh_config_keyword_table[i].opcode) {
+ return ssh_config_keyword_table[i].cli_supported;
+ }
}
- }
- return SOC_UNKNOWN;
+ return false;
}
#define LIBSSH_CONF_MAX_DEPTH 16
@@ -203,7 +244,7 @@ local_parse_file(ssh_session session,
unsigned int depth,
bool global)
{
- FILE *f;
+ FILE *f = NULL;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
@@ -215,10 +256,9 @@ local_parse_file(ssh_session session,
return;
}
- f = fopen(filename, "r");
+ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) {
- SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
- filename);
+ /* The underlying function logs the reasons */
return;
}
@@ -243,9 +283,14 @@ static void local_parse_glob(ssh_session session,
unsigned int depth,
bool global)
{
+#ifdef __FILC__
+ /* FilC's libc provides glob_t without the non-standard gl_flags member. */
+ glob_t globbuf = {0};
+#else
glob_t globbuf = {
.gl_flags = 0,
};
+#endif
int rt;
size_t i;
@@ -299,20 +344,8 @@ ssh_config_match(char *value, const char *pattern, bool negate)
return result;
}
-#ifdef _WIN32
-static int
-ssh_match_exec(ssh_session session, const char *command, bool negate)
-{
- (void) session;
- (void) command;
- (void) negate;
-
- SSH_LOG(SSH_LOG_TRACE, "Unsupported 'exec' command on Windows '%s'",
- command);
- return 0;
-}
-#else /* _WIN32 */
-
+#ifdef WITH_EXEC
+/* FIXME reuse the ssh_execute_command() from socket.c */
static int
ssh_exec_shell(char *cmd)
{
@@ -425,12 +458,33 @@ ssh_match_exec(ssh_session session, const char *command, bool negate)
free(cmd);
return result;
}
-#endif /* _WIN32 */
+#else
+static int
+ssh_match_exec(ssh_session session, const char *command, bool negate)
+{
+ (void)session;
+ (void)command;
+ (void)negate;
-/* @brief: Parse the ProxyJump configuration line and if parsing,
+ SSH_LOG(SSH_LOG_TRACE,
+ "Unsupported 'exec' command on Windows '%s'",
+ command);
+ return 0;
+}
+#endif /* WITH_EXEC */
+
+/**
+ * @brief: Parse the ProxyJump configuration line and if parsing,
* stores the result in the configuration option
+ *
+ * @param[in] session The ssh session
+ * @param[in] s The string to be parsed.
+ * @param[in] do_parsing Whether to parse or not.
+ *
+ * @returns SSH_OK if the provided string is formatted and parsed correctly
+ * SSH_ERROR on failure
*/
-static int
+int
ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
{
char *c = NULL, *cp = NULL, *endp = NULL;
@@ -439,12 +493,20 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
char *port = NULL;
char *next = NULL;
int cmp, rv = SSH_ERROR;
+ struct ssh_jump_info_struct *jump_host = NULL;
bool parse_entry = do_parsing;
+ bool libssh_proxy_jump = ssh_libssh_proxy_jumps();
+ if (do_parsing) {
+ SAFE_FREE(session->opts.proxy_jumps_str);
+ ssh_proxyjumps_free(session->opts.proxy_jumps);
+ }
/* Special value none disables the proxy */
cmp = strcasecmp(s, "none");
- if (cmp == 0 && do_parsing) {
- ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, s);
+ if (cmp == 0) {
+ if (!libssh_proxy_jump && do_parsing) {
+ ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, s);
+ }
return SSH_OK;
}
@@ -455,6 +517,17 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
return SSH_ERROR;
}
+ if (do_parsing) {
+ /* Store the whole string in session */
+ SAFE_FREE(session->opts.proxy_jumps_str);
+ session->opts.proxy_jumps_str = strdup(s);
+ if (session->opts.proxy_jumps_str == NULL) {
+ free(c);
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ }
+
cp = c;
do {
endp = strchr(cp, ',');
@@ -462,25 +535,65 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
/* Split out the token */
*endp = '\0';
}
- if (parse_entry) {
+ if (parse_entry && libssh_proxy_jump) {
+ jump_host = calloc(1, sizeof(struct ssh_jump_info_struct));
+ if (jump_host == NULL) {
+ ssh_set_error_oom(session);
+ rv = SSH_ERROR;
+ goto out;
+ }
+
+ rv = ssh_config_parse_uri(cp,
+ &jump_host->username,
+ &jump_host->hostname,
+ &port,
+ false);
+ if (rv != SSH_OK) {
+ ssh_set_error_invalid(session);
+ SAFE_FREE(jump_host);
+ goto out;
+ }
+ if (port == NULL) {
+ jump_host->port = 22;
+ } else {
+ jump_host->port = strtol(port, NULL, 10);
+ SAFE_FREE(port);
+ }
+
+ /* Prepend because we will recursively proxy jump */
+ rv = ssh_list_prepend(session->opts.proxy_jumps, jump_host);
+ if (rv != SSH_OK) {
+ ssh_set_error_oom(session);
+ SAFE_FREE(jump_host);
+ goto out;
+ }
+ } else if (parse_entry) {
/* We actually care only about the first item */
- rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
+ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
+ if (rv != SSH_OK) {
+ ssh_set_error_invalid(session);
+ goto out;
+ }
/* The rest of the list needs to be passed on */
if (endp != NULL) {
next = strdup(endp + 1);
if (next == NULL) {
ssh_set_error_oom(session);
rv = SSH_ERROR;
+ goto out;
}
}
} else {
/* The rest is just sanity-checked to avoid failures later */
- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
+ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
+ if (rv != SSH_OK) {
+ ssh_set_error_invalid(session);
+ goto out;
+ }
}
- if (rv != SSH_OK) {
- goto out;
+ if (!libssh_proxy_jump) {
+ parse_entry = 0;
}
- parse_entry = 0;
if (endp != NULL) {
cp = endp + 1;
} else {
@@ -488,7 +601,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} while (cp != NULL);
- if (hostname != NULL && do_parsing) {
+ if (!libssh_proxy_jump && hostname != NULL && do_parsing) {
char com[512] = {0};
rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
@@ -504,11 +617,19 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
rv = SSH_ERROR;
goto out;
}
- ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, com);
+ rv = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, com);
+ if (rv != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto out;
+ }
}
+
rv = SSH_OK;
out:
+ if (rv != SSH_OK) {
+ ssh_proxyjumps_free(session->opts.proxy_jumps);
+ }
SAFE_FREE(username);
SAFE_FREE(hostname);
SAFE_FREE(port);
@@ -572,13 +693,169 @@ ssh_config_make_absolute(ssh_session session,
return out;
}
+#ifdef HAVE_IFADDRS_H
+/**
+ * @brief Checks if host address matches the local network specified.
+ *
+ * Verify whether a local network interface address matches any of the CIDR
+ * patterns.
+ *
+ * @param addrlist The CIDR pattern-list to be checked, can contain both
+ * IPv4 and IPv6 addresses and has to be comma separated
+ * (',' only, space after comma not allowed).
+ *
+ * @param negate The negate condition. The return value is negated
+ * (returns 1 instead of 0 and vice versa).
+ *
+ * @return 1 if match found.
+ * @return 0 if no match found.
+ * @return -1 on errors.
+ */
static int
-ssh_config_parse_line(ssh_session session,
- const char *line,
- unsigned int count,
- int *parsing,
- unsigned int depth,
- bool global)
+ssh_match_localnetwork(const char *addrlist, bool negate)
+{
+ struct ifaddrs *ifa = NULL, *ifaddrs = NULL;
+ int r, found = 0;
+ char address[NI_MAXHOST], err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ socklen_t sa_len;
+
+ r = getifaddrs(&ifaddrs);
+ if (r != 0) {
+ SSH_LOG(SSH_LOG_WARN,
+ "Match localnetwork: getifaddrs() failed: %s",
+ ssh_strerror(r, err_msg, SSH_ERRNO_MSG_MAX));
+ return -1;
+ }
+
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || (ifa->ifa_flags & IFF_UP) == 0) {
+ continue;
+ }
+
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ sa_len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ sa_len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ SSH_LOG(SSH_LOG_TRACE,
+ "Interface %s: unsupported address family %d",
+ ifa->ifa_name,
+ ifa->ifa_addr->sa_family);
+ continue;
+ }
+
+ r = getnameinfo(ifa->ifa_addr,
+ sa_len,
+ address,
+ sizeof(address),
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ if (r != 0) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Interface %s getnameinfo failed: %s",
+ ifa->ifa_name,
+ gai_strerror(r));
+ continue;
+ }
+ SSH_LOG(SSH_LOG_TRACE,
+ "Interface %s address %s",
+ ifa->ifa_name,
+ address);
+
+ r = match_cidr_address_list(address,
+ addrlist,
+ ifa->ifa_addr->sa_family);
+ if (r == 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Matched interface %s: address %s in %s",
+ ifa->ifa_name,
+ address,
+ addrlist);
+ found = 1;
+ break;
+ }
+ }
+
+ freeifaddrs(ifaddrs);
+
+ return (found == (negate ? 0 : 1));
+}
+#endif /* HAVE_IFADDRS_H */
+
+static enum ssh_options_e
+ssh_config_get_auth_option(enum ssh_config_opcode_e opcode)
+{
+ struct auth_option_map {
+ enum ssh_config_opcode_e opcode;
+ const char *name;
+ enum ssh_options_e option;
+ };
+
+ static struct auth_option_map auth_options[] = {
+ {
+ SOC_GSSAPIAUTHENTICATION,
+ "GSSAPIAuthentication",
+ SSH_OPTIONS_GSSAPI_AUTH,
+ },
+ {
+ SOC_KBDINTERACTIVEAUTHENTICATION,
+ "KbdInteractiveAuthentication",
+ SSH_OPTIONS_KBDINT_AUTH,
+ },
+ {
+ SOC_PASSWORDAUTHENTICATION,
+ "PasswordAuthentication",
+ SSH_OPTIONS_PASSWORD_AUTH,
+ },
+ {
+ SOC_PUBKEYAUTHENTICATION,
+ "PubkeyAuthentication",
+ SSH_OPTIONS_PUBKEY_AUTH,
+ },
+ {0, NULL, 0},
+ };
+
+ for (struct auth_option_map *map = auth_options; map->name != NULL; map++) {
+ if (map->opcode == opcode) {
+ return map->option;
+ }
+ }
+ return -1;
+}
+
+#define CHECK_COND_OR_FAIL(cond, error_message) \
+ if ((cond)) { \
+ SSH_LOG(SSH_LOG_DEBUG, \
+ "line %d: %s: %s", \
+ count, \
+ error_message, \
+ keyword); \
+ if (fail_on_unknown) { \
+ ssh_set_error(session, \
+ SSH_FATAL, \
+ is_cli ? "%s '%s' value on CLI" \
+ : "%s '%s' value at line %d", \
+ error_message, \
+ keyword, \
+ is_cli ? 0 : count); \
+ SAFE_FREE(x); \
+ return SSH_ERROR; \
+ } \
+ break; \
+ }
+
+static int ssh_config_parse_line_internal(ssh_session session,
+ const char *line,
+ unsigned int count,
+ int *parsing,
+ unsigned int depth,
+ bool global,
+ bool is_cli,
+ bool fail_on_unknown)
{
enum ssh_config_opcode_e opcode;
const char *p = NULL, *p2 = NULL;
@@ -593,6 +870,9 @@ ssh_config_parse_line(ssh_session session,
/* Ignore empty lines */
if (line == NULL || *line == '\0') {
+ if (is_cli) {
+ return SSH_ERROR;
+ }
return 0;
}
@@ -618,11 +898,22 @@ ssh_config_parse_line(ssh_session session,
}
opcode = ssh_config_get_opcode(keyword);
+ if (is_cli && !ssh_config_is_cli_supported(opcode)) {
+ ssh_set_error(
+ session,
+ SSH_FATAL,
+ "Option '%s' is not supported in command-line configuration",
+ keyword);
+ SAFE_FREE(x);
+ return SSH_ERROR;
+ }
+
if (*parsing == 1 &&
opcode != SOC_HOST &&
opcode != SOC_MATCH &&
opcode != SOC_INCLUDE &&
opcode != SOC_IDENTITY &&
+ opcode != SOC_CERTIFICATE &&
opcode > SOC_UNSUPPORTED &&
opcode < SOC_MAX) { /* Ignore all unknown types here */
/* Skip all the options that were already applied */
@@ -731,9 +1022,11 @@ ssh_config_parse_line(ssh_session session,
/* Here we match only one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
- ssh_set_error(session, SSH_FATAL,
- "line %d: ERROR - Match user keyword "
- "requires argument", count);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "line %d: ERROR - Match localuser keyword "
+ "requires argument",
+ count);
SAFE_FREE(x);
return -1;
}
@@ -794,19 +1087,68 @@ ssh_config_parse_line(ssh_session session,
args++;
break;
- case MATCH_UNKNOWN:
- default:
- ssh_set_error(session, SSH_FATAL,
- "ERROR - Unknown argument '%s' for Match keyword", p);
+ case MATCH_LOCALNETWORK:
+ /* Here we match only one argument */
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p == NULL || p[0] == '\0') {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "line %d: ERROR - Match local network keyword"
+ "requires argument",
+ count);
+ SAFE_FREE(x);
+ return -1;
+ }
+#ifdef HAVE_IFADDRS_H
+ rv = match_cidr_address_list(NULL, p, -1);
+ if (rv == -1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "line %d: ERROR - List invalid entry: %s",
+ count,
+ p);
+ SAFE_FREE(x);
+ return -1;
+ }
+ rv = ssh_match_localnetwork(p, negate);
+ if (rv == -1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "line %d: ERROR - Error while retrieving "
+ "network interface information -"
+ " List entry: %s",
+ count,
+ p);
+ SAFE_FREE(x);
+ return -1;
+ }
+
+ result &= rv;
+#else /* HAVE_IFADDRS_H */
+ ssh_set_error(session,
+ SSH_FATAL,
+ "line %d: ERROR - match localnetwork "
+ "not supported on this platform",
+ count);
SAFE_FREE(x);
return -1;
+#endif /* HAVE_IFADDRS_H */
+ args++;
+ break;
+
+ case MATCH_UNKNOWN:
+ default:
+ SSH_LOG(SSH_LOG_WARN,
+ "Unknown argument '%s' for Match keyword. Not matching",
+ p);
+ result = 0;
+ break;
}
} while (p != NULL && p[0] != '\0');
if (args == 0) {
- ssh_set_error(session, SSH_FATAL,
- "ERROR - Match keyword requires an argument");
- SAFE_FREE(x);
- return -1;
+ SSH_LOG(SSH_LOG_WARN,
+ "ERROR - Match keyword requires an argument. Not matching");
+ result = 0;
}
*parsing = result;
break;
@@ -836,82 +1178,91 @@ ssh_config_parse_line(ssh_session session,
}
case SOC_HOSTNAME:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- char *z = ssh_path_expand_escape(session, p);
- if (z == NULL) {
- z = strdup(p);
- }
- ssh_options_set(session, SSH_OPTIONS_HOST, z);
- free(z);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ char *z = ssh_path_expand_escape(session, p);
+ if (z == NULL) {
+ z = strdup(p);
+ }
+ ssh_options_set(session, SSH_OPTIONS_HOST, z);
+ free(z);
}
break;
case SOC_PORT:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
}
break;
case SOC_USERNAME:
- if (session->opts.username == NULL) {
- p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ p = ssh_config_get_str_tok(&s, NULL);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p);
- }
- }
- break;
+ }
+ break;
case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
}
break;
case SOC_CIPHERS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
- ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
+ ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
}
break;
case SOC_MACS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
- ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
+ ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
}
break;
case SOC_COMPRESSION:
i = ssh_config_get_yesno(&s, -1);
- if (i >= 0 && *parsing) {
- if (i) {
- ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
- } else {
- ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
- }
+ CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
+ if (*parsing) {
+ if (i) {
+ ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
+ } else {
+ ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
+ }
}
break;
case SOC_TIMEOUT:
l = ssh_config_get_long(&s, -1);
- if (l >= 0 && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
+ CHECK_COND_OR_FAIL(l < 0, "Invalid argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
}
break;
case SOC_STRICTHOSTKEYCHECK:
i = ssh_config_get_yesno(&s, -1);
- if (i >= 0 && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
+ CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
}
break;
case SOC_KNOWNHOSTS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
}
break;
case SOC_PROXYCOMMAND:
p = ssh_config_get_cmd(&s);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
/* We share the seen value with the ProxyJump */
- if (p && *parsing && !seen[SOC_PROXYJUMP]) {
- ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
+ if (*parsing && !seen[SOC_PROXYJUMP]) {
+ ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
}
break;
case SOC_PROXYJUMP:
@@ -921,7 +1272,8 @@ ssh_config_parse_line(ssh_session session,
return -1;
}
/* We share the seen value with the ProxyCommand */
- rv = ssh_config_parse_proxy_jump(session, p,
+ rv = ssh_config_parse_proxy_jump(session,
+ p,
(*parsing && !seen[SOC_PROXYCOMMAND]));
if (rv != SSH_OK) {
SAFE_FREE(x);
@@ -930,37 +1282,43 @@ ssh_config_parse_line(ssh_session session,
break;
case SOC_GSSAPISERVERIDENTITY:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
}
break;
case SOC_GSSAPICLIENTIDENTITY:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
}
break;
case SOC_GSSAPIDELEGATECREDENTIALS:
i = ssh_config_get_yesno(&s, -1);
- if (i >=0 && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
+ CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
}
break;
case SOC_BINDADDRESS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_BINDADDR, p);
}
break;
case SOC_GLOBALKNOWNHOSTSFILE:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p);
}
break;
case SOC_LOGLEVEL:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
int value = -1;
if (strcasecmp(p, "quiet") == 0) {
@@ -978,6 +1336,7 @@ ssh_config_parse_line(ssh_session session,
strcasecmp(p, "DEBUG3") == 0) {
value = SSH_LOG_TRACE;
}
+ CHECK_COND_OR_FAIL(value == -1, "Invalid value");
if (value != -1) {
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &value);
}
@@ -985,19 +1344,22 @@ ssh_config_parse_line(ssh_session session,
break;
case SOC_HOSTKEYALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
}
break;
case SOC_PUBKEYACCEPTEDKEYTYPES:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
}
break;
case SOC_KEXALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p);
}
break;
@@ -1005,6 +1367,7 @@ ssh_config_parse_line(ssh_session session,
/* Parse the data limit */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL) {
+ CHECK_COND_OR_FAIL(1, "Missing data limit");
break;
} else if (strcmp(p, "default") == 0) {
/* Default rekey limits enforced automatically */
@@ -1013,8 +1376,7 @@ ssh_config_parse_line(ssh_session session,
char *endp = NULL;
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
- /* No number or negative */
- SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
+ CHECK_COND_OR_FAIL(1, "Invalid data limit");
break;
}
switch (*endp) {
@@ -1052,19 +1414,19 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != ' ' && *endp != '\0') {
- SSH_LOG(SSH_LOG_TRACE,
- "Invalid trailing characters after the rekey limit: %s",
- endp);
+ CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
break;
}
}
- if (ll > -1 && *parsing) {
+ CHECK_COND_OR_FAIL(ll < 0, "Invalid data limit");
+ if (*parsing) {
uint64_t v = (uint64_t)ll;
ssh_options_set(session, SSH_OPTIONS_REKEY_DATA, &v);
}
/* Parse the time limit */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL) {
+ CHECK_COND_OR_FAIL(1, "Missing time limit");
break;
} else if (strcmp(p, "none") == 0) {
ll = 0;
@@ -1073,7 +1435,7 @@ ssh_config_parse_line(ssh_session session,
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
/* No number or negative */
- SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
+ CHECK_COND_OR_FAIL(1, "Invalid time limit");
break;
}
switch (*endp) {
@@ -1126,11 +1488,11 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != '\0') {
- SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
- " rekey limit: %s", endp);
+ CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
break;
}
}
+ CHECK_COND_OR_FAIL(ll < 0, "Invalid time limit");
if (ll > -1 && *parsing) {
uint32_t v = (uint32_t)ll;
ssh_options_set(session, SSH_OPTIONS_REKEY_TIME, &v);
@@ -1139,56 +1501,44 @@ ssh_config_parse_line(ssh_session session,
case SOC_GSSAPIAUTHENTICATION:
case SOC_KBDINTERACTIVEAUTHENTICATION:
case SOC_PASSWORDAUTHENTICATION:
- case SOC_PUBKEYAUTHENTICATION:
+ case SOC_PUBKEYAUTHENTICATION: {
+ enum ssh_options_e option = ssh_config_get_auth_option(opcode);
i = ssh_config_get_yesno(&s, 0);
- if (i>=0 && *parsing) {
- switch(opcode){
- case SOC_GSSAPIAUTHENTICATION:
- ssh_options_set(session, SSH_OPTIONS_GSSAPI_AUTH, &i);
- break;
- case SOC_KBDINTERACTIVEAUTHENTICATION:
- ssh_options_set(session, SSH_OPTIONS_KBDINT_AUTH, &i);
- break;
- case SOC_PASSWORDAUTHENTICATION:
- ssh_options_set(session, SSH_OPTIONS_PASSWORD_AUTH, &i);
- break;
- case SOC_PUBKEYAUTHENTICATION:
- ssh_options_set(session, SSH_OPTIONS_PUBKEY_AUTH, &i);
- break;
- /* make gcc happy */
- default:
- break;
- }
+
+ CHECK_COND_OR_FAIL(i < 0, "Authentication option");
+ if (*parsing) {
+ ssh_options_set(session, option, &i);
}
break;
+ }
case SOC_NA:
- SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
- keyword, count);
- break;
+ CHECK_COND_OR_FAIL(1, "Unapplicable option");
+ break;
case SOC_UNSUPPORTED:
- SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
- keyword, count);
- break;
+ CHECK_COND_OR_FAIL(1, "Unsupported option");
+ break;
case SOC_UNKNOWN:
- SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
- keyword, count);
- break;
+ CHECK_COND_OR_FAIL(1, "Unknown option");
+ break;
case SOC_IDENTITYAGENT:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
}
break;
case SOC_IDENTITIESONLY:
i = ssh_config_get_yesno(&s, -1);
- if (i >= 0 && *parsing) {
- bool b = i;
- ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
+ CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
+ if (*parsing) {
+ bool b = i;
+ ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
}
break;
case SOC_CONTROLMASTER:
p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
+ CHECK_COND_OR_FAIL(p == NULL, "ControlMaster");
+ if (*parsing) {
int value = -1;
if (strcasecmp(p, "auto") == 0) {
@@ -1203,6 +1553,7 @@ ssh_config_parse_line(ssh_session session,
value = SSH_CONTROL_MASTER_ASK;
}
+ CHECK_COND_OR_FAIL(value == -1, "Invalid argument");
if (value != -1) {
ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
}
@@ -1218,6 +1569,66 @@ ssh_config_parse_line(ssh_session session,
ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
}
break;
+ case SOC_CERTIFICATE:
+ p = ssh_config_get_str_tok(&s, NULL);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
+ }
+ break;
+ case SOC_GSSAPIKEYEXCHANGE: {
+ i = ssh_config_get_yesno(&s, -1);
+ CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
+ if (*parsing) {
+ bool b = (i == 1) ? true : false;
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &b);
+ }
+ break;
+ }
+ case SOC_GSSAPIKEXALGORITHMS:
+ p = ssh_config_get_str_tok(&s, NULL);
+ CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, p);
+ }
+ break;
+ case SOC_REQUIRED_RSA_SIZE:
+ l = ssh_config_get_long(&s, -1);
+ CHECK_COND_OR_FAIL(l < 0 || l > INT_MAX, "Invalid argument");
+ if (*parsing) {
+ i = (int)l;
+ ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &i);
+ }
+ break;
+ case SOC_ADDRESSFAMILY:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p == NULL) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "line %d: no argument after keyword \"addressfamily\"",
+ count);
+ SAFE_FREE(x);
+ return SSH_ERROR;
+ }
+ if (*parsing) {
+ int value = -1;
+
+ if (strcasecmp(p, "any") == 0) {
+ value = SSH_ADDRESS_FAMILY_ANY;
+ } else if (strcasecmp(p, "inet") == 0) {
+ value = SSH_ADDRESS_FAMILY_INET;
+ } else if (strcasecmp(p, "inet6") == 0) {
+ value = SSH_ADDRESS_FAMILY_INET6;
+ } else {
+ SSH_LOG(SSH_LOG_WARNING,
+ "line %d: invalid argument \"%s\"",
+ count,
+ p);
+ SAFE_FREE(x);
+ return SSH_ERROR;
+ }
+ ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &value);
+ }
+ break;
default:
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
opcode);
@@ -1230,45 +1641,100 @@ ssh_config_parse_line(ssh_session session,
return 0;
}
-/* @brief Parse configuration file and set the options to the given session
+#undef CHECK_COND_OR_FAIL
+
+int ssh_config_parse_line(ssh_session session,
+ const char *line,
+ unsigned int count,
+ int *parsing,
+ unsigned int depth,
+ bool global)
+{
+ return ssh_config_parse_line_internal(session,
+ line,
+ count,
+ parsing,
+ depth,
+ global,
+ false,
+ false);
+}
+
+int ssh_config_parse_line_cli(ssh_session session, const char *line)
+{
+ int parsing = 1;
+ return ssh_config_parse_line_internal(session,
+ line,
+ 0,
+ &parsing,
+ 0,
+ false,
+ true,
+ true);
+}
+
+/* @brief Parse configuration from a file pointer
*
* @params[in] session The ssh session
- * @params[in] filename The path to the ssh configuration file
+ * @params[in] fp A valid file pointer
+ * @params[in] global Whether the config is global or not
*
* @returns 0 on successful parsing the configuration file, -1 on error
*/
-int ssh_config_parse_file(ssh_session session, const char *filename)
+int ssh_config_parse(ssh_session session, FILE *fp, bool global)
{
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
- FILE *f;
int parsing, rv;
+
+ parsing = 1;
+ while (fgets(line, sizeof(line), fp)) {
+ count++;
+ rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
+ if (rv < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* @brief Parse configuration file and set the options to the given session
+ *
+ * @params[in] session The ssh session
+ * @params[in] filename The path to the ssh configuration file
+ *
+ * @returns 0 on successful parsing the configuration file, -1 on error
+ */
+int ssh_config_parse_file(ssh_session session, const char *filename)
+{
+ FILE *fp = NULL;
+ int rv;
bool global = 0;
- f = fopen(filename, "r");
- if (f == NULL) {
+ fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if (fp == NULL) {
+ /* The underlying function logs the reasons */
return 0;
}
rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
+#ifdef USR_GLOBAL_CLIENT_CONFIG
+ if (rv != 0) {
+ rv = strcmp(filename, USR_GLOBAL_CLIENT_CONFIG);
+ }
+#endif
+
if (rv == 0) {
global = true;
}
SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
- parsing = 1;
- while (fgets(line, sizeof(line), f)) {
- count++;
- rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
- if (rv < 0) {
- fclose(f);
- return -1;
- }
- }
+ rv = ssh_config_parse(session, fp, global);
- fclose(f);
- return 0;
+ fclose(fp);
+ return rv;
}
/* @brief Parse configuration string and set the options to the given session
@@ -1283,7 +1749,8 @@ int ssh_config_parse_string(ssh_session session, const char *input)
{
char line[MAX_LINE_SIZE] = {0};
const char *c = input, *line_start = input;
- unsigned int line_num = 0, line_len;
+ unsigned int line_num = 0;
+ size_t line_len;
int parsing, rv;
SSH_LOG(SSH_LOG_DEBUG, "Reading configuration data from string:");
@@ -1305,8 +1772,10 @@ int ssh_config_parse_string(ssh_session session, const char *input)
}
line_len = c - line_start;
if (line_len > MAX_LINE_SIZE - 1) {
- SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
- line_num, line_len);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Line %u too long: %zu characters",
+ line_num,
+ line_len);
return SSH_ERROR;
}
memcpy(line, line_start, line_len);
diff --git a/src/config_parser.c b/src/config_parser.c
index 10cc614f..06264f84 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -30,6 +30,7 @@
#include "libssh/config_parser.h"
#include "libssh/priv.h"
+#include "libssh/misc.h"
/* Returns the original string after skipping the leading whitespace
* until finding LF.
@@ -38,8 +39,8 @@
*/
char *ssh_config_get_cmd(char **str)
{
- register char *c;
- char *r;
+ register char *c = NULL;
+ char *r = NULL;
/* Ignore leading spaces */
for (c = *str; *c; c++) {
@@ -47,7 +48,7 @@ char *ssh_config_get_cmd(char **str)
break;
}
}
-
+
for (r = c; *c; c++) {
if (*c == '\n') {
*c = '\0';
@@ -66,7 +67,7 @@ char *ssh_config_get_cmd(char **str)
*/
char *ssh_config_get_token(char **str)
{
- register char *c;
+ register char *c = NULL;
bool had_equal = false;
char *r = NULL;
@@ -81,6 +82,13 @@ char *ssh_config_get_token(char **str)
if (*c == '\"') {
for (r = ++c; *c; c++) {
if (*c == '\"' || *c == '\n') {
+ if (*c == '\"' && r != c && *(c - 1) == '\\') {
+ /* Escaped quote: Move the remaining one char left */
+ int remaining_len = strlen(c);
+ memmove(c - 1, c, remaining_len);
+ c[remaining_len - 1] = '\0';
+ continue;
+ }
*c = '\0';
c++;
break;
@@ -115,7 +123,7 @@ char *ssh_config_get_token(char **str)
long ssh_config_get_long(char **str, long notfound)
{
- char *p, *endp;
+ char *p = NULL, *endp = NULL;
long i;
p = ssh_config_get_token(str);
@@ -132,7 +140,7 @@ long ssh_config_get_long(char **str, long notfound)
const char *ssh_config_get_str_tok(char **str, const char *def)
{
- char *p;
+ char *p = NULL;
p = ssh_config_get_token(str);
if (p && *p) {
@@ -144,7 +152,7 @@ const char *ssh_config_get_str_tok(char **str, const char *def)
int ssh_config_get_yesno(char **str, int notfound)
{
- const char *p;
+ const char *p = NULL;
p = ssh_config_get_str_tok(str, NULL);
if (p == NULL) {
@@ -161,12 +169,14 @@ int ssh_config_get_yesno(char **str, int notfound)
}
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port)
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port)
{
char *endp = NULL;
long port_n;
+ int rc;
/* Sanitize inputs */
if (username != NULL) {
@@ -180,7 +190,7 @@ int ssh_config_parse_uri(const char *tok,
}
/* Username part (optional) */
- endp = strchr(tok, '@');
+ endp = strrchr(tok, '@');
if (endp != NULL) {
/* Zero-length username is not valid */
if (tok == endp) {
@@ -191,6 +201,10 @@ int ssh_config_parse_uri(const char *tok,
if (*username == NULL) {
goto error;
}
+ rc = ssh_check_username_syntax(*username);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
tok = endp + 1;
/* If there is second @ character, this does not look like our URI */
@@ -208,12 +222,17 @@ int ssh_config_parse_uri(const char *tok,
if (endp == NULL) {
goto error;
}
- } else {
- /* Hostnames or aliases expand to the last colon or to the end */
+ } else if (!ignore_port) {
+ /* Hostnames or aliases expand to the last colon (if port is requested)
+ * or to the end */
endp = strrchr(tok, ':');
if (endp == NULL) {
endp = strchr(tok, '\0');
}
+ } else {
+ /* If no port is requested, expand to the end of line
+ * (to accommodate the IPv6 addresses) */
+ endp = strchr(tok, '\0');
}
if (tok == endp) {
/* Zero-length hostnames are not valid */
@@ -224,6 +243,14 @@ int ssh_config_parse_uri(const char *tok,
if (*hostname == NULL) {
goto error;
}
+ /* if not an ip, check syntax */
+ rc = ssh_is_ipaddr(*hostname);
+ if (rc == 0) {
+ rc = ssh_check_hostname_syntax(*hostname);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
}
/* Skip also the closing bracket */
if (*endp == ']') {
diff --git a/src/connect.c b/src/connect.c
index fd54c511..0758e468 100644
--- a/src/connect.c
+++ b/src/connect.c
@@ -109,7 +109,8 @@ static int ssh_connect_socket_close(socket_t s)
#endif
}
-static int getai(const char *host, int port, struct addrinfo **ai)
+static int
+getai(const char *host, int port, int ai_family, struct addrinfo **ai)
{
const char *service = NULL;
struct addrinfo hints;
@@ -118,7 +119,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
ZERO_STRUCT(hints);
hints.ai_protocol = IPPROTO_TCP;
- hints.ai_family = PF_UNSPEC;
+ hints.ai_family = ai_family;
hints.ai_socktype = SOCK_STREAM;
if (port == 0) {
@@ -131,7 +132,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
#endif
}
- if (ssh_is_ipaddr(host)) {
+ if (ssh_is_ipaddr(host) == 1) {
/* this is an IP address */
SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host);
hints.ai_flags |= AI_NUMERICHOST;
@@ -165,14 +166,39 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
{
socket_t s = -1, first = -1;
int rc;
+ int ai_family;
+ static const char *ai_family_str = NULL;
struct addrinfo *ai = NULL;
struct addrinfo *itr = NULL;
+ char addrname[NI_MAXHOST], portname[NI_MAXSERV];
- rc = getai(host, port, &ai);
+ switch (session->opts.address_family) {
+ case SSH_ADDRESS_FAMILY_INET:
+ ai_family = PF_INET;
+ ai_family_str = "inet";
+ break;
+ case SSH_ADDRESS_FAMILY_INET6:
+ ai_family = PF_INET6;
+ ai_family_str = "inet6";
+ break;
+ case SSH_ADDRESS_FAMILY_ANY:
+ default:
+ ai_family = PF_UNSPEC;
+ ai_family_str = "any";
+ }
+ SSH_LOG(SSH_LOG_PACKET,
+ "Resolve target hostname %s port %d (%s)",
+ host,
+ port,
+ ai_family_str);
+ rc = getai(host, port, ai_family, &ai);
if (rc != 0) {
- ssh_set_error(session, SSH_FATAL,
- "Failed to resolve hostname %s (%s)",
- host, gai_strerror(rc));
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to resolve hostname %s (%s): %s",
+ host,
+ ai_family_str,
+ gai_strerror(rc));
return -1;
}
@@ -189,16 +215,21 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
}
if (bind_addr) {
- struct addrinfo *bind_ai;
- struct addrinfo *bind_itr;
+ struct addrinfo *bind_ai = NULL;
+ struct addrinfo *bind_itr = NULL;
- SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Resolving bind address %s (%s)",
+ bind_addr,
+ ai_family_str);
- rc = getai(bind_addr, 0, &bind_ai);
+ rc = getai(bind_addr, 0, ai_family, &bind_ai);
if (rc != 0) {
- ssh_set_error(session, SSH_FATAL,
- "Failed to resolve bind address %s (%s)",
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to resolve bind address %s (%s): %s",
bind_addr,
+ ai_family_str,
gai_strerror(rc));
ssh_connect_socket_close(s);
s = -1;
@@ -209,7 +240,8 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
bind_itr != NULL;
bind_itr = bind_itr->ai_next)
{
- if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
+ rc = bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen);
+ if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Binding local address: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
@@ -251,7 +283,28 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
}
}
+ rc = getnameinfo(itr->ai_addr,
+ itr->ai_addrlen,
+ addrname,
+ sizeof(addrname),
+ portname,
+ sizeof(portname),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc != 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "getnameinfo failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ ssh_connect_socket_close(s);
+ s = -1;
+ continue;
+ }
+
errno = 0;
+ SSH_LOG(SSH_LOG_PACKET,
+ "Connecting to host %s [%s] port %s",
+ host,
+ addrname,
+ portname);
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
if (rc == -1) {
if ((errno != 0) && (errno != EINPROGRESS)) {
@@ -262,6 +315,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
s = -1;
} else {
if (first == -1) {
+ SSH_LOG(SSH_LOG_PACKET, "EINPROGRESS => Store for later.");
first = s;
} else { /* errno == EINPROGRESS */
/* save only the first "working" socket */
@@ -281,6 +335,9 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
* connection, otherwise return the first address without error or error */
if (s == -1) {
s = first;
+ } else if (s != first && first != -1) {
+ /* Clean up the saved socket if any */
+ ssh_connect_socket_close(first);
}
return s;
diff --git a/src/connector.c b/src/connector.c
index 21c4d79b..2cdfea47 100644
--- a/src/connector.c
+++ b/src/connector.c
@@ -83,6 +83,20 @@ static ssize_t ssh_connector_fd_write(ssh_connector connector,
uint32_t len);
static bool ssh_connector_fd_is_socket(socket_t socket);
+/**
+ * @brief Create a new SSH connector.
+ *
+ * Allocates and initializes a new connector object for moving data between
+ * an SSH session and file descriptors. The connector is created with invalid
+ * file descriptors and callback structures initialized, but not yet attached
+ * to any channels or sockets.
+ *
+ * @param[in] session The SSH session to associate with the connector.
+ *
+ * @return A newly allocated connector on success, or NULL if an
+ * error occurred. On error, an out-of-memory error is
+ * set on the session.
+ */
ssh_connector ssh_connector_new(ssh_session session)
{
ssh_connector connector;
@@ -112,8 +126,20 @@ ssh_connector ssh_connector_new(ssh_session session)
return connector;
}
+/**
+ * @brief Free an SSH connector.
+ *
+ * Cleans up and deallocates a connector created by ssh_connector_new().
+ * Any channel callbacks and poll objects associated with the @p connector
+ * are removed and freed before the connector structure itself is released.
+ *
+ * @param[in] connector The connector to free.
+ */
void ssh_connector_free (ssh_connector connector)
{
+ if (connector == NULL) {
+ return;
+ }
if (connector->in_channel != NULL) {
ssh_remove_channel_callbacks(connector->in_channel,
&connector->in_channel_cb);
@@ -140,6 +166,24 @@ void ssh_connector_free (ssh_connector connector)
free(connector);
}
+/**
+ * @brief Set the input channel for a connector.
+ *
+ * Associates an SSH channel with the @p connector as its input source and
+ * installs the internal channel callbacks used for reading data. Any
+ * configured input file descriptor is disabled and the connector will
+ * receive data from the given channel only.
+ *
+ * If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
+ * in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
+ *
+ * @param[in] connector The connector to configure.
+ * @param[in] channel The SSH channel to use as input.
+ * @param[in] flags A combination of ssh_connector_flags_e values
+ * selecting which channel streams to read from.
+ *
+ * @return `SSH_OK` on success, `SSH_ERROR` on failure.
+ */
int ssh_connector_set_in_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
@@ -156,6 +200,24 @@ int ssh_connector_set_in_channel(ssh_connector connector,
return ssh_add_channel_callbacks(channel, &connector->in_channel_cb);
}
+/**
+ * @brief Set the output channel for a connector.
+ *
+ * Associates an SSH channel with the @p connector as its output target and
+ * installs the internal channel callbacks used for writing data. Any
+ * configured output file descriptor is disabled and the connector will
+ * send data to the given channel only.
+ *
+ * If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
+ * in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
+ *
+ * @param[in] connector The connector to configure.
+ * @param[in] channel The SSH channel to use as output.
+ * @param[in] flags A combination of ssh_connector_flags_e values
+ * selecting which channel streams to write to.
+ *
+ * @return `SSH_OK` on success, `SSH_ERROR` on failure.
+ */
int ssh_connector_set_out_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
@@ -166,12 +228,21 @@ int ssh_connector_set_out_channel(ssh_connector connector,
/* Fallback to default value for invalid flags */
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
- connector->in_flags = SSH_CONNECTOR_STDOUT;
+ connector->out_flags = SSH_CONNECTOR_STDOUT;
}
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
}
+/**
+ * @brief Set the connector's input file descriptor.
+ *
+ * Sets the @p fd (file descriptor) to be used as the input source for the
+ * @p connector , replacing any previously configured input channel.
+ *
+ * @param[in] connector The connector to configure.
+ * @param[in] fd The file descriptor (socket or regular).
+ */
void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
{
connector->in_fd = fd;
@@ -179,6 +250,15 @@ void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
connector->in_channel = NULL;
}
+/**
+ * @brief Set the connector's output file descriptor.
+ *
+ * Sets the @p fd (file descriptor) to be used as the output target for the
+ * @p connector , replacing any previously configured output channel.
+ *
+ * @param[in] connector The connector to configure.
+ * @param[in] fd The file descriptor (socket or regular).
+ */
void ssh_connector_set_out_fd(ssh_connector connector, socket_t fd)
{
connector->out_fd = fd;
@@ -225,6 +305,87 @@ static void ssh_connector_reset_pollevents(ssh_connector connector)
}
}
+/**
+ * @internal
+ *
+ * @brief Update the connector's flags after a read-write io
+ * operation
+ *
+ * This should be called after some data is successfully read from
+ * connector's input and written to connector's output.
+ *
+ * @param[in, out] connector Connector for which the io operation occurred.
+ *
+ * @warning This does not consider the case when the io indicated failure
+ *
+ * @warning This does not consider the case when the input indicated that
+ * EOF was encountered.
+ */
+static void ssh_connector_update_flags_after_io(ssh_connector connector)
+{
+ /*
+ * With fds we can afford to mark:
+ * - in_available as 0 after an fd read (even if more pending data can be
+ * immediately read from the fd)
+ *
+ * - out_wontblock as 0 after an fd write (even if more data can
+ * be written to the fd without blocking)
+ *
+ * since poll events set on the fd will get raised to indicate
+ * possibility of read/write in case existing situation is apt
+ * (i.e can read/write occur right now) or if situation becomes
+ * apt in future (read data becomes available, write becomes
+ * possible)
+ */
+
+ /*
+ * On the other hand, with channels we need to be more careful
+ * before claiming read/write not possible because channel callbacks
+ * are called in limited scenarios.
+ *
+ * (e.g. connector callback to indicate read data available on input
+ * channel is called only when new data is received on channel. It is
+ * not called when we have some pending data in channel's buffers but
+ * don't receive any new data on the channel)
+ *
+ * Hence, in case of channels, blindly setting flag associated with
+ * read/write input/output to 0 after a read/write may not be a good
+ * idea as the callback that sets it back to 1 again may not be ever
+ * called again.
+ */
+
+ uint32_t window_size;
+
+ /* update in_available based on input source (fd or channel) */
+ if (connector->in_fd != SSH_INVALID_SOCKET) {
+ connector->in_available = 0;
+ } else if (connector->in_channel != NULL) {
+ if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0) {
+ connector->in_available = 1;
+ } else {
+ connector->in_available = 0;
+ }
+ } else {
+ /* connector input is invalid ! */
+ return;
+ }
+
+ /* update out_wontblock based on output source (fd or channel) */
+ if (connector->out_fd != SSH_INVALID_SOCKET) {
+ connector->out_wontblock = 0;
+ } else if (connector->out_channel != NULL) {
+ window_size = ssh_channel_window_size(connector->out_channel);
+ if (window_size > 0) {
+ connector->out_wontblock = 1;
+ } else {
+ connector->out_wontblock = 0;
+ }
+ } else {
+ /* connector output is invalid ! */
+ return;
+ }
+}
+
/**
* @internal
*
@@ -250,7 +411,9 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
}
r = ssh_connector_fd_read(connector, buffer, toread);
- if (r < 0) {
+ /* Sanity: Make sure we do not get too large return value to make static
+ * analysis tools happy */
+ if (r < 0 || r > (ssize_t)toread) {
ssh_connector_except(connector, connector->in_fd);
return;
}
@@ -264,17 +427,17 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
}
connector->in_available = 1; /* Don't poll on it */
return;
- } else if (r> 0) {
+ } else if (r > 0) {
/* loop around ssh_channel_write in case our window reduced due to a race */
while (total != r){
if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
w = ssh_channel_write(connector->out_channel,
buffer + total,
- r - total);
+ (uint32_t)(r - total));
} else {
w = ssh_channel_write_stderr(connector->out_channel,
buffer + total,
- r - total);
+ (uint32_t)(r - total));
}
if (w == SSH_ERROR) {
return;
@@ -291,9 +454,13 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
* Loop around write in case the write blocks even for CHUNKSIZE
* bytes
*/
- while (total != r) {
- w = ssh_connector_fd_write(connector, buffer + total, r - total);
- if (w < 0){
+ while (total < r) {
+ w = ssh_connector_fd_write(connector,
+ buffer + total,
+ (uint32_t)(r - total));
+ /* Sanity: Make sure we do not get too large return value
+ * to make static analysis tools happy */
+ if (w < 0 || w > (r - total)) {
ssh_connector_except(connector, connector->out_fd);
return;
}
@@ -304,8 +471,8 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
return;
}
- connector->out_wontblock = 0;
- connector->in_available = 0;
+
+ ssh_connector_update_flags_after_io(connector);
} else {
connector->in_available = 1;
}
@@ -338,8 +505,9 @@ ssh_connector_fd_out_cb(ssh_connector connector)
} else if (r > 0) {
/* loop around write in case the write blocks even for CHUNKSIZE bytes */
while (total != r) {
- w = ssh_connector_fd_write(connector, buffer + total,
- r - total);
+ w = ssh_connector_fd_write(connector,
+ buffer + total,
+ (uint32_t)(r - total));
if (w < 0) {
ssh_connector_except(connector, connector->out_fd);
return;
@@ -357,8 +525,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
"Output socket or channel closed");
return;
}
- connector->in_available = 0;
- connector->out_wontblock = 0;
+
+ ssh_connector_update_flags_after_io(connector);
} else {
connector->out_wontblock = 1;
}
@@ -369,7 +537,7 @@ ssh_connector_fd_out_cb(ssh_connector connector)
*
* @brief Callback called when a poll event is received on a file descriptor.
*
- * This is for (input or output.
+ * This is for input or output.
*
* @param[in] fd file descriptor receiving the event
*
@@ -379,15 +547,13 @@ ssh_connector_fd_out_cb(ssh_connector connector)
*
* @returns 0
*/
-static int ssh_connector_fd_cb(ssh_poll_handle p,
+static int ssh_connector_fd_cb(UNUSED_PARAM(ssh_poll_handle p),
socket_t fd,
int revents,
void *userdata)
{
ssh_connector connector = userdata;
- (void)p;
-
if (revents & POLLERR) {
ssh_connector_except(connector, fd);
} else if((revents & (POLLIN|POLLHUP)) && fd == connector->in_fd) {
@@ -406,6 +572,10 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
*
* @brief Callback called when data is received on channel.
*
+ * @param[in] session The SSH session
+ *
+ * @param[in] channel The channel data came from
+ *
* @param[in] data Pointer to the data
*
* @param[in] len Length of data
@@ -417,7 +587,7 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
* @returns Amount of data bytes consumed
*/
static int ssh_connector_channel_data_cb(ssh_session session,
- ssh_channel channel,
+ UNUSED_PARAM(ssh_channel channel),
void *data,
uint32_t len,
int is_stderr,
@@ -427,11 +597,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
int w;
uint32_t window;
- (void) session;
- (void) channel;
- (void) is_stderr;
-
- SSH_LOG(SSH_LOG_TRACE,"connector data on channel");
+ SSH_LOG(SSH_LOG_TRACE,
+ "Received data (%" PRIu32 ") on channel (%" PRIu32 ":%" PRIu32 ")",
+ len,
+ channel->local_channel,
+ channel->remote_channel);
if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) {
/* ignore stderr */
@@ -445,6 +615,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
}
if (connector->out_wontblock) {
+ SSH_LOG(SSH_LOG_TRACE, "Writing won't block");
if (connector->out_channel != NULL) {
uint32_t window_len;
@@ -452,16 +623,8 @@ static int ssh_connector_channel_data_cb(ssh_session session,
window_len = MIN(window, len);
/* Route the data to the right exception channel */
- if (is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR)) {
- w = ssh_channel_write_stderr(connector->out_channel,
- data,
- window_len);
- } else if (!is_stderr &&
- (connector->out_flags & SSH_CONNECTOR_STDOUT)) {
- w = ssh_channel_write(connector->out_channel,
- data,
- window_len);
- } else if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
+ if (connector->out_flags & SSH_CONNECTOR_STDOUT &&
+ !(is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR))) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
@@ -474,23 +637,22 @@ static int ssh_connector_channel_data_cb(ssh_session session,
ssh_connector_except_channel(connector, connector->out_channel);
}
} else if (connector->out_fd != SSH_INVALID_SOCKET) {
- w = ssh_connector_fd_write(connector, data, len);
- if (w < 0)
+ ssize_t ws = ssh_connector_fd_write(connector, data, len);
+ if (ws < 0) {
ssh_connector_except(connector, connector->out_fd);
+ }
+ w = (int)ws;
} else {
ssh_set_error(session, SSH_FATAL, "output socket or channel closed");
return SSH_ERROR;
}
- connector->out_wontblock = 0;
- connector->in_available = 0;
- if ((unsigned int)w < len) {
- connector->in_available = 1;
- }
+ ssh_connector_update_flags_after_io(connector);
ssh_connector_reset_pollevents(connector);
return w;
} else {
+ SSH_LOG(SSH_LOG_TRACE, "Writing would block: wait?");
connector->in_available = 1;
return 0;
@@ -508,10 +670,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
*
* @returns Amount of data bytes consumed
*/
-static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
- ssh_channel channel,
- uint32_t bytes,
- void *userdata)
+static int
+ssh_connector_channel_write_wontblock_cb(ssh_session session,
+ UNUSED_PARAM(ssh_channel channel),
+ uint32_t bytes,
+ void *userdata)
{
ssh_connector connector = userdata;
uint8_t buffer[CHUNKSIZE];
@@ -519,7 +682,12 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
(void) channel;
- SSH_LOG(SSH_LOG_TRACE, "Channel write won't block");
+ SSH_LOG(SSH_LOG_TRACE,
+ "Write won't block (%" PRIu32 ") on channel (%" PRIu32 ":%" PRIu32 ")",
+ bytes,
+ channel->local_channel,
+ channel->remote_channel);
+
if (connector->in_available) {
if (connector->in_channel != NULL) {
uint32_t len = MIN(CHUNKSIZE, bytes);
@@ -530,7 +698,7 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
0);
if (r == SSH_ERROR) {
ssh_connector_except_channel(connector, connector->in_channel);
- } else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
+ } else if (r == 0 && ssh_channel_is_eof(connector->in_channel)) {
ssh_channel_send_eof(connector->out_channel);
} else if (r > 0) {
w = ssh_channel_write(connector->out_channel, buffer, r);
@@ -551,8 +719,8 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
return 0;
}
- connector->in_available = 0;
- connector->out_wontblock = 0;
+
+ ssh_connector_update_flags_after_io(connector);
} else {
connector->out_wontblock = 1;
}
@@ -601,15 +769,15 @@ int ssh_connector_set_event(ssh_connector connector, ssh_event event)
}
}
if (connector->in_channel != NULL) {
- rc = ssh_event_add_session(event,
- ssh_channel_get_session(connector->in_channel));
+ ssh_session session = ssh_channel_get_session(connector->in_channel);
+ rc = ssh_event_add_session(event, session);
if (rc != SSH_OK)
goto error;
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0){
connector->in_available = 1;
}
}
- if(connector->out_channel != NULL) {
+ if (connector->out_channel != NULL) {
ssh_session session = ssh_channel_get_session(connector->out_channel);
rc = ssh_event_add_session(event, session);
@@ -625,8 +793,9 @@ int ssh_connector_set_event(ssh_connector connector, ssh_event event)
return rc;
}
-int ssh_connector_remove_event(ssh_connector connector) {
- ssh_session session;
+int ssh_connector_remove_event(ssh_connector connector)
+{
+ ssh_session session = NULL;
if (connector->in_poll != NULL) {
ssh_event_remove_poll(connector->event, connector->in_poll);
diff --git a/src/curve25519.c b/src/curve25519.c
index 26603681..c68fbc79 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -26,119 +26,43 @@
#include "libssh/curve25519.h"
#ifdef HAVE_CURVE25519
-#ifdef WITH_NACL
-#include "nacl/crypto_scalarmult_curve25519.h"
-#endif
-
-#include "libssh/ssh2.h"
+#include "libssh/bignum.h"
#include "libssh/buffer.h"
-#include "libssh/priv.h"
-#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/dh.h"
#include "libssh/pki.h"
-#include "libssh/bignum.h"
-
-#ifdef HAVE_LIBCRYPTO
-#include
-#endif
+#include "libssh/priv.h"
+#include "libssh/session.h"
+#include "libssh/ssh2.h"
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply);
static ssh_packet_callback dh_client_callbacks[] = {
- ssh_packet_client_curve25519_reply
+ ssh_packet_client_curve25519_reply,
};
static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = {
.start = SSH2_MSG_KEX_ECDH_REPLY,
.n_callbacks = 1,
.callbacks = dh_client_callbacks,
- .user = NULL
+ .user = NULL,
};
-static int ssh_curve25519_init(ssh_session session)
+int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
int rc;
-#ifdef HAVE_LIBCRYPTO
- EVP_PKEY_CTX *pctx = NULL;
- EVP_PKEY *pkey = NULL;
- size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
- size_t pkey_len = CURVE25519_PRIVKEY_SIZE;
-
- pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
- if (pctx == NULL) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to initialize X25519 context: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return SSH_ERROR;
- }
- rc = EVP_PKEY_keygen_init(pctx);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to initialize X25519 keygen: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_CTX_free(pctx);
- return SSH_ERROR;
- }
-
- rc = EVP_PKEY_keygen(pctx, &pkey);
- EVP_PKEY_CTX_free(pctx);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to generate X25519 keys: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return SSH_ERROR;
- }
-
- if (session->server) {
- rc = EVP_PKEY_get_raw_public_key(pkey,
- session->next_crypto->curve25519_server_pubkey,
- &pubkey_len);
- } else {
- rc = EVP_PKEY_get_raw_public_key(pkey,
- session->next_crypto->curve25519_client_pubkey,
- &pubkey_len);
- }
-
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to get X25519 raw public key: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_free(pkey);
- return SSH_ERROR;
- }
-
- rc = EVP_PKEY_get_raw_private_key(pkey,
- session->next_crypto->curve25519_privkey,
- &pkey_len);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to get X25519 raw private key: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_free(pkey);
- return SSH_ERROR;
- }
-
- EVP_PKEY_free(pkey);
-#else
- rc = ssh_get_random(session->next_crypto->curve25519_privkey,
- CURVE25519_PRIVKEY_SIZE, 1);
- if (rc != 1) {
- ssh_set_error(session, SSH_FATAL, "PRNG error");
- return SSH_ERROR;
- }
-
- if (session->server) {
- crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey,
- session->next_crypto->curve25519_privkey);
- } else {
- crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey,
- session->next_crypto->curve25519_privkey);
- }
-#endif /* HAVE_LIBCRYPTO */
+#ifdef DEBUG_CRYPTO
+ ssh_log_hexdump("Session server cookie",
+ session->next_crypto->server_kex.cookie,
+ 16);
+ ssh_log_hexdump("Session client cookie",
+ session->next_crypto->client_kex.cookie,
+ 16);
+#endif
- return SSH_OK;
+ rc = curve25519_do_create_k(session, k);
+ return rc;
}
/** @internal
@@ -177,202 +101,123 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session)
ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
}
-static int ssh_curve25519_build_k(ssh_session session)
+int ssh_curve25519_build_k(ssh_session session)
{
ssh_curve25519_pubkey k;
+ int rc;
-#ifdef HAVE_LIBCRYPTO
- EVP_PKEY_CTX *pctx = NULL;
- EVP_PKEY *pkey = NULL, *pubkey = NULL;
- size_t shared_key_len = sizeof(k);
- int rc, ret = SSH_ERROR;
-
- pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL,
- session->next_crypto->curve25519_privkey,
- CURVE25519_PRIVKEY_SIZE);
- if (pkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to create X25519 EVP_PKEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return SSH_ERROR;
- }
-
- pctx = EVP_PKEY_CTX_new(pkey, NULL);
- if (pctx == NULL) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to initialize X25519 context: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-
- rc = EVP_PKEY_derive_init(pctx);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to initialize X25519 key derivation: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-
- if (session->server) {
- pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL,
- session->next_crypto->curve25519_client_pubkey,
- CURVE25519_PUBKEY_SIZE);
- } else {
- pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL,
- session->next_crypto->curve25519_server_pubkey,
- CURVE25519_PUBKEY_SIZE);
- }
- if (pubkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to create X25519 public key EVP_PKEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-
- rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to set peer X25519 public key: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-
- rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "Failed to derive X25519 shared secret: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
- ret = SSH_OK;
-out:
- EVP_PKEY_free(pkey);
- EVP_PKEY_free(pubkey);
- EVP_PKEY_CTX_free(pctx);
- if (ret == SSH_ERROR) {
- return ret;
- }
-#else
- if (session->server) {
- crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
- session->next_crypto->curve25519_client_pubkey);
- } else {
- crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
- session->next_crypto->curve25519_server_pubkey);
+ rc = ssh_curve25519_create_k(session, k);
+ if (rc != SSH_OK) {
+ return rc;
}
-#endif /* HAVE_LIBCRYPTO */
- bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret);
+ bignum_bin2bn(k,
+ CURVE25519_PUBKEY_SIZE,
+ &session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) {
return SSH_ERROR;
}
#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("Session server cookie",
- session->next_crypto->server_kex.cookie, 16);
- ssh_log_hexdump("Session client cookie",
- session->next_crypto->client_kex.cookie, 16);
ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret);
#endif
- return 0;
+ return SSH_OK;
}
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
*/
-static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
- ssh_string q_s_string = NULL;
- ssh_string pubkey_blob = NULL;
- ssh_string signature = NULL;
- int rc;
- (void)type;
- (void)user;
-
- ssh_client_curve25519_remove_callbacks(session);
-
- pubkey_blob = ssh_buffer_get_ssh_string(packet);
- if (pubkey_blob == NULL) {
- ssh_set_error(session,SSH_FATAL, "No public key in packet");
- goto error;
- }
-
- rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
- SSH_STRING_FREE(pubkey_blob);
- if (rc != 0) {
- ssh_set_error(session,
- SSH_FATAL,
- "Failed to import next public key");
- goto error;
- }
-
- q_s_string = ssh_buffer_get_ssh_string(packet);
- if (q_s_string == NULL) {
- ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet");
- goto error;
- }
- if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){
- ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d",
- (int)ssh_string_len(q_s_string));
- SSH_STRING_FREE(q_s_string);
- goto error;
- }
- memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE);
- SSH_STRING_FREE(q_s_string);
-
- signature = ssh_buffer_get_ssh_string(packet);
- if (signature == NULL) {
- ssh_set_error(session, SSH_FATAL, "No signature in packet");
- goto error;
- }
- session->next_crypto->dh_server_signature = signature;
- signature=NULL; /* ownership changed */
- /* TODO: verify signature now instead of waiting for NEWKEYS */
- if (ssh_curve25519_build_k(session) < 0) {
- ssh_set_error(session, SSH_FATAL, "Cannot build k number");
- goto error;
- }
-
- /* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
- if (rc == SSH_ERROR) {
- goto error;
- }
-
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
-
- return SSH_PACKET_USED;
+static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply)
+{
+ ssh_string q_s_string = NULL;
+ ssh_string pubkey_blob = NULL;
+ ssh_string signature = NULL;
+ int rc;
+ (void)type;
+ (void)user;
+
+ ssh_client_curve25519_remove_callbacks(session);
+
+ pubkey_blob = ssh_buffer_get_ssh_string(packet);
+ if (pubkey_blob == NULL) {
+ ssh_set_error(session, SSH_FATAL, "No public key in packet");
+ goto error;
+ }
+
+ rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
+ SSH_STRING_FREE(pubkey_blob);
+ if (rc != 0) {
+ ssh_set_error(session, SSH_FATAL, "Failed to import next public key");
+ goto error;
+ }
+
+ q_s_string = ssh_buffer_get_ssh_string(packet);
+ if (q_s_string == NULL) {
+ ssh_set_error(session, SSH_FATAL, "No Q_S ECC point in packet");
+ goto error;
+ }
+ if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Incorrect size for server Curve25519 public key: %zu",
+ ssh_string_len(q_s_string));
+ SSH_STRING_FREE(q_s_string);
+ goto error;
+ }
+ memcpy(session->next_crypto->curve25519_server_pubkey,
+ ssh_string_data(q_s_string),
+ CURVE25519_PUBKEY_SIZE);
+ SSH_STRING_FREE(q_s_string);
+
+ signature = ssh_buffer_get_ssh_string(packet);
+ if (signature == NULL) {
+ ssh_set_error(session, SSH_FATAL, "No signature in packet");
+ goto error;
+ }
+ session->next_crypto->dh_server_signature = signature;
+ signature = NULL; /* ownership changed */
+ /* TODO: verify signature now instead of waiting for NEWKEYS */
+ if (ssh_curve25519_build_k(session) < 0) {
+ ssh_set_error(session, SSH_FATAL, "Cannot build k number");
+ goto error;
+ }
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+
+ return SSH_PACKET_USED;
error:
- session->session_state=SSH_SESSION_STATE_ERROR;
- return SSH_PACKET_USED;
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ return SSH_PACKET_USED;
}
#ifdef WITH_SERVER
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init);
-static ssh_packet_callback dh_server_callbacks[]= {
- ssh_packet_server_curve25519_init
+static ssh_packet_callback dh_server_callbacks[] = {
+ ssh_packet_server_curve25519_init,
};
static struct ssh_packet_callbacks_struct ssh_curve25519_server_callbacks = {
.start = SSH2_MSG_KEX_ECDH_INIT,
.n_callbacks = 1,
.callbacks = dh_server_callbacks,
- .user = NULL
+ .user = NULL,
};
/** @internal
* @brief sets up the curve25519-sha256@libssh.org kex callbacks
*/
-void ssh_server_curve25519_init(ssh_session session){
+void ssh_server_curve25519_init(ssh_session session)
+{
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_curve25519_server_callbacks);
}
@@ -380,7 +225,8 @@ void ssh_server_curve25519_init(ssh_session session){
/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a
* SSH_MSG_KEXDH_REPLY
*/
-static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
+static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init)
+{
/* ECDH keys */
ssh_string q_c_string = NULL;
ssh_string q_s_string = NULL;
@@ -399,10 +245,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* Extract the client pubkey from the init packet */
q_c_string = ssh_buffer_get_ssh_string(packet);
if (q_c_string == NULL) {
- ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet");
+ ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet");
goto error;
}
- if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){
+ if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE) {
ssh_set_error(session,
SSH_FATAL,
"Incorrect size for server Curve25519 public key: %zu",
@@ -411,7 +257,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
}
memcpy(session->next_crypto->curve25519_client_pubkey,
- ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE);
+ ssh_string_data(q_c_string),
+ CURVE25519_PUBKEY_SIZE);
SSH_STRING_FREE(q_c_string);
/* Build server's key pair */
@@ -453,8 +300,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
}
/* add host's public key */
- rc = ssh_buffer_add_ssh_string(session->out_buffer,
- server_pubkey_blob);
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob);
SSH_STRING_FREE(server_pubkey_blob);
if (rc < 0) {
ssh_set_error_oom(session);
@@ -502,25 +348,20 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
return SSH_ERROR;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
SSH_STRING_FREE(q_c_string);
SSH_STRING_FREE(q_s_string);
ssh_buffer_reinit(session->out_buffer);
- session->session_state=SSH_SESSION_STATE_ERROR;
+ session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
diff --git a/src/curve25519_crypto.c b/src/curve25519_crypto.c
new file mode 100644
index 00000000..3314f70e
--- /dev/null
+++ b/src/curve25519_crypto.c
@@ -0,0 +1,164 @@
+/*
+ * curve25519_crypto.c - Curve25519 ECDH functions for key exchange (OpenSSL)
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013-2023 by Aris Adamantiadis
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libssh/curve25519.h"
+
+#include "libssh/crypto.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#include
+#include
+
+int ssh_curve25519_init(ssh_session session)
+{
+ ssh_curve25519_pubkey *pubkey_loc = NULL;
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
+ int rc;
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ }
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
+ if (pctx == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize X25519 context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_keygen_init(pctx);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize X25519 keygen: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(pctx);
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_keygen(pctx, &pkey);
+ EVP_PKEY_CTX_free(pctx);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to generate X25519 keys: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to get X25519 raw public key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return SSH_ERROR;
+ }
+
+ /* Free any previously allocated privkey */
+ if (session->next_crypto->curve25519_privkey != NULL) {
+ EVP_PKEY_free(session->next_crypto->curve25519_privkey);
+ session->next_crypto->curve25519_privkey = NULL;
+ }
+
+ session->next_crypto->curve25519_privkey = pkey;
+ pkey = NULL;
+
+ return SSH_OK;
+}
+
+int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
+{
+ ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
+ int rc, ret = SSH_ERROR;
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *pkey = NULL, *pubkey = NULL;
+ size_t shared_key_len = CURVE25519_PUBKEY_SIZE;
+
+ if (session->server) {
+ peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ } else {
+ peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ }
+
+ pkey = session->next_crypto->curve25519_privkey;
+ if (pkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to create X25519 EVP_PKEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return SSH_ERROR;
+ }
+
+ pctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (pctx == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize X25519 context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ rc = EVP_PKEY_derive_init(pctx);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize X25519 key derivation: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519,
+ NULL,
+ *peer_pubkey_loc,
+ CURVE25519_PUBKEY_SIZE);
+ if (pubkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to create X25519 public key EVP_PKEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to set peer X25519 public key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to derive X25519 shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+ ret = SSH_OK;
+
+out:
+ EVP_PKEY_free(pubkey);
+ EVP_PKEY_CTX_free(pctx);
+ return ret;
+}
diff --git a/src/curve25519_fallback.c b/src/curve25519_fallback.c
new file mode 100644
index 00000000..e05331d5
--- /dev/null
+++ b/src/curve25519_fallback.c
@@ -0,0 +1,73 @@
+/*
+ * curve25519_fallback.c - Curve25519 ECDH functions for key exchange
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013-2023 by Aris Adamantiadis
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libssh/curve25519.h"
+
+#include "libssh/crypto.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#ifdef WITH_NACL
+#include "nacl/crypto_scalarmult_curve25519.h"
+#endif
+
+int ssh_curve25519_init(ssh_session session)
+{
+ ssh_curve25519_pubkey *pubkey_loc = NULL;
+ int rc;
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ }
+
+ rc = ssh_get_random(session->next_crypto->curve25519_privkey,
+ CURVE25519_PRIVKEY_SIZE,
+ 1);
+ if (rc != 1) {
+ ssh_set_error(session, SSH_FATAL, "PRNG error");
+ return SSH_ERROR;
+ }
+
+ crypto_scalarmult_base(*pubkey_loc,
+ session->next_crypto->curve25519_privkey);
+
+ return SSH_OK;
+}
+
+int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
+{
+ ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
+
+ if (session->server) {
+ peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ } else {
+ peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ }
+
+ crypto_scalarmult(k,
+ session->next_crypto->curve25519_privkey,
+ *peer_pubkey_loc);
+ return SSH_OK;
+}
diff --git a/src/curve25519_gcrypt.c b/src/curve25519_gcrypt.c
new file mode 100644
index 00000000..cd522a1d
--- /dev/null
+++ b/src/curve25519_gcrypt.c
@@ -0,0 +1,205 @@
+/*
+ * curve25519_gcrypt.c - Curve25519 ECDH functions for key exchange (Gcrypt)
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013-2023 by Aris Adamantiadis
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libssh/curve25519.h"
+
+#include "libssh/buffer.h"
+#include "libssh/crypto.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#include
+
+int ssh_curve25519_init(ssh_session session)
+{
+ ssh_curve25519_pubkey *pubkey_loc = NULL;
+ gcry_error_t gcry_err;
+ gcry_sexp_t param = NULL, keypair_sexp = NULL;
+ ssh_string pubkey = NULL;
+ const char *pubkey_data = NULL;
+ int ret = SSH_ERROR;
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ }
+
+ gcry_err =
+ gcry_sexp_build(¶m, NULL, "(genkey (ecdh (curve Curve25519)))");
+ if (gcry_err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to create keypair sexp: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ gcry_err = gcry_pk_genkey(&keypair_sexp, param);
+ if (gcry_err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to generate keypair: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ /* Extract the public key */
+ pubkey = ssh_sexp_extract_mpi(keypair_sexp,
+ "q",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (pubkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to extract public key: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ /* Store the public key in the session */
+ /* The first byte should be 0x40 indicating that the point is compressed, so
+ * we skip storing it */
+ pubkey_data = (char *)ssh_string_data(pubkey);
+ if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 ||
+ pubkey_data[0] != 0x40) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Invalid public key with length: %zu",
+ ssh_string_len(pubkey));
+ goto out;
+ }
+
+ memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE);
+
+ /* Free any previously allocated privkey */
+ if (session->next_crypto->curve25519_privkey != NULL) {
+ gcry_sexp_release(session->next_crypto->curve25519_privkey);
+ session->next_crypto->curve25519_privkey = NULL;
+ }
+
+ /* Store the private key */
+ session->next_crypto->curve25519_privkey = keypair_sexp;
+ keypair_sexp = NULL;
+ ret = SSH_OK;
+
+out:
+ ssh_string_burn(pubkey);
+ SSH_STRING_FREE(pubkey);
+ gcry_sexp_release(param);
+ gcry_sexp_release(keypair_sexp);
+ return ret;
+}
+
+int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
+{
+ ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
+ gcry_error_t gcry_err;
+ gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL,
+ result_sexp = NULL;
+ ssh_string shared_secret = NULL, privkey = NULL;
+ char *shared_secret_data = NULL;
+ int ret = SSH_ERROR;
+
+ if (session->server) {
+ peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ } else {
+ peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ }
+
+ gcry_err = gcry_sexp_build(
+ &pubkey_sexp,
+ NULL,
+ "(key-data(public-key (ecdh (curve Curve25519) (q %b))))",
+ CURVE25519_PUBKEY_SIZE,
+ *peer_pubkey_loc);
+ if (gcry_err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to create peer public key sexp: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey,
+ "d",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (privkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key");
+ goto out;
+ }
+
+ gcry_err = gcry_sexp_build(&privkey_data_sexp,
+ NULL,
+ "(data(flags raw)(value %b))",
+ ssh_string_len(privkey),
+ ssh_string_data(privkey));
+ if (gcry_err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to create private key sexp: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp);
+ if (gcry_err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to compute shared secret: %s",
+ gcry_strerror(gcry_err));
+ goto out;
+ }
+
+ shared_secret = ssh_sexp_extract_mpi(result_sexp,
+ "s",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_USG);
+ if (shared_secret == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret");
+ goto out;
+ }
+
+ /* Copy the shared secret to the output buffer */
+ /* The first byte should be 0x40 indicating that it is a compressed point,
+ * so we skip it */
+ shared_secret_data = (char *)ssh_string_data(shared_secret);
+ if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 ||
+ shared_secret_data[0] != 0x40) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Invalid shared secret with length: %zu",
+ ssh_string_len(shared_secret));
+ goto out;
+ }
+
+ memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE);
+
+ ret = SSH_OK;
+ gcry_sexp_release(session->next_crypto->curve25519_privkey);
+ session->next_crypto->curve25519_privkey = NULL;
+
+out:
+ ssh_string_burn(shared_secret);
+ SSH_STRING_FREE(shared_secret);
+ ssh_string_burn(privkey);
+ SSH_STRING_FREE(privkey);
+ gcry_sexp_release(privkey_data_sexp);
+ gcry_sexp_release(pubkey_sexp);
+ gcry_sexp_release(result_sexp);
+ return ret;
+}
diff --git a/src/curve25519_mbedcrypto.c b/src/curve25519_mbedcrypto.c
new file mode 100644
index 00000000..f328f7b7
--- /dev/null
+++ b/src/curve25519_mbedcrypto.c
@@ -0,0 +1,189 @@
+/*
+ * curve25519_mbedcrypto.c - Curve25519 ECDH functions for key exchange
+ * (MbedTLS)
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013-2023 by Aris Adamantiadis
+ * Copyright (c) 2025 Praneeth Sarode
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 2.1 of the License.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libssh/curve25519.h"
+
+#include "libssh/crypto.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+#include "mbedcrypto-compat.h"
+
+#include
+#include
+
+int ssh_curve25519_init(ssh_session session)
+{
+ ssh_curve25519_pubkey *pubkey_loc = NULL;
+ mbedtls_ecdh_context ecdh_ctx;
+ mbedtls_ecdh_params *ecdh_params = NULL;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ int rc, ret = SSH_ERROR;
+ char error_buf[128];
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ }
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+
+ mbedtls_ecdh_init(&ecdh_ctx);
+ rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
+ goto out;
+ }
+
+ ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
+
+ rc = mbedtls_ecdh_gen_public(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to generate X25519 keypair: %s",
+ error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
+ session->next_crypto->curve25519_privkey,
+ CURVE25519_PRIVKEY_SIZE);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to write X25519 private key: %s",
+ error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_mpi_write_binary_le(
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(Q).MBEDTLS_ECDH_PRIVATE(X),
+ *pubkey_loc,
+ CURVE25519_PUBKEY_SIZE);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to write X25519 public key: %s",
+ error_buf);
+ goto out;
+ }
+
+ ret = SSH_OK;
+
+out:
+ mbedtls_ecdh_free(&ecdh_ctx);
+ return ret;
+}
+
+int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
+{
+ ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
+ int rc, ret = SSH_ERROR;
+ mbedtls_ecdh_context ecdh_ctx;
+ mbedtls_ecdh_params *ecdh_params = NULL;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ char error_buf[128];
+
+ if (session->server) {
+ peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
+ } else {
+ peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
+ }
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+
+ mbedtls_ecdh_init(&ecdh_ctx);
+ rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
+ goto out;
+ }
+
+ ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
+
+ rc = mbedtls_mpi_read_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
+ session->next_crypto->curve25519_privkey,
+ CURVE25519_PRIVKEY_SIZE);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_mpi_read_binary_le(
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(X),
+ *peer_pubkey_loc,
+ CURVE25519_PUBKEY_SIZE);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_mpi_lset(
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(Z),
+ 1);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_ecdh_compute_shared(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp),
+ &ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to compute shared secret: %s",
+ error_buf);
+ goto out;
+ }
+
+ rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
+ k,
+ CURVE25519_PUBKEY_SIZE);
+ if (rc != 0) {
+ mbedtls_strerror(rc, error_buf, sizeof(error_buf));
+ SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf);
+ goto out;
+ }
+
+ ret = SSH_OK;
+
+out:
+ mbedtls_ecdh_free(&ecdh_ctx);
+ return ret;
+}
diff --git a/src/dh-gex.c b/src/dh-gex.c
index ea30e4e9..f9fe3cd6 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -297,15 +297,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc = ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
@@ -414,6 +409,7 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
size_t line = 0;
size_t best_nlines = 0;
+ *best_size = 0;
for(;;) {
line++;
firstbyte = getc(moduli);
@@ -421,6 +417,9 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
do {
firstbyte = getc(moduli);
} while(firstbyte != '\n' && firstbyte != EOF);
+ if (firstbyte == EOF) {
+ break;
+ }
continue;
}
if (firstbyte == EOF) {
@@ -444,6 +443,9 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
do {
firstbyte = getc(moduli);
} while(firstbyte != '\n' && firstbyte != EOF);
+ if (firstbyte == EOF) {
+ break;
+ }
continue;
}
@@ -455,10 +457,9 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
proposed_size = size + 1;
if (proposed_size != *best_size &&
dhgroup_better_size(pmin, pn, pmax, *best_size, proposed_size)) {
- best_nlines = 0;
+ best_nlines = 1;
*best_size = proposed_size;
- }
- if (proposed_size == *best_size) {
+ } else if (proposed_size == *best_size) {
best_nlines++;
}
@@ -525,9 +526,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
}
if (moduli_file != NULL)
- moduli = fopen(moduli_file, "r");
+ moduli = ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE);
else
- moduli = fopen(MODULI_FILE, "r");
+ moduli = ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
if (moduli == NULL) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
@@ -577,12 +578,12 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request);
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init);
-static ssh_packet_callback dhgex_server_callbacks[]= {
- NULL, /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD */
- NULL, /* SSH_MSG_KEX_DH_GEX_GROUP */
+static ssh_packet_callback dhgex_server_callbacks[] = {
+ NULL, /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD */
+ NULL, /* SSH_MSG_KEX_DH_GEX_GROUP */
ssh_packet_server_dhgex_init, /* SSH_MSG_KEX_DH_GEX_INIT */
NULL, /* SSH_MSG_KEX_DH_GEX_REPLY */
- ssh_packet_server_dhgex_request /* SSH_MSG_GEX_DH_GEX_REQUEST */
+ ssh_packet_server_dhgex_request /* SSH_MSG_KEX_DH_GEX_REQUEST */
};
@@ -648,7 +649,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
pn = pmin;
}
}
- rc = ssh_retrieve_dhgroup(session->opts.moduli_file,
+ rc = ssh_retrieve_dhgroup(session->server_opts.moduli_file,
pmin,
pn,
pmax,
diff --git a/src/dh.c b/src/dh.c
index cd2876cb..c04418dd 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -26,6 +26,10 @@
#include "config.h"
#include
+#ifdef WITH_GSSAPI
+#include "libssh/gssapi.h"
+#include
+#endif
#include "libssh/priv.h"
#include "libssh/crypto.h"
@@ -36,6 +40,7 @@
#include "libssh/ssh2.h"
#include "libssh/pki.h"
#include "libssh/bignum.h"
+#include "libssh/string.h"
static unsigned char p_group1_value[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
@@ -398,16 +403,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
@@ -551,15 +550,12 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
}
SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY");
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- ssh_buffer_reinit(session->out_buffer);
- goto error;
- }
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
- if (ssh_packet_send(session) == SSH_ERROR) {
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
return SSH_OK;
error:
diff --git a/src/dh_crypto.c b/src/dh_crypto.c
index 9ff7ad3c..647e3bdd 100644
--- a/src/dh_crypto.c
+++ b/src/dh_crypto.c
@@ -404,9 +404,14 @@ int ssh_dh_set_parameters(struct dh_ctx *ctx,
*/
int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
{
- struct dh_ctx *ctx;
+ struct dh_ctx *ctx = NULL;
int rc;
+ /* Cleanup any previously allocated dh_ctx */
+ if (crypto->dh_ctx != NULL) {
+ ssh_dh_cleanup(crypto);
+ }
+
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
return SSH_ERROR;
@@ -419,9 +424,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break;
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
+ case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP16_SHA512:
+ case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP18_SHA512:
@@ -592,7 +599,7 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
}
#endif /* OPENSSL_VERSION_NUMBER */
- *dest = BN_bin2bn(kstring, klen, NULL);
+ *dest = BN_bin2bn(kstring, (int)klen, NULL);
if (*dest == NULL) {
rc = SSH_ERROR;
goto done;
diff --git a/src/dh_key.c b/src/dh_key.c
index 20d24a31..19785a9a 100644
--- a/src/dh_key.c
+++ b/src/dh_key.c
@@ -237,6 +237,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
struct dh_ctx *ctx = NULL;
int rc;
+ /* Cleanup any previously allocated dh_ctx */
+ if (crypto->dh_ctx != NULL) {
+ ssh_dh_cleanup(crypto);
+ }
+
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
return SSH_ERROR;
@@ -248,9 +253,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break;
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
+ case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP16_SHA512:
+ case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP18_SHA512:
diff --git a/src/ecdh.c b/src/ecdh.c
index fa5e08c5..af80beec 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -93,16 +93,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c
index e31cc0aa..6b4e7706 100644
--- a/src/ecdh_crypto.c
+++ b/src/ecdh_crypto.c
@@ -51,18 +51,25 @@ static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
#else
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
#endif /* OPENSSL_VERSION_NUMBER */
- if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
+ switch (kex_type) {
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_KEX_MLKEM768NISTP256_SHA256:
+ case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return NISTP256;
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
+ case SSH_KEX_ECDH_SHA2_NISTP384:
+#if HAVE_MLKEM1024
+ case SSH_KEX_MLKEM1024NISTP384_SHA384:
+#endif
return NISTP384;
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
+ case SSH_KEX_ECDH_SHA2_NISTP521:
return NISTP521;
- }
+ default:
#if OPENSSL_VERSION_NUMBER < 0x30000000L
- return SSH_ERROR;
+ return SSH_ERROR;
#else
- return NULL;
+ return NULL;
#endif
+ }
}
/* @internal
@@ -191,16 +198,51 @@ static ssh_string ssh_ecdh_generate(ssh_session session)
#endif /* OPENSSL_VERSION_NUMBER */
return NULL;
}
+
+ /* Free any previously allocated privkey */
+ if (session->next_crypto->ecdh_privkey != NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(session->next_crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(session->next_crypto->ecdh_privkey);
+#endif
+ session->next_crypto->ecdh_privkey = NULL;
+ }
+
session->next_crypto->ecdh_privkey = key;
return pubkey_string;
}
+/** @internal
+ * @brief Set up a nistp{256,384,521} key pair for ECDH key exchange.
+ */
+int ssh_ecdh_init(ssh_session session)
+{
+ ssh_string pubkey = NULL;
+ ssh_string *pubkey_loc = NULL;
+
+ pubkey = ssh_ecdh_generate(session);
+ if (pubkey == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->ecdh_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->ecdh_client_pubkey;
+ }
+
+ ssh_string_free(*pubkey_loc);
+ *pubkey_loc = pubkey;
+
+ return SSH_OK;
+}
+
/** @internal
* @brief Starts ecdh-sha2-nistp256 key exchange
*/
int ssh_client_ecdh_init(ssh_session session)
{
- ssh_string client_pubkey = NULL;
int rc;
rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
@@ -208,19 +250,17 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR;
}
- client_pubkey = ssh_ecdh_generate(session);
- if (client_pubkey == NULL) {
+ rc = ssh_ecdh_init(session);
+ if (rc < 0) {
return SSH_ERROR;
}
- rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
+ rc = ssh_buffer_add_ssh_string(session->out_buffer,
+ session->next_crypto->ecdh_client_pubkey);
if (rc < 0) {
- ssh_string_free(client_pubkey);
return SSH_ERROR;
}
- session->next_crypto->ecdh_client_pubkey = client_pubkey;
-
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
session->dh_handshake_state = DH_STATE_INIT_SENT;
@@ -397,6 +437,7 @@ int ecdh_build_k(ssh_session session)
"Could not derive shared key: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_CTX_free(dh_ctx);
+ free(secret);
return -1;
}
@@ -441,9 +482,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
{
/* ECDH keys */
ssh_string q_c_string = NULL;
- ssh_string q_s_string = NULL;
/* SSH host keys (rsa, ed25519 and ecdsa) */
- ssh_key privkey;
+ ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
@@ -462,13 +502,11 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- q_s_string = ssh_ecdh_generate(session);
- if (q_s_string == NULL) {
+ rc = ssh_ecdh_init(session);
+ if (rc < 0) {
goto error;
}
- session->next_crypto->ecdh_server_pubkey = q_s_string;
-
/* build k and session_id */
rc = ecdh_build_k(session);
if (rc < 0) {
@@ -505,7 +543,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
"bSSS",
SSH2_MSG_KEXDH_REPLY,
pubkey_blob, /* host's pubkey */
- q_s_string, /* ecdh public key */
+ session->next_crypto->ecdh_server_pubkey, /* ecdh public key */
sig_blob); /* signature blob */
SSH_STRING_FREE(sig_blob);
@@ -522,18 +560,12 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
goto error;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- if (rc == SSH_ERROR){
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c
index 76e24873..ec7dccfd 100644
--- a/src/ecdh_gcrypt.c
+++ b/src/ecdh_gcrypt.c
@@ -36,45 +36,54 @@
/** @internal
* @brief Map the given key exchange enum value to its curve name.
*/
-static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
- if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
+static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
+{
+ switch (kex_type) {
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
+ case SSH_KEX_MLKEM768NISTP256_SHA256:
return "NIST P-256";
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
+ case SSH_KEX_ECDH_SHA2_NISTP384:
+#if HAVE_MLKEM1024
+ case SSH_KEX_MLKEM1024NISTP384_SHA384:
+#endif
return "NIST P-384";
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
+ case SSH_KEX_ECDH_SHA2_NISTP521:
return "NIST P-521";
+ default:
+ return NULL;
}
- return NULL;
}
/** @internal
- * @brief Starts ecdh-sha2-nistp{256,384,521} key exchange.
+ * @brief Set up a nistp{256,384,521} key pair for ECDH key exchange.
*/
-int ssh_client_ecdh_init(ssh_session session)
+int ssh_ecdh_init(ssh_session session)
{
- int rc;
+ int rc = SSH_OK;
+ const char *curve = NULL;
+ const char *genstring = NULL;
gpg_error_t err;
- ssh_string client_pubkey = NULL;
gcry_sexp_t param = NULL;
gcry_sexp_t key = NULL;
- const char *curve = NULL;
+ ssh_string pubkey = NULL;
+ ssh_string *pubkey_loc = NULL;
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == NULL) {
- rc = SSH_ERROR;
- goto out;
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->ecdh_server_pubkey;
+ genstring = "(genkey(ecdh(curve %s) (flags transient-key)))";
+ } else {
+ pubkey_loc = &session->next_crypto->ecdh_client_pubkey;
+ genstring = "(genkey(ecdh(curve %s)))";
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
- if (rc < 0) {
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+ if (curve == NULL) {
rc = SSH_ERROR;
goto out;
}
- err = gcry_sexp_build(¶m,
- NULL,
- "(genkey(ecdh(curve %s)))",
- curve);
+ err = gcry_sexp_build(¶m, NULL, genstring, curve);
if (err) {
rc = SSH_ERROR;
goto out;
@@ -86,36 +95,59 @@ int ssh_client_ecdh_init(ssh_session session)
goto out;
}
- client_pubkey = ssh_sexp_extract_mpi(key,
- "q",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (client_pubkey == NULL) {
+ pubkey = ssh_sexp_extract_mpi(key, "q", GCRYMPI_FMT_USG, GCRYMPI_FMT_STD);
+ if (pubkey == NULL) {
rc = SSH_ERROR;
goto out;
}
- rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
- if (rc < 0) {
- rc = SSH_ERROR;
- goto out;
+ /* Free any previously allocated privkey */
+ if (session->next_crypto->ecdh_privkey != NULL) {
+ gcry_sexp_release(session->next_crypto->ecdh_privkey);
+ session->next_crypto->ecdh_privkey = NULL;
}
-
session->next_crypto->ecdh_privkey = key;
key = NULL;
- session->next_crypto->ecdh_client_pubkey = client_pubkey;
- client_pubkey = NULL;
+
+ SSH_STRING_FREE(*pubkey_loc);
+ *pubkey_loc = pubkey;
+ pubkey = NULL;
+
+out:
+ gcry_sexp_release(param);
+ gcry_sexp_release(key);
+ SSH_STRING_FREE(pubkey);
+ return rc;
+}
+
+/** @internal
+ * @brief Starts ecdh-sha2-nistp{256,384,521} key exchange.
+ */
+int ssh_client_ecdh_init(ssh_session session)
+{
+ int rc;
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_ecdh_init(session);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_buffer,
+ session->next_crypto->ecdh_client_pubkey);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
session->dh_handshake_state = DH_STATE_INIT_SENT;
rc = ssh_packet_send(session);
-
- out:
- gcry_sexp_release(param);
- gcry_sexp_release(key);
- SSH_STRING_FREE(client_pubkey);
return rc;
}
@@ -132,9 +164,9 @@ int ecdh_build_k(ssh_session session)
#else
size_t k_len = 0;
enum ssh_key_exchange_e kex_type = session->next_crypto->kex_type;
- ssh_string s;
+ ssh_string s = NULL;
#endif
- ssh_string pubkey_raw;
+ ssh_string pubkey_raw = NULL;
gcry_sexp_t pubkey = NULL;
ssh_string privkey = NULL;
int rc = SSH_ERROR;
@@ -265,27 +297,16 @@ int ecdh_build_k(ssh_session session)
* SSH_MSG_KEXDH_REPLY
*/
SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
- gpg_error_t err;
- /* ECDH keys */
- ssh_string q_c_string;
- ssh_string q_s_string;
- gcry_sexp_t param = NULL;
- gcry_sexp_t key = NULL;
- /* SSH host keys (rsa, ed25519 and ecdsa) */
- ssh_key privkey;
+ ssh_string q_c_string = NULL;
+ ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
int rc = SSH_ERROR;
- const char *curve = NULL;
(void)type;
(void)user;
ssh_packet_remove_callbacks(session, &ssh_ecdh_server_callbacks);
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == NULL) {
- goto out;
- }
/* Extract the client pubkey from the init packet */
q_c_string = ssh_buffer_get_ssh_string(packet);
@@ -295,29 +316,12 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- /* Build server's key pair */
- err = gcry_sexp_build(¶m, NULL, "(genkey(ecdh(curve %s) (flags transient-key)))",
- curve);
- if (err) {
- goto out;
- }
-
- err = gcry_pk_genkey(&key, param);
- if (err)
- goto out;
-
- q_s_string = ssh_sexp_extract_mpi(key,
- "q",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (q_s_string == NULL) {
+ rc = ssh_ecdh_init(session);
+ if (rc != SSH_OK) {
+ ssh_set_error(session, SSH_FATAL, "Failed to generate a key pair");
goto out;
}
- session->next_crypto->ecdh_privkey = key;
- key = NULL;
- session->next_crypto->ecdh_server_pubkey = q_s_string;
-
/* build k and session_id */
rc = ecdh_build_k(session);
if (rc != SSH_OK) {
@@ -355,7 +359,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
"bSSS",
SSH2_MSG_KEXDH_REPLY,
pubkey_blob, /* host's pubkey */
- q_s_string, /* ecdh public key */
+ session->next_crypto->ecdh_server_pubkey, /* ecdh public key */
sig_blob); /* signature blob */
SSH_STRING_FREE(sig_blob);
@@ -372,20 +376,14 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
-
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc != SSH_OK) {
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
-
out:
- gcry_sexp_release(param);
- gcry_sexp_release(key);
if (rc == SSH_ERROR) {
ssh_buffer_reinit(session->out_buffer);
session->session_state = SSH_SESSION_STATE_ERROR;
diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c
index 1c930fb5..7d013b74 100644
--- a/src/ecdh_mbedcrypto.c
+++ b/src/ecdh_mbedcrypto.c
@@ -38,25 +38,39 @@
#ifdef HAVE_ECDH
-static mbedtls_ecp_group_id ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
- if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
+static mbedtls_ecp_group_id
+ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
+{
+ switch (kex_type) {
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_KEX_MLKEM768NISTP256_SHA256:
+ case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return MBEDTLS_ECP_DP_SECP256R1;
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
+ case SSH_KEX_ECDH_SHA2_NISTP384:
return MBEDTLS_ECP_DP_SECP384R1;
- } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
+ case SSH_KEX_ECDH_SHA2_NISTP521:
return MBEDTLS_ECP_DP_SECP521R1;
+ default:
+ return MBEDTLS_ECP_DP_NONE;
}
-
return MBEDTLS_ECP_DP_NONE;
}
-int ssh_client_ecdh_init(ssh_session session)
+
+int ssh_ecdh_init(ssh_session session)
{
- ssh_string client_pubkey = NULL;
- mbedtls_ecp_group grp;
int rc;
+ mbedtls_ecp_group grp;
mbedtls_ecp_group_id curve;
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
mbedtls_ecp_keypair *ecdh_privkey = NULL;
+ ssh_string pubkey = NULL;
+ ssh_string *pubkey_loc = NULL;
+
+ if (session->server) {
+ pubkey_loc = &session->next_crypto->ecdh_server_pubkey;
+ } else {
+ pubkey_loc = &session->next_crypto->ecdh_client_pubkey;
+ }
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
@@ -65,9 +79,10 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR;
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
- if (rc < 0) {
- return SSH_ERROR;
+ /* Free any previously allocated privkey */
+ if (session->next_crypto->ecdh_privkey != NULL) {
+ mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
+ SAFE_FREE(session->next_crypto->ecdh_privkey);
}
session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
@@ -87,41 +102,56 @@ int ssh_client_ecdh_init(ssh_session session)
}
rc = mbedtls_ecp_gen_keypair(&grp,
- &ecdh_privkey->MBEDTLS_PRIVATE(d),
- &ecdh_privkey->MBEDTLS_PRIVATE(Q),
- mbedtls_ctr_drbg_random,
- ctr_drbg);
-
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
goto out;
}
- client_pubkey = make_ecpoint_string(&grp,
- &ecdh_privkey->MBEDTLS_PRIVATE(Q));
- if (client_pubkey == NULL) {
+ pubkey = make_ecpoint_string(&grp, &ecdh_privkey->MBEDTLS_PRIVATE(Q));
+ if (pubkey == NULL) {
rc = SSH_ERROR;
goto out;
}
- rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
+ SSH_STRING_FREE(*pubkey_loc);
+ *pubkey_loc = pubkey;
+ pubkey = NULL;
+
+out:
+ mbedtls_ecp_group_free(&grp);
+ SSH_STRING_FREE(pubkey);
+ return rc;
+}
+
+int ssh_client_ecdh_init(ssh_session session)
+{
+ int rc;
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
if (rc < 0) {
- rc = SSH_ERROR;
- goto out;
+ return SSH_ERROR;
+ }
+
+ rc = ssh_ecdh_init(session);
+ if (rc < 0) {
+ return SSH_ERROR;
}
- session->next_crypto->ecdh_client_pubkey = client_pubkey;
- client_pubkey = NULL;
+ rc = ssh_buffer_add_ssh_string(session->out_buffer,
+ session->next_crypto->ecdh_client_pubkey);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
session->dh_handshake_state = DH_STATE_INIT_SENT;
rc = ssh_packet_send(session);
-out:
- mbedtls_ecp_group_free(&grp);
- SSH_STRING_FREE(client_pubkey);
-
return rc;
}
@@ -197,24 +227,15 @@ int ecdh_build_k(ssh_session session)
SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
ssh_string q_c_string = NULL;
- ssh_string q_s_string = NULL;
- mbedtls_ecp_group grp;
- mbedtls_ctr_drbg_context *ctr_drbg = NULL;
- mbedtls_ecp_keypair *ecdh_privkey = NULL;
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
int rc;
- mbedtls_ecp_group_id curve;
(void)type;
(void)user;
ssh_packet_remove_callbacks(session, &ssh_ecdh_server_callbacks);
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == MBEDTLS_ECP_DP_NONE) {
- return SSH_ERROR;
- }
q_c_string = ssh_buffer_get_ssh_string(packet);
if (q_c_string == NULL) {
@@ -222,45 +243,13 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
return SSH_ERROR;
}
- session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
- if (session->next_crypto->ecdh_privkey == NULL) {
- ssh_set_error_oom(session);
- return SSH_ERROR;
- }
-
- ecdh_privkey = session->next_crypto->ecdh_privkey;
-
session->next_crypto->ecdh_client_pubkey = q_c_string;
- ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
-
- mbedtls_ecp_group_init(&grp);
- mbedtls_ecp_keypair_init(ecdh_privkey);
-
- rc = mbedtls_ecp_group_load(&grp, curve);
- if (rc != 0) {
- rc = SSH_ERROR;
- goto out;
- }
-
- rc = mbedtls_ecp_gen_keypair(&grp,
- &ecdh_privkey->MBEDTLS_PRIVATE(d),
- &ecdh_privkey->MBEDTLS_PRIVATE(Q),
- mbedtls_ctr_drbg_random,
- ctr_drbg);
- if (rc != 0) {
- rc = SSH_ERROR;
- goto out;
- }
-
- q_s_string = make_ecpoint_string(&grp, &ecdh_privkey->MBEDTLS_PRIVATE(Q));
- if (q_s_string == NULL) {
- rc = SSH_ERROR;
- goto out;
+ rc = ssh_ecdh_init(session);
+ if (rc < 0) {
+ return SSH_ERROR;
}
- session->next_crypto->ecdh_server_pubkey = q_s_string;
-
/* build k and session_id */
rc = ecdh_build_k(session);
if (rc != SSH_OK) {
@@ -299,7 +288,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
rc = ssh_buffer_pack(session->out_buffer, "bSSS",
SSH2_MSG_KEXDH_REPLY,
pubkey_blob, /* host's pubkey */
- q_s_string, /* ecdh public key */
+ session->next_crypto->ecdh_server_pubkey, /* ecdh public key */
sig_blob); /* signature blob */
SSH_STRING_FREE(sig_blob);
@@ -318,18 +307,14 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- rc = SSH_ERROR;
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
-
out:
- mbedtls_ecp_group_free(&grp);
if (rc == SSH_ERROR) {
ssh_buffer_reinit(session->out_buffer);
session->session_state = SSH_SESSION_STATE_ERROR;
diff --git a/src/external/bcrypt_pbkdf.c b/src/external/bcrypt_pbkdf.c
index 85f4be47..b05b87f2 100644
--- a/src/external/bcrypt_pbkdf.c
+++ b/src/external/bcrypt_pbkdf.c
@@ -97,8 +97,8 @@ bcrypt_hash(ssh_blf_ctx *state, uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *o
}
/* zap */
- explicit_bzero(ciphertext, sizeof(ciphertext));
- explicit_bzero(cdata, sizeof(cdata));
+ ssh_burn(ciphertext, sizeof(ciphertext));
+ ssh_burn(cdata, sizeof(cdata));
}
int
@@ -180,12 +180,12 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
}
/* zap */
- explicit_bzero(out, sizeof(out));
- explicit_bzero(state, sizeof(*state));
+ ssh_burn(out, sizeof(out));
+ ssh_burn(state, sizeof(*state));
- free(state);
- free(countsalt);
+ free(state);
+ free(countsalt);
- return 0;
+ return 0;
}
#endif /* HAVE_BCRYPT_PBKDF */
diff --git a/src/external/blowfish.c b/src/external/blowfish.c
index 4008a9c0..42d5df1e 100644
--- a/src/external/blowfish.c
+++ b/src/external/blowfish.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: blowfish.c,v 1.18 2004/11/02 17:23:26 hshoexer Exp $ */
+/* $OpenBSD: blowfish.c,v 1.20 2021/11/29 01:04:45 djm Exp $ */
/*
* Blowfish block cipher for OpenBSD
* Copyright 1997 Niels Provos
@@ -14,10 +14,7 @@
* 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 acknowledgement:
- * This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
+ * 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
diff --git a/src/external/libcrux_mlkem768_sha3.c b/src/external/libcrux_mlkem768_sha3.c
new file mode 100644
index 00000000..59130fbd
--- /dev/null
+++ b/src/external/libcrux_mlkem768_sha3.c
@@ -0,0 +1,8897 @@
+/* $OpenBSD: libcrux_mlkem768_sha3.h,v 1.4 2025/11/13 05:13:06 djm Exp $ */
+
+/* Extracted from libcrux revision 026a87ab6d88ad3626b9fbbf3710d1e0483c1849 */
+
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Cryspen
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include
+
+#include "libssh/mlkem_native.h"
+
+#if !defined(__GNUC__) || (__GNUC__ < 2)
+# define __attribute__(x)
+#endif
+#define KRML_MUSTINLINE inline
+#define KRML_NOINLINE __attribute__((noinline, unused))
+#define KRML_HOST_EPRINTF(...)
+#define KRML_HOST_EXIT(x) do { \
+ fprintf(stderr, "mlkem internal error"); \
+ exit(x); \
+} while (0)
+
+static inline void
+store64_le(uint8_t dst[8], uint64_t src)
+{
+ dst[0] = src & 0xff;
+ dst[1] = (src >> 8) & 0xff;
+ dst[2] = (src >> 16) & 0xff;
+ dst[3] = (src >> 24) & 0xff;
+ dst[4] = (src >> 32) & 0xff;
+ dst[5] = (src >> 40) & 0xff;
+ dst[6] = (src >> 48) & 0xff;
+ dst[7] = (src >> 56) & 0xff;
+}
+
+static inline uint64_t
+load64_le(uint8_t src[8])
+{
+ return (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);
+}
+
+#ifdef MISSING_BUILTIN_POPCOUNT
+static inline unsigned int
+__builtin_popcount(unsigned int num)
+{
+ const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
+ return v[num & 0xf] + v[(num >> 4) & 0xf];
+}
+#endif
+
+/* from libcrux/libcrux-ml-kem/extracts/c_header_only/generated/eurydice_glue.h */
+
+
+#ifdef _MSC_VER
+// For __popcnt
+#endif
+
+
+// C++ HELPERS
+
+#if defined(__cplusplus)
+
+#ifndef KRML_HOST_EPRINTF
+#define KRML_HOST_EPRINTF(...) fprintf(stderr, __VA_ARGS__)
+#endif
+
+
+#ifndef __cpp_lib_type_identity
+template
+struct type_identity {
+ using type = T;
+};
+
+template
+using type_identity_t = typename type_identity::type;
+#else
+using std::type_identity_t;
+#endif
+
+#define KRML_UNION_CONSTRUCTOR(T) \
+ template \
+ constexpr T(int t, V U::*m, type_identity_t v) : tag(t) { \
+ val.*m = std::move(v); \
+ } \
+ T() = default;
+
+#endif
+
+// GENERAL-PURPOSE STUFF
+
+#define LowStar_Ignore_ignore(e, t, _ret_t) ((void)e)
+
+#define EURYDICE_ASSERT(test, msg) \
+ do { \
+ if (!(test)) { \
+ fprintf(stderr, "assertion \"%s\" failed: file \"%s\", line %d\n", msg, \
+ __FILE__, __LINE__); \
+ exit(255); \
+ } \
+ } while (0)
+
+// SLICES, ARRAYS, ETC.
+
+// We represent a slice as a pair of an (untyped) pointer, along with the length
+// of the slice, i.e. the number of elements in the slice (this is NOT the
+// number of bytes). This design choice has two important consequences.
+// - if you need to use `ptr`, you MUST cast it to a proper type *before*
+// performing pointer arithmetic on it (remember that C desugars pointer
+// arithmetic based on the type of the address)
+// - if you need to use `len` for a C style function (e.g. memcpy, memcmp), you
+// need to multiply it by sizeof t, where t is the type of the elements.
+//
+// Empty slices have `len == 0` and `ptr` always needs to be a valid pointer
+// that is not NULL (otherwise the construction in EURYDICE_SLICE computes `NULL
+// + start`).
+typedef struct {
+ void *ptr;
+ size_t len;
+} Eurydice_slice;
+
+#if defined(__cplusplus)
+#define KRML_CLITERAL(type) type
+#else
+#define KRML_CLITERAL(type) (type)
+#endif
+
+#if defined(__cplusplus) && defined(__cpp_designated_initializers) || \
+ !(defined(__cplusplus))
+#define EURYDICE_CFIELD(X) X
+#else
+#define EURYDICE_CFIELD(X)
+#endif
+
+// Helper macro to create a slice out of a pointer x, a start index in x
+// (included), and an end index in x (excluded). The argument x must be suitably
+// cast to something that can decay (see remark above about how pointer
+// arithmetic works in C), meaning either pointer or array type.
+#define EURYDICE_SLICE(x, start, end) \
+ (KRML_CLITERAL(Eurydice_slice){(void *)(x + start), end - start})
+
+// Slice length
+#define EURYDICE_SLICE_LEN(s, _) (s).len
+#define Eurydice_slice_len(s, _) (s).len
+
+// This macro is a pain because in case the dereferenced element type is an
+// array, you cannot simply write `t x` as it would yield `int[4] x` instead,
+// which is NOT correct C syntax, so we add a dedicated phase in Eurydice that
+// adds an extra argument to this macro at the last minute so that we have the
+// correct type of *pointers* to elements.
+#define Eurydice_slice_index(s, i, t, t_ptr_t) (((t_ptr_t)s.ptr)[i])
+
+// The following functions get sub slices from a slice.
+
+#define Eurydice_slice_subslice(s, r, t, _0, _1) \
+ EURYDICE_SLICE((t *)s.ptr, r.start, r.end)
+
+// Variant for when the start and end indices are statically known (i.e., the
+// range argument `r` is a literal).
+#define Eurydice_slice_subslice2(s, start, end, t) \
+ EURYDICE_SLICE((t *)s.ptr, (start), (end))
+
+// Previous version above does not work when t is an array type (as usual). Will
+// be deprecated soon.
+#define Eurydice_slice_subslice3(s, start, end, t_ptr) \
+ EURYDICE_SLICE((t_ptr)s.ptr, (start), (end))
+
+#define Eurydice_slice_subslice_to(s, subslice_end_pos, t, _0, _1) \
+ EURYDICE_SLICE((t *)s.ptr, 0, subslice_end_pos)
+
+#define Eurydice_slice_subslice_from(s, subslice_start_pos, t, _0, _1) \
+ EURYDICE_SLICE((t *)s.ptr, subslice_start_pos, s.len)
+
+#define Eurydice_array_to_slice(end, x, t) \
+ EURYDICE_SLICE(x, 0, \
+ end) /* x is already at an array type, no need for cast */
+#define Eurydice_array_to_subslice(_arraylen, x, r, t, _0, _1) \
+ EURYDICE_SLICE((t *)x, r.start, r.end)
+
+// Same as above, variant for when start and end are statically known
+#define Eurydice_array_to_subslice2(x, start, end, t) \
+ EURYDICE_SLICE((t *)x, (start), (end))
+
+// Same as above, variant for when start and end are statically known
+#define Eurydice_array_to_subslice3(x, start, end, t_ptr) \
+ EURYDICE_SLICE((t_ptr)x, (start), (end))
+
+#define Eurydice_array_repeat(dst, len, init, t) \
+ ERROR "should've been desugared"
+
+// The following functions convert an array into a slice.
+
+#define Eurydice_array_to_subslice_to(_size, x, r, t, _range_t, _0) \
+ EURYDICE_SLICE((t *)x, 0, r)
+#define Eurydice_array_to_subslice_from(size, x, r, t, _range_t, _0) \
+ EURYDICE_SLICE((t *)x, r, size)
+
+// Copy a slice with memcopy
+#define Eurydice_slice_copy(dst, src, t) \
+ memcpy(dst.ptr, src.ptr, dst.len * sizeof(t))
+
+#define core_array___Array_T__N___as_slice(len_, ptr_, t, _ret_t) \
+ KRML_CLITERAL(Eurydice_slice) { ptr_, len_ }
+
+#define core_array__core__clone__Clone_for__Array_T__N___clone( \
+ len, src, dst, elem_type, _ret_t) \
+ (memcpy(dst, src, len * sizeof(elem_type)))
+#define TryFromSliceError uint8_t
+#define core_array_TryFromSliceError uint8_t
+
+#define Eurydice_array_eq(sz, a1, a2, t) (memcmp(a1, a2, sz * sizeof(t)) == 0)
+
+// core::cmp::PartialEq<&0 (@Slice)> for @Array
+#define Eurydice_array_eq_slice(sz, a1, s2, t, _) \
+ (memcmp(a1, (s2)->ptr, sz * sizeof(t)) == 0)
+
+#define core_array_equality___core__cmp__PartialEq__Array_U__N___for__Array_T__N____eq( \
+ sz, a1, a2, t, _, _ret_t) \
+ Eurydice_array_eq(sz, a1, a2, t, _)
+#define core_array_equality___core__cmp__PartialEq__0___Slice_U____for__Array_T__N___3__eq( \
+ sz, a1, a2, t, _, _ret_t) \
+ Eurydice_array_eq(sz, a1, ((a2)->ptr), t, _)
+
+#define Eurydice_slice_split_at(slice, mid, element_type, ret_t) \
+ KRML_CLITERAL(ret_t) { \
+ EURYDICE_CFIELD(.fst =) \
+ EURYDICE_SLICE((element_type *)(slice).ptr, 0, mid), \
+ EURYDICE_CFIELD(.snd =) \
+ EURYDICE_SLICE((element_type *)(slice).ptr, mid, (slice).len) \
+ }
+
+#define Eurydice_slice_split_at_mut(slice, mid, element_type, ret_t) \
+ KRML_CLITERAL(ret_t) { \
+ EURYDICE_CFIELD(.fst =) \
+ KRML_CLITERAL(Eurydice_slice){EURYDICE_CFIELD(.ptr =)(slice.ptr), \
+ EURYDICE_CFIELD(.len =) mid}, \
+ EURYDICE_CFIELD(.snd =) KRML_CLITERAL(Eurydice_slice) { \
+ EURYDICE_CFIELD(.ptr =) \
+ ((char *)slice.ptr + mid * sizeof(element_type)), \
+ EURYDICE_CFIELD(.len =)(slice.len - mid) \
+ } \
+ }
+
+// Conversion of slice to an array, rewritten (by Eurydice) to name the
+// destination array, since arrays are not values in C.
+// N.B.: see note in karamel/lib/Inlining.ml if you change this.
+#define Eurydice_slice_to_array2(dst, src, _0, t_arr, _1) \
+ Eurydice_slice_to_array3(&(dst)->tag, (char *)&(dst)->val.case_Ok, src, \
+ sizeof(t_arr))
+
+static inline void Eurydice_slice_to_array3(uint8_t *dst_tag, char *dst_ok,
+ Eurydice_slice src, size_t sz) {
+ *dst_tag = 0;
+ memcpy(dst_ok, src.ptr, sz);
+}
+
+// SUPPORT FOR DSTs (Dynamically-Sized Types)
+
+// A DST is a fat pointer that keeps tracks of the size of it flexible array
+// member. Slices are a specific case of DSTs, where [T; N] implements
+// Unsize<[T]>, meaning an array of statically known size can be converted to a
+// fat pointer, i.e. a slice.
+//
+// Unlike slices, DSTs have a built-in definition that gets monomorphized, of
+// the form:
+//
+// typedef struct {
+// T *ptr;
+// size_t len; // number of elements
+// } Eurydice_dst;
+//
+// Furthermore, T = T0<[U0]> where `struct T0`, where the `U` is the
+// last field. This means that there are two monomorphizations of T0 in the
+// program. One is `T0<[V; N]>`
+// -- this is directly converted to a Eurydice_dst via suitable codegen (no
+// macro). The other is `T = T0<[U]>`, where `[U]` gets emitted to
+// `Eurydice_derefed_slice`, a type that only appears in that precise situation
+// and is thus defined to give rise to a flexible array member.
+
+typedef char Eurydice_derefed_slice[];
+
+#define Eurydice_slice_of_dst(fam_ptr, len_, t, _) \
+ ((Eurydice_slice){.ptr = (void *)(fam_ptr), .len = len_})
+
+#define Eurydice_slice_of_boxed_array(ptr_, len_, t, _) \
+ ((Eurydice_slice){.ptr = (void *)(ptr_), .len = len_})
+
+// CORE STUFF (conversions, endianness, ...)
+
+// We slap extern "C" on declarations that intend to implement a prototype
+// generated by Eurydice, because Eurydice prototypes are always emitted within
+// an extern "C" block, UNLESS you use -fcxx17-compat, in which case, you must
+// pass -DKRML_CXX17_COMPAT="" to your C++ compiler.
+#if defined(__cplusplus) && !defined(KRML_CXX17_COMPAT)
+extern "C" {
+#endif
+
+static inline void core_num__u64__to_le_bytes(uint64_t v, uint8_t buf[8]) {
+ store64_le(buf, v);
+}
+
+static inline uint64_t core_num__u64__from_le_bytes(uint8_t buf[8]) {
+ return load64_le(buf);
+}
+
+// unsigned overflow wraparound semantics in C
+static inline uint16_t core_num__u16__wrapping_add(uint16_t x, uint16_t y) {
+ return x + y;
+}
+static inline uint8_t core_num__u8__wrapping_sub(uint8_t x, uint8_t y) {
+ return x - y;
+}
+static inline uint64_t core_num__u64__rotate_left(uint64_t x0, uint32_t x1) {
+ return (x0 << x1 | x0 >> (64 - x1));
+}
+
+#if defined(__cplusplus) && !defined(KRML_CXX17_COMPAT)
+}
+#endif
+
+// ITERATORS
+
+#define Eurydice_range_iter_next(iter_ptr, t, ret_t) \
+ (((iter_ptr)->start >= (iter_ptr)->end) \
+ ? (KRML_CLITERAL(ret_t){EURYDICE_CFIELD(.tag =) 0, \
+ EURYDICE_CFIELD(.f0 =) 0}) \
+ : (KRML_CLITERAL(ret_t){EURYDICE_CFIELD(.tag =) 1, \
+ EURYDICE_CFIELD(.f0 =)(iter_ptr)->start++}))
+
+#define core_iter_range___core__iter__traits__iterator__Iterator_A__for_core__ops__range__Range_A__TraitClause_0___6__next \
+ Eurydice_range_iter_next
+
+// See note in karamel/lib/Inlining.ml if you change this
+#define Eurydice_into_iter(x, t, _ret_t, _) (x)
+#define core_iter_traits_collect___core__iter__traits__collect__IntoIterator_Clause1_Item__I__for_I__1__into_iter \
+ Eurydice_into_iter
+
+typedef struct {
+ Eurydice_slice s;
+ size_t index;
+} Eurydice_slice_iterator;
+
+#define core_slice___Slice_T___iter(x, t, _ret_t) \
+ ((Eurydice_slice_iterator){.s = x, .index = 0})
+#define core_slice_iter_Iter Eurydice_slice_iterator
+#define core_slice_iter__core__slice__iter__Iter__a__T__181__next(iter, t, \
+ ret_t) \
+ (((iter)->index == (iter)->s.len) \
+ ? (KRML_CLITERAL(ret_t){.tag = core_option_None}) \
+ : (KRML_CLITERAL(ret_t){ \
+ .tag = core_option_Some, \
+ .f0 = ((iter)->index++, \
+ &((t *)((iter)->s.ptr))[(iter)->index - 1])}))
+#define core_option__core__option__Option_T__TraitClause_0___is_some(X, _0, \
+ _1) \
+ ((X)->tag == 1)
+// STRINGS
+
+typedef const char *Prims_string;
+
+// MISC (UNTESTED)
+
+typedef void *core_fmt_Formatter;
+typedef void *core_fmt_Arguments;
+typedef void *core_fmt_rt_Argument;
+#define core_fmt_rt__core__fmt__rt__Argument__a__1__new_display(x1, x2, x3, \
+ x4) \
+ NULL
+
+// BOXES
+
+/* from libcrux/libcrux-ml-kem/extracts/c_header_only/generated/libcrux_mlkem_core.h */
+/*
+ * SPDX-FileCopyrightText: 2025 Cryspen Sarl
+ *
+ * SPDX-License-Identifier: MIT or Apache-2.0
+ *
+ * This code was generated with the following revisions:
+ * Charon: 667d2fc98984ff7f3df989c2367e6c1fa4a000e7
+ * Eurydice: 2381cbc416ef2ad0b561c362c500bc84f36b6785
+ * Karamel: 80f5435f2fc505973c469a4afcc8d875cddd0d8b
+ * F*: 71d8221589d4d438af3706d89cb653cf53e18aab
+ * Libcrux: 68dfed5a4a9e40277f62828471c029afed1ecdcc
+ */
+
+#ifndef libcrux_mlkem_core_H
+#define libcrux_mlkem_core_H
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+A monomorphic instance of core.ops.range.Range
+with types size_t
+
+*/
+typedef struct core_ops_range_Range_08_s {
+ size_t start;
+ size_t end;
+} core_ops_range_Range_08;
+
+static inline uint16_t core_num__u16__wrapping_add(uint16_t x0, uint16_t x1);
+
+static inline uint64_t core_num__u64__from_le_bytes(uint8_t x0[8U]);
+
+static inline uint64_t core_num__u64__rotate_left(uint64_t x0, uint32_t x1);
+
+static inline void core_num__u64__to_le_bytes(uint64_t x0, uint8_t x1[8U]);
+
+static inline uint8_t core_num__u8__wrapping_sub(uint8_t x0, uint8_t x1);
+
+#define LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE ((size_t)32U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_COEFFICIENT ((size_t)12U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT ((size_t)256U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_RING_ELEMENT \
+ (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT * (size_t)12U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT \
+ (LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_RING_ELEMENT / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_CPA_PKE_KEY_GENERATION_SEED_SIZE ((size_t)32U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_G_DIGEST_SIZE ((size_t)64U)
+
+#define LIBCRUX_ML_KEM_CONSTANTS_H_DIGEST_SIZE ((size_t)32U)
+
+/**
+ K * BITS_PER_RING_ELEMENT / 8
+
+ [eurydice] Note that we can't use const generics here because that breaks
+ C extraction with eurydice.
+*/
+static inline size_t libcrux_ml_kem_constants_ranked_bytes_per_ring_element(
+ size_t rank) {
+ return rank * LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_RING_ELEMENT / (size_t)8U;
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types uint8_t
+
+*/
+static KRML_MUSTINLINE uint8_t
+libcrux_secrets_int_public_integers_classify_27_90(uint8_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types int16_t
+
+*/
+static KRML_MUSTINLINE int16_t
+libcrux_secrets_int_public_integers_declassify_d8_39(int16_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for i16}
+*/
+static KRML_MUSTINLINE uint8_t libcrux_secrets_int_as_u8_f5(int16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_90(
+ (uint8_t)libcrux_secrets_int_public_integers_declassify_d8_39(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types int16_t
+
+*/
+static KRML_MUSTINLINE int16_t
+libcrux_secrets_int_public_integers_classify_27_39(int16_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint8_t
+
+*/
+static KRML_MUSTINLINE uint8_t
+libcrux_secrets_int_public_integers_declassify_d8_90(uint8_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u8}
+*/
+static KRML_MUSTINLINE int16_t libcrux_secrets_int_as_i16_59(uint8_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_39(
+ (int16_t)libcrux_secrets_int_public_integers_declassify_d8_90(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types int32_t
+
+*/
+static KRML_MUSTINLINE int32_t
+libcrux_secrets_int_public_integers_classify_27_a8(int32_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for i16}
+*/
+static KRML_MUSTINLINE int32_t libcrux_secrets_int_as_i32_f5(int16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_a8(
+ (int32_t)libcrux_secrets_int_public_integers_declassify_d8_39(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types int32_t
+
+*/
+static KRML_MUSTINLINE int32_t
+libcrux_secrets_int_public_integers_declassify_d8_a8(int32_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for i32}
+*/
+static KRML_MUSTINLINE int16_t libcrux_secrets_int_as_i16_36(int32_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_39(
+ (int16_t)libcrux_secrets_int_public_integers_declassify_d8_a8(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint32_t
+
+*/
+static KRML_MUSTINLINE uint32_t
+libcrux_secrets_int_public_integers_declassify_d8_df(uint32_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u32}
+*/
+static KRML_MUSTINLINE int32_t libcrux_secrets_int_as_i32_b8(uint32_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_a8(
+ (int32_t)libcrux_secrets_int_public_integers_declassify_d8_df(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types uint16_t
+
+*/
+static KRML_MUSTINLINE uint16_t
+libcrux_secrets_int_public_integers_classify_27_de(uint16_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for i16}
+*/
+static KRML_MUSTINLINE uint16_t libcrux_secrets_int_as_u16_f5(int16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_de(
+ (uint16_t)libcrux_secrets_int_public_integers_declassify_d8_39(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint16_t
+
+*/
+static KRML_MUSTINLINE uint16_t
+libcrux_secrets_int_public_integers_declassify_d8_de(uint16_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u16}
+*/
+static KRML_MUSTINLINE int16_t libcrux_secrets_int_as_i16_ca(uint16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_39(
+ (int16_t)libcrux_secrets_int_public_integers_declassify_d8_de(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types uint64_t
+
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_secrets_int_public_integers_classify_27_49(uint64_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u16}
+*/
+static KRML_MUSTINLINE uint64_t libcrux_secrets_int_as_u64_ca(uint16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_49(
+ (uint64_t)libcrux_secrets_int_public_integers_declassify_d8_de(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types uint32_t
+
+*/
+static KRML_MUSTINLINE uint32_t
+libcrux_secrets_int_public_integers_classify_27_df(uint32_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint64_t
+
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_secrets_int_public_integers_declassify_d8_49(uint64_t self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u64}
+*/
+static KRML_MUSTINLINE uint32_t libcrux_secrets_int_as_u32_a3(uint64_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_df(
+ (uint32_t)libcrux_secrets_int_public_integers_declassify_d8_49(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for u32}
+*/
+static KRML_MUSTINLINE int16_t libcrux_secrets_int_as_i16_b8(uint32_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_39(
+ (int16_t)libcrux_secrets_int_public_integers_declassify_d8_df(self));
+}
+
+/**
+This function found in impl {libcrux_secrets::int::CastOps for i16}
+*/
+static KRML_MUSTINLINE int16_t libcrux_secrets_int_as_i16_f5(int16_t self) {
+ return libcrux_secrets_int_public_integers_classify_27_39(
+ libcrux_secrets_int_public_integers_declassify_d8_39(self));
+}
+
+typedef struct libcrux_ml_kem_utils_extraction_helper_Keypair768_s {
+ uint8_t fst[1152U];
+ uint8_t snd[1184U];
+} libcrux_ml_kem_utils_extraction_helper_Keypair768;
+
+#define Ok 0
+#define Err 1
+
+typedef uint8_t Result_b2_tags;
+
+/**
+This function found in impl {core::convert::From<@Array> for
+libcrux_ml_kem::types::MlKemPublicKey}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.from_fd
+with const generics
+- SIZE= 1184
+*/
+static inline libcrux_ml_kem_types_MlKemPublicKey_30
+libcrux_ml_kem_types_from_fd_d0(uint8_t value[1184U]) {
+ /* Passing arrays by value in Rust generates a copy in C */
+ uint8_t copy_of_value[1184U];
+ memcpy(copy_of_value, value, (size_t)1184U * sizeof(uint8_t));
+ libcrux_ml_kem_types_MlKemPublicKey_30 lit;
+ memcpy(lit.value, copy_of_value, (size_t)1184U * sizeof(uint8_t));
+ return lit;
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::types::MlKemKeyPair}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.from_17
+with const generics
+- PRIVATE_KEY_SIZE= 2400
+- PUBLIC_KEY_SIZE= 1184
+*/
+static inline libcrux_ml_kem_mlkem768_MlKem768KeyPair
+libcrux_ml_kem_types_from_17_74(libcrux_ml_kem_types_MlKemPrivateKey_d9 sk,
+ libcrux_ml_kem_types_MlKemPublicKey_30 pk) {
+ return (KRML_CLITERAL(libcrux_ml_kem_mlkem768_MlKem768KeyPair){.sk = sk,
+ .pk = pk});
+}
+
+/**
+This function found in impl {core::convert::From<@Array> for
+libcrux_ml_kem::types::MlKemPrivateKey}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.from_77
+with const generics
+- SIZE= 2400
+*/
+static inline libcrux_ml_kem_types_MlKemPrivateKey_d9
+libcrux_ml_kem_types_from_77_28(uint8_t value[2400U]) {
+ /* Passing arrays by value in Rust generates a copy in C */
+ uint8_t copy_of_value[2400U];
+ memcpy(copy_of_value, value, (size_t)2400U * sizeof(uint8_t));
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 lit;
+ memcpy(lit.value, copy_of_value, (size_t)2400U * sizeof(uint8_t));
+ return lit;
+}
+
+/**
+A monomorphic instance of core.result.Result
+with types uint8_t[32size_t], core_array_TryFromSliceError
+
+*/
+typedef struct Result_fb_s {
+ Result_b2_tags tag;
+ union {
+ uint8_t case_Ok[32U];
+ TryFromSliceError case_Err;
+ } val;
+} Result_fb;
+
+/**
+This function found in impl {core::result::Result[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of core.result.unwrap_26
+with types uint8_t[32size_t], core_array_TryFromSliceError
+
+*/
+static inline void unwrap_26_b3(Result_fb self, uint8_t ret[32U]) {
+ if (self.tag == Ok) {
+ uint8_t f0[32U];
+ memcpy(f0, self.val.case_Ok, (size_t)32U * sizeof(uint8_t));
+ memcpy(ret, f0, (size_t)32U * sizeof(uint8_t));
+ } else {
+ KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__,
+ "unwrap not Ok");
+ KRML_HOST_EXIT(255U);
+ }
+}
+
+/**
+This function found in impl {core::convert::From<@Array> for
+libcrux_ml_kem::types::MlKemCiphertext}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.from_e0
+with const generics
+- SIZE= 1088
+*/
+static inline libcrux_ml_kem_mlkem768_MlKem768Ciphertext
+libcrux_ml_kem_types_from_e0_80(uint8_t value[1088U]) {
+ /* Passing arrays by value in Rust generates a copy in C */
+ uint8_t copy_of_value[1088U];
+ memcpy(copy_of_value, value, (size_t)1088U * sizeof(uint8_t));
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext lit;
+ memcpy(lit.value, copy_of_value, (size_t)1088U * sizeof(uint8_t));
+ return lit;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::types::MlKemPublicKey}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.as_slice_e6
+with const generics
+- SIZE= 1184
+*/
+static inline uint8_t *libcrux_ml_kem_types_as_slice_e6_d0(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *self) {
+ return self->value;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::types::MlKemCiphertext}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.as_slice_a9
+with const generics
+- SIZE= 1088
+*/
+static inline uint8_t *libcrux_ml_kem_types_as_slice_a9_80(
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *self) {
+ return self->value;
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.utils.prf_input_inc
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE uint8_t libcrux_ml_kem_utils_prf_input_inc_e0(
+ uint8_t (*prf_inputs)[33U], uint8_t domain_separator) {
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ prf_inputs[i0][32U] = domain_separator;
+ domain_separator = (uint32_t)domain_separator + 1U;
+ }
+ return domain_separator;
+}
+
+/**
+ Pad the `slice` with `0`s at the end.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.utils.into_padded_array
+with const generics
+- LEN= 33
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_utils_into_padded_array_c8(
+ Eurydice_slice slice, uint8_t ret[33U]) {
+ uint8_t out[33U] = {0U};
+ uint8_t *uu____0 = out;
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(
+ uu____0, (size_t)0U, Eurydice_slice_len(slice, uint8_t), uint8_t *),
+ slice, uint8_t);
+ memcpy(ret, out, (size_t)33U * sizeof(uint8_t));
+}
+
+/**
+ Pad the `slice` with `0`s at the end.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.utils.into_padded_array
+with const generics
+- LEN= 34
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_utils_into_padded_array_b6(
+ Eurydice_slice slice, uint8_t ret[34U]) {
+ uint8_t out[34U] = {0U};
+ uint8_t *uu____0 = out;
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(
+ uu____0, (size_t)0U, Eurydice_slice_len(slice, uint8_t), uint8_t *),
+ slice, uint8_t);
+ memcpy(ret, out, (size_t)34U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {core::convert::AsRef<@Slice> for
+libcrux_ml_kem::types::MlKemCiphertext}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.as_ref_d3
+with const generics
+- SIZE= 1088
+*/
+static inline Eurydice_slice libcrux_ml_kem_types_as_ref_d3_80(
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *self) {
+ return Eurydice_array_to_slice((size_t)1088U, self->value, uint8_t);
+}
+
+/**
+ Pad the `slice` with `0`s at the end.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.utils.into_padded_array
+with const generics
+- LEN= 1120
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_utils_into_padded_array_15(
+ Eurydice_slice slice, uint8_t ret[1120U]) {
+ uint8_t out[1120U] = {0U};
+ uint8_t *uu____0 = out;
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(
+ uu____0, (size_t)0U, Eurydice_slice_len(slice, uint8_t), uint8_t *),
+ slice, uint8_t);
+ memcpy(ret, out, (size_t)1120U * sizeof(uint8_t));
+}
+
+/**
+ Pad the `slice` with `0`s at the end.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.utils.into_padded_array
+with const generics
+- LEN= 64
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_utils_into_padded_array_24(
+ Eurydice_slice slice, uint8_t ret[64U]) {
+ uint8_t out[64U] = {0U};
+ uint8_t *uu____0 = out;
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(
+ uu____0, (size_t)0U, Eurydice_slice_len(slice, uint8_t), uint8_t *),
+ slice, uint8_t);
+ memcpy(ret, out, (size_t)64U * sizeof(uint8_t));
+}
+
+typedef struct Eurydice_slice_uint8_t_x4_s {
+ Eurydice_slice fst;
+ Eurydice_slice snd;
+ Eurydice_slice thd;
+ Eurydice_slice f3;
+} Eurydice_slice_uint8_t_x4;
+
+typedef struct Eurydice_slice_uint8_t_x2_s {
+ Eurydice_slice fst;
+ Eurydice_slice snd;
+} Eurydice_slice_uint8_t_x2;
+
+/**
+ Unpack an incoming private key into it's different parts.
+
+ We have this here in types to extract into a common core for C.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.types.unpack_private_key
+with const generics
+- CPA_SECRET_KEY_SIZE= 1152
+- PUBLIC_KEY_SIZE= 1184
+*/
+static inline Eurydice_slice_uint8_t_x4
+libcrux_ml_kem_types_unpack_private_key_b4(Eurydice_slice private_key) {
+ Eurydice_slice_uint8_t_x2 uu____0 = Eurydice_slice_split_at(
+ private_key, (size_t)1152U, uint8_t, Eurydice_slice_uint8_t_x2);
+ Eurydice_slice ind_cpa_secret_key = uu____0.fst;
+ Eurydice_slice secret_key0 = uu____0.snd;
+ Eurydice_slice_uint8_t_x2 uu____1 = Eurydice_slice_split_at(
+ secret_key0, (size_t)1184U, uint8_t, Eurydice_slice_uint8_t_x2);
+ Eurydice_slice ind_cpa_public_key = uu____1.fst;
+ Eurydice_slice secret_key = uu____1.snd;
+ Eurydice_slice_uint8_t_x2 uu____2 = Eurydice_slice_split_at(
+ secret_key, LIBCRUX_ML_KEM_CONSTANTS_H_DIGEST_SIZE, uint8_t,
+ Eurydice_slice_uint8_t_x2);
+ Eurydice_slice ind_cpa_public_key_hash = uu____2.fst;
+ Eurydice_slice implicit_rejection_value = uu____2.snd;
+ return (
+ KRML_CLITERAL(Eurydice_slice_uint8_t_x4){.fst = ind_cpa_secret_key,
+ .snd = ind_cpa_public_key,
+ .thd = ind_cpa_public_key_hash,
+ .f3 = implicit_rejection_value});
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint8_t[24size_t]
+
+*/
+static KRML_MUSTINLINE void
+libcrux_secrets_int_public_integers_declassify_d8_d2(uint8_t self[24U],
+ uint8_t ret[24U]) {
+ memcpy(ret, self, (size_t)24U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint8_t[20size_t]
+
+*/
+static KRML_MUSTINLINE void
+libcrux_secrets_int_public_integers_declassify_d8_57(uint8_t self[20U],
+ uint8_t ret[20U]) {
+ memcpy(ret, self, (size_t)20U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint8_t[8size_t]
+
+*/
+static KRML_MUSTINLINE void
+libcrux_secrets_int_public_integers_declassify_d8_76(uint8_t self[8U],
+ uint8_t ret[8U]) {
+ memcpy(ret, self, (size_t)8U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Declassify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.declassify_d8
+with types uint8_t[2size_t]
+
+*/
+static KRML_MUSTINLINE void
+libcrux_secrets_int_public_integers_declassify_d8_d4(uint8_t self[2U],
+ uint8_t ret[2U]) {
+ memcpy(ret, self, (size_t)2U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::Classify for T}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.public_integers.classify_27
+with types int16_t[16size_t]
+
+*/
+static KRML_MUSTINLINE void libcrux_secrets_int_public_integers_classify_27_46(
+ int16_t self[16U], int16_t ret[16U]) {
+ memcpy(ret, self, (size_t)16U * sizeof(int16_t));
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::ClassifyRef<&'a
+(@Slice)> for &'a (@Slice)}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.classify_public.classify_ref_9b
+with types uint8_t
+
+*/
+static KRML_MUSTINLINE Eurydice_slice
+libcrux_secrets_int_classify_public_classify_ref_9b_90(Eurydice_slice self) {
+ return self;
+}
+
+/**
+This function found in impl {libcrux_secrets::traits::ClassifyRef<&'a
+(@Slice)> for &'a (@Slice)}
+*/
+/**
+A monomorphic instance of libcrux_secrets.int.classify_public.classify_ref_9b
+with types int16_t
+
+*/
+static KRML_MUSTINLINE Eurydice_slice
+libcrux_secrets_int_classify_public_classify_ref_9b_39(Eurydice_slice self) {
+ return self;
+}
+
+/**
+A monomorphic instance of core.result.Result
+with types int16_t[16size_t], core_array_TryFromSliceError
+
+*/
+typedef struct Result_0a_s {
+ Result_b2_tags tag;
+ union {
+ int16_t case_Ok[16U];
+ TryFromSliceError case_Err;
+ } val;
+} Result_0a;
+
+/**
+This function found in impl {core::result::Result[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of core.result.unwrap_26
+with types int16_t[16size_t], core_array_TryFromSliceError
+
+*/
+static inline void unwrap_26_00(Result_0a self, int16_t ret[16U]) {
+ if (self.tag == Ok) {
+ int16_t f0[16U];
+ memcpy(f0, self.val.case_Ok, (size_t)16U * sizeof(int16_t));
+ memcpy(ret, f0, (size_t)16U * sizeof(int16_t));
+ } else {
+ KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__,
+ "unwrap not Ok");
+ KRML_HOST_EXIT(255U);
+ }
+}
+
+/**
+A monomorphic instance of core.result.Result
+with types uint8_t[8size_t], core_array_TryFromSliceError
+
+*/
+typedef struct Result_15_s {
+ Result_b2_tags tag;
+ union {
+ uint8_t case_Ok[8U];
+ TryFromSliceError case_Err;
+ } val;
+} Result_15;
+
+/**
+This function found in impl {core::result::Result[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of core.result.unwrap_26
+with types uint8_t[8size_t], core_array_TryFromSliceError
+
+*/
+static inline void unwrap_26_68(Result_15 self, uint8_t ret[8U]) {
+ if (self.tag == Ok) {
+ uint8_t f0[8U];
+ memcpy(f0, self.val.case_Ok, (size_t)8U * sizeof(uint8_t));
+ memcpy(ret, f0, (size_t)8U * sizeof(uint8_t));
+ } else {
+ KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__,
+ "unwrap not Ok");
+ KRML_HOST_EXIT(255U);
+ }
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#define libcrux_mlkem_core_H_DEFINED
+#endif /* libcrux_mlkem_core_H */
+
+/* from libcrux/libcrux-ml-kem/extracts/c_header_only/generated/libcrux_ct_ops.h */
+/*
+ * SPDX-FileCopyrightText: 2025 Cryspen Sarl
+ *
+ * SPDX-License-Identifier: MIT or Apache-2.0
+ *
+ * This code was generated with the following revisions:
+ * Charon: 667d2fc98984ff7f3df989c2367e6c1fa4a000e7
+ * Eurydice: 2381cbc416ef2ad0b561c362c500bc84f36b6785
+ * Karamel: 80f5435f2fc505973c469a4afcc8d875cddd0d8b
+ * F*: 71d8221589d4d438af3706d89cb653cf53e18aab
+ * Libcrux: 68dfed5a4a9e40277f62828471c029afed1ecdcc
+ */
+
+#ifndef libcrux_ct_ops_H
+#define libcrux_ct_ops_H
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/**
+ Return 1 if `value` is not zero and 0 otherwise.
+*/
+static KRML_NOINLINE uint8_t
+libcrux_ml_kem_constant_time_ops_inz(uint8_t value) {
+ uint16_t value0 = (uint16_t)value;
+ uint8_t result =
+ (uint8_t)((uint32_t)core_num__u16__wrapping_add(~value0, 1U) >> 8U);
+ return (uint32_t)result & 1U;
+}
+
+static KRML_NOINLINE uint8_t
+libcrux_ml_kem_constant_time_ops_is_non_zero(uint8_t value) {
+ return libcrux_ml_kem_constant_time_ops_inz(value);
+}
+
+/**
+ Return 1 if the bytes of `lhs` and `rhs` do not exactly
+ match and 0 otherwise.
+*/
+static KRML_NOINLINE uint8_t libcrux_ml_kem_constant_time_ops_compare(
+ Eurydice_slice lhs, Eurydice_slice rhs) {
+ uint8_t r = 0U;
+ for (size_t i = (size_t)0U; i < Eurydice_slice_len(lhs, uint8_t); i++) {
+ size_t i0 = i;
+ uint8_t nr = (uint32_t)r |
+ ((uint32_t)Eurydice_slice_index(lhs, i0, uint8_t, uint8_t *) ^
+ (uint32_t)Eurydice_slice_index(rhs, i0, uint8_t, uint8_t *));
+ r = nr;
+ }
+ return libcrux_ml_kem_constant_time_ops_is_non_zero(r);
+}
+
+static KRML_NOINLINE uint8_t
+libcrux_ml_kem_constant_time_ops_compare_ciphertexts_in_constant_time(
+ Eurydice_slice lhs, Eurydice_slice rhs) {
+ return libcrux_ml_kem_constant_time_ops_compare(lhs, rhs);
+}
+
+/**
+ If `selector` is not zero, return the bytes in `rhs`; return the bytes in
+ `lhs` otherwise.
+*/
+static KRML_NOINLINE void libcrux_ml_kem_constant_time_ops_select_ct(
+ Eurydice_slice lhs, Eurydice_slice rhs, uint8_t selector,
+ uint8_t ret[32U]) {
+ uint8_t mask = core_num__u8__wrapping_sub(
+ libcrux_ml_kem_constant_time_ops_is_non_zero(selector), 1U);
+ uint8_t out[32U] = {0U};
+ for (size_t i = (size_t)0U; i < LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE;
+ i++) {
+ size_t i0 = i;
+ uint8_t outi =
+ ((uint32_t)Eurydice_slice_index(lhs, i0, uint8_t, uint8_t *) &
+ (uint32_t)mask) |
+ ((uint32_t)Eurydice_slice_index(rhs, i0, uint8_t, uint8_t *) &
+ (uint32_t)~mask);
+ out[i0] = outi;
+ }
+ memcpy(ret, out, (size_t)32U * sizeof(uint8_t));
+}
+
+static KRML_NOINLINE void
+libcrux_ml_kem_constant_time_ops_select_shared_secret_in_constant_time(
+ Eurydice_slice lhs, Eurydice_slice rhs, uint8_t selector,
+ uint8_t ret[32U]) {
+ libcrux_ml_kem_constant_time_ops_select_ct(lhs, rhs, selector, ret);
+}
+
+static KRML_NOINLINE void
+libcrux_ml_kem_constant_time_ops_compare_ciphertexts_select_shared_secret_in_constant_time(
+ Eurydice_slice lhs_c, Eurydice_slice rhs_c, Eurydice_slice lhs_s,
+ Eurydice_slice rhs_s, uint8_t ret[32U]) {
+ uint8_t selector =
+ libcrux_ml_kem_constant_time_ops_compare_ciphertexts_in_constant_time(
+ lhs_c, rhs_c);
+ uint8_t ret0[32U];
+ libcrux_ml_kem_constant_time_ops_select_shared_secret_in_constant_time(
+ lhs_s, rhs_s, selector, ret0);
+ memcpy(ret, ret0, (size_t)32U * sizeof(uint8_t));
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#define libcrux_ct_ops_H_DEFINED
+#endif /* libcrux_ct_ops_H */
+
+/* from libcrux/libcrux-ml-kem/extracts/c_header_only/generated/libcrux_sha3_portable.h */
+/*
+ * SPDX-FileCopyrightText: 2025 Cryspen Sarl
+ *
+ * SPDX-License-Identifier: MIT or Apache-2.0
+ *
+ * This code was generated with the following revisions:
+ * Charon: 667d2fc98984ff7f3df989c2367e6c1fa4a000e7
+ * Eurydice: 2381cbc416ef2ad0b561c362c500bc84f36b6785
+ * Karamel: 80f5435f2fc505973c469a4afcc8d875cddd0d8b
+ * F*: 71d8221589d4d438af3706d89cb653cf53e18aab
+ * Libcrux: 68dfed5a4a9e40277f62828471c029afed1ecdcc
+ */
+
+#ifndef libcrux_sha3_portable_H
+#define libcrux_sha3_portable_H
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t libcrux_sha3_simd_portable_zero_d2(void) {
+ return 0ULL;
+}
+
+static KRML_MUSTINLINE uint64_t libcrux_sha3_simd_portable__veor5q_u64(
+ uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) {
+ return (((a ^ b) ^ c) ^ d) ^ e;
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t libcrux_sha3_simd_portable_xor5_d2(
+ uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) {
+ return libcrux_sha3_simd_portable__veor5q_u64(a, b, c, d, e);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 1
+- RIGHT= 63
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_76(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)1);
+}
+
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vrax1q_u64(uint64_t a, uint64_t b) {
+ uint64_t uu____0 = a;
+ return uu____0 ^ libcrux_sha3_simd_portable_rotate_left_76(b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vrax1q_u64(a, b);
+}
+
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vbcaxq_u64(uint64_t a, uint64_t b, uint64_t c) {
+ return a ^ (b & ~c);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_and_not_xor_d2(uint64_t a, uint64_t b, uint64_t c) {
+ return libcrux_sha3_simd_portable__vbcaxq_u64(a, b, c);
+}
+
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__veorq_n_u64(uint64_t a, uint64_t c) {
+ return a ^ c;
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_constant_d2(uint64_t a, uint64_t c) {
+ return libcrux_sha3_simd_portable__veorq_n_u64(a, c);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+static KRML_MUSTINLINE uint64_t libcrux_sha3_simd_portable_xor_d2(uint64_t a,
+ uint64_t b) {
+ return a ^ b;
+}
+
+static const uint64_t
+ libcrux_sha3_generic_keccak_constants_ROUNDCONSTANTS[24U] = {
+ 1ULL,
+ 32898ULL,
+ 9223372036854808714ULL,
+ 9223372039002292224ULL,
+ 32907ULL,
+ 2147483649ULL,
+ 9223372039002292353ULL,
+ 9223372036854808585ULL,
+ 138ULL,
+ 136ULL,
+ 2147516425ULL,
+ 2147483658ULL,
+ 2147516555ULL,
+ 9223372036854775947ULL,
+ 9223372036854808713ULL,
+ 9223372036854808579ULL,
+ 9223372036854808578ULL,
+ 9223372036854775936ULL,
+ 32778ULL,
+ 9223372039002259466ULL,
+ 9223372039002292353ULL,
+ 9223372036854808704ULL,
+ 2147483649ULL,
+ 9223372039002292232ULL};
+
+typedef struct size_t_x2_s {
+ size_t fst;
+ size_t snd;
+} size_t_x2;
+
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.KeccakState
+with types uint64_t
+with const generics
+- $1size_t
+*/
+typedef struct libcrux_sha3_generic_keccak_KeccakState_17_s {
+ uint64_t st[25U];
+} libcrux_sha3_generic_keccak_KeccakState_17;
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.new_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE libcrux_sha3_generic_keccak_KeccakState_17
+libcrux_sha3_generic_keccak_new_80_04(void) {
+ libcrux_sha3_generic_keccak_KeccakState_17 lit;
+ uint64_t repeat_expression[25U];
+ for (size_t i = (size_t)0U; i < (size_t)25U; i++) {
+ repeat_expression[i] = libcrux_sha3_simd_portable_zero_d2();
+ }
+ memcpy(lit.st, repeat_expression, (size_t)25U * sizeof(uint64_t));
+ return lit;
+}
+
+/**
+A monomorphic instance of libcrux_sha3.traits.get_ij
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE uint64_t *libcrux_sha3_traits_get_ij_04(uint64_t *arr,
+ size_t i,
+ size_t j) {
+ return &arr[(size_t)5U * j + i];
+}
+
+/**
+A monomorphic instance of libcrux_sha3.traits.set_ij
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_traits_set_ij_04(uint64_t *arr,
+ size_t i, size_t j,
+ uint64_t value) {
+ arr[(size_t)5U * j + i] = value;
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_block
+with const generics
+- RATE= 72
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_block_f8(
+ uint64_t *state, Eurydice_slice blocks, size_t start) {
+ uint64_t state_flat[25U] = {0U};
+ for (size_t i = (size_t)0U; i < (size_t)72U / (size_t)8U; i++) {
+ size_t i0 = i;
+ size_t offset = start + (size_t)8U * i0;
+ uint8_t uu____0[8U];
+ Result_15 dst;
+ Eurydice_slice_to_array2(
+ &dst,
+ Eurydice_slice_subslice3(blocks, offset, offset + (size_t)8U,
+ uint8_t *),
+ Eurydice_slice, uint8_t[8U], TryFromSliceError);
+ unwrap_26_68(dst, uu____0);
+ state_flat[i0] = core_num__u64__from_le_bytes(uu____0);
+ }
+ for (size_t i = (size_t)0U; i < (size_t)72U / (size_t)8U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_traits_set_ij_04(
+ state, i0 / (size_t)5U, i0 % (size_t)5U,
+ libcrux_sha3_traits_get_ij_04(state, i0 / (size_t)5U,
+ i0 % (size_t)5U)[0U] ^
+ state_flat[i0]);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_block_a1
+with const generics
+- RATE= 72
+*/
+static inline void libcrux_sha3_simd_portable_load_block_a1_f8(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start) {
+ libcrux_sha3_simd_portable_load_block_f8(self->st, input[0U], start);
+}
+
+/**
+This function found in impl {core::ops::index::Index<(usize, usize), T> for
+libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.index_c2
+with types uint64_t
+with const generics
+- N= 1
+*/
+static inline uint64_t *libcrux_sha3_generic_keccak_index_c2_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, size_t_x2 index) {
+ return libcrux_sha3_traits_get_ij_04(self->st, index.fst, index.snd);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.theta_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_theta_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, uint64_t ret[5U]) {
+ uint64_t c[5U] = {
+ libcrux_sha3_simd_portable_xor5_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)0U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)0U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)0U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)0U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)0U}))[0U]),
+ libcrux_sha3_simd_portable_xor5_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)1U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)1U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)1U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)1U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)1U}))[0U]),
+ libcrux_sha3_simd_portable_xor5_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)2U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)2U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)2U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)2U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)2U}))[0U]),
+ libcrux_sha3_simd_portable_xor5_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)3U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)3U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)3U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)3U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)3U}))[0U]),
+ libcrux_sha3_simd_portable_xor5_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)4U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)4U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)4U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)4U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)4U}))[0U])};
+ uint64_t uu____0 = libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(
+ c[((size_t)0U + (size_t)4U) % (size_t)5U],
+ c[((size_t)0U + (size_t)1U) % (size_t)5U]);
+ uint64_t uu____1 = libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(
+ c[((size_t)1U + (size_t)4U) % (size_t)5U],
+ c[((size_t)1U + (size_t)1U) % (size_t)5U]);
+ uint64_t uu____2 = libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(
+ c[((size_t)2U + (size_t)4U) % (size_t)5U],
+ c[((size_t)2U + (size_t)1U) % (size_t)5U]);
+ uint64_t uu____3 = libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(
+ c[((size_t)3U + (size_t)4U) % (size_t)5U],
+ c[((size_t)3U + (size_t)1U) % (size_t)5U]);
+ ret[0U] = uu____0;
+ ret[1U] = uu____1;
+ ret[2U] = uu____2;
+ ret[3U] = uu____3;
+ ret[4U] = libcrux_sha3_simd_portable_rotate_left1_and_xor_d2(
+ c[((size_t)4U + (size_t)4U) % (size_t)5U],
+ c[((size_t)4U + (size_t)1U) % (size_t)5U]);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.set_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static inline void libcrux_sha3_generic_keccak_set_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, size_t i, size_t j,
+ uint64_t v) {
+ libcrux_sha3_traits_set_ij_04(self->st, i, j, v);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 36
+- RIGHT= 28
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_02(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)36);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 36
+- RIGHT= 28
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_02(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_02(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 36
+- RIGHT= 28
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_02(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_02(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 3
+- RIGHT= 61
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_ac(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)3);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 3
+- RIGHT= 61
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_ac(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_ac(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 3
+- RIGHT= 61
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_ac(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_ac(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 41
+- RIGHT= 23
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_020(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)41);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 41
+- RIGHT= 23
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_020(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_020(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 41
+- RIGHT= 23
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_020(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_020(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 18
+- RIGHT= 46
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_a9(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)18);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 18
+- RIGHT= 46
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_a9(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_a9(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 18
+- RIGHT= 46
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_a9(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_a9(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 1
+- RIGHT= 63
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_76(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_76(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 1
+- RIGHT= 63
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_76(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_76(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 44
+- RIGHT= 20
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_58(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)44);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 44
+- RIGHT= 20
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_58(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_58(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 44
+- RIGHT= 20
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_58(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_58(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 10
+- RIGHT= 54
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_e0(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)10);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 10
+- RIGHT= 54
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_e0(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_e0(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 10
+- RIGHT= 54
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_e0(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_e0(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 45
+- RIGHT= 19
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_63(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)45);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 45
+- RIGHT= 19
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_63(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_63(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 45
+- RIGHT= 19
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_63(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_63(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 2
+- RIGHT= 62
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_6a(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)2);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 2
+- RIGHT= 62
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_6a(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_6a(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 2
+- RIGHT= 62
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_6a(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_6a(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 62
+- RIGHT= 2
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_ab(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)62);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 62
+- RIGHT= 2
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_ab(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_ab(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 62
+- RIGHT= 2
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_ab(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_ab(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 6
+- RIGHT= 58
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_5b(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)6);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 6
+- RIGHT= 58
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_5b(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_5b(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 6
+- RIGHT= 58
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_5b(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_5b(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 43
+- RIGHT= 21
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_6f(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)43);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 43
+- RIGHT= 21
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_6f(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_6f(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 43
+- RIGHT= 21
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_6f(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_6f(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 15
+- RIGHT= 49
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_62(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)15);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 15
+- RIGHT= 49
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_62(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_62(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 15
+- RIGHT= 49
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_62(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_62(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 61
+- RIGHT= 3
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_23(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)61);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 61
+- RIGHT= 3
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_23(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_23(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 61
+- RIGHT= 3
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_23(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_23(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 28
+- RIGHT= 36
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_37(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)28);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 28
+- RIGHT= 36
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_37(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_37(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 28
+- RIGHT= 36
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_37(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_37(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 55
+- RIGHT= 9
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_bb(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)55);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 55
+- RIGHT= 9
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_bb(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_bb(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 55
+- RIGHT= 9
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_bb(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_bb(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 25
+- RIGHT= 39
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_b9(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)25);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 25
+- RIGHT= 39
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_b9(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_b9(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 25
+- RIGHT= 39
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_b9(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_b9(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 21
+- RIGHT= 43
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_54(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)21);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 21
+- RIGHT= 43
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_54(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_54(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 21
+- RIGHT= 43
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_54(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_54(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 56
+- RIGHT= 8
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_4c(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)56);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 56
+- RIGHT= 8
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_4c(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_4c(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 56
+- RIGHT= 8
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_4c(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_4c(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 27
+- RIGHT= 37
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_ce(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)27);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 27
+- RIGHT= 37
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_ce(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_ce(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 27
+- RIGHT= 37
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_ce(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_ce(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 20
+- RIGHT= 44
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_77(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)20);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 20
+- RIGHT= 44
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_77(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_77(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 20
+- RIGHT= 44
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_77(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_77(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 39
+- RIGHT= 25
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_25(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)39);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 39
+- RIGHT= 25
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_25(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_25(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 39
+- RIGHT= 25
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_25(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_25(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 8
+- RIGHT= 56
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_af(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)8);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 8
+- RIGHT= 56
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_af(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_af(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 8
+- RIGHT= 56
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_af(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_af(a, b);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.rotate_left
+with const generics
+- LEFT= 14
+- RIGHT= 50
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_rotate_left_fd(uint64_t x) {
+ return core_num__u64__rotate_left(x, (uint32_t)(int32_t)14);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable._vxarq_u64
+with const generics
+- LEFT= 14
+- RIGHT= 50
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable__vxarq_u64_fd(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable_rotate_left_fd(a ^ b);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::KeccakItem<1usize> for u64}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.xor_and_rotate_d2
+with const generics
+- LEFT= 14
+- RIGHT= 50
+*/
+static KRML_MUSTINLINE uint64_t
+libcrux_sha3_simd_portable_xor_and_rotate_d2_fd(uint64_t a, uint64_t b) {
+ return libcrux_sha3_simd_portable__vxarq_u64_fd(a, b);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.rho_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_rho_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, uint64_t t[5U]) {
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)0U}))[0U],
+ t[0U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____0 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____0, (size_t)1U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_02(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)0U}))[0U],
+ t[0U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____1 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____1, (size_t)2U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_ac(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)0U}))[0U],
+ t[0U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____2 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____2, (size_t)3U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_020(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)0U}))[0U],
+ t[0U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____3 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____3, (size_t)4U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_a9(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)0U}))[0U],
+ t[0U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____4 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____4, (size_t)0U, (size_t)1U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_76(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)1U}))[0U],
+ t[1U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____5 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____5, (size_t)1U, (size_t)1U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_58(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)1U}))[0U],
+ t[1U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____6 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____6, (size_t)2U, (size_t)1U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_e0(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)1U}))[0U],
+ t[1U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____7 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____7, (size_t)3U, (size_t)1U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_63(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)1U}))[0U],
+ t[1U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____8 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____8, (size_t)4U, (size_t)1U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_6a(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)1U}))[0U],
+ t[1U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____9 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____9, (size_t)0U, (size_t)2U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_ab(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)2U}))[0U],
+ t[2U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____10 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____10, (size_t)1U, (size_t)2U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_5b(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)2U}))[0U],
+ t[2U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____11 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____11, (size_t)2U, (size_t)2U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_6f(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)2U}))[0U],
+ t[2U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____12 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____12, (size_t)3U, (size_t)2U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_62(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)2U}))[0U],
+ t[2U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____13 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____13, (size_t)4U, (size_t)2U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_23(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)2U}))[0U],
+ t[2U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____14 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____14, (size_t)0U, (size_t)3U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_37(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)3U}))[0U],
+ t[3U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____15 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____15, (size_t)1U, (size_t)3U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_bb(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)3U}))[0U],
+ t[3U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____16 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____16, (size_t)2U, (size_t)3U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_b9(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)3U}))[0U],
+ t[3U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____17 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____17, (size_t)3U, (size_t)3U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_54(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)3U}))[0U],
+ t[3U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____18 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____18, (size_t)4U, (size_t)3U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_4c(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)3U}))[0U],
+ t[3U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____19 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____19, (size_t)0U, (size_t)4U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_ce(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)4U}))[0U],
+ t[4U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____20 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____20, (size_t)1U, (size_t)4U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_77(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)4U}))[0U],
+ t[4U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____21 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____21, (size_t)2U, (size_t)4U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_25(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)4U}))[0U],
+ t[4U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____22 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____22, (size_t)3U, (size_t)4U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_af(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)4U}))[0U],
+ t[4U]));
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____23 = self;
+ libcrux_sha3_generic_keccak_set_80_04(
+ uu____23, (size_t)4U, (size_t)4U,
+ libcrux_sha3_simd_portable_xor_and_rotate_d2_fd(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)4U}))[0U],
+ t[4U]));
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.pi_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_pi_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self) {
+ libcrux_sha3_generic_keccak_KeccakState_17 old = self[0U];
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)1U, (size_t)0U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)3U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)2U, (size_t)0U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)1U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)3U, (size_t)0U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)4U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)4U, (size_t)0U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)2U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)1U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)1U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)1U, (size_t)1U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)4U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)2U, (size_t)1U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)2U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)3U, (size_t)1U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)0U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)4U, (size_t)1U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)1U,
+ .snd = (size_t)3U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)2U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)2U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)1U, (size_t)2U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)0U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)2U, (size_t)2U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)3U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)3U, (size_t)2U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)1U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)4U, (size_t)2U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)2U,
+ .snd = (size_t)4U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)3U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)3U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)1U, (size_t)3U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)1U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)2U, (size_t)3U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)4U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)3U, (size_t)3U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)2U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)4U, (size_t)3U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)3U,
+ .snd = (size_t)0U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)4U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)4U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)1U, (size_t)4U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)2U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)2U, (size_t)4U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)0U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)3U, (size_t)4U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)3U}))[0U]);
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)4U, (size_t)4U,
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old, (KRML_CLITERAL(size_t_x2){.fst = (size_t)4U,
+ .snd = (size_t)1U}))[0U]);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.chi_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_chi_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self) {
+ libcrux_sha3_generic_keccak_KeccakState_17 old = self[0U];
+ for (size_t i0 = (size_t)0U; i0 < (size_t)5U; i0++) {
+ size_t i1 = i0;
+ for (size_t i = (size_t)0U; i < (size_t)5U; i++) {
+ size_t j = i;
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, i1, j,
+ libcrux_sha3_simd_portable_and_not_xor_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = i1, .snd = j}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old,
+ (KRML_CLITERAL(size_t_x2){
+ .fst = i1, .snd = (j + (size_t)2U) % (size_t)5U}))[0U],
+ libcrux_sha3_generic_keccak_index_c2_04(
+ &old,
+ (KRML_CLITERAL(size_t_x2){
+ .fst = i1, .snd = (j + (size_t)1U) % (size_t)5U}))[0U]));
+ }
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.iota_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_iota_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, size_t i) {
+ libcrux_sha3_generic_keccak_set_80_04(
+ self, (size_t)0U, (size_t)0U,
+ libcrux_sha3_simd_portable_xor_constant_d2(
+ libcrux_sha3_generic_keccak_index_c2_04(
+ self, (KRML_CLITERAL(size_t_x2){.fst = (size_t)0U,
+ .snd = (size_t)0U}))[0U],
+ libcrux_sha3_generic_keccak_constants_ROUNDCONSTANTS[i]));
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.keccakf1600_80
+with types uint64_t
+with const generics
+- N= 1
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_keccakf1600_80_04(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self) {
+ for (size_t i = (size_t)0U; i < (size_t)24U; i++) {
+ size_t i0 = i;
+ uint64_t t[5U];
+ libcrux_sha3_generic_keccak_theta_80_04(self, t);
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____0 = self;
+ uint64_t uu____1[5U];
+ memcpy(uu____1, t, (size_t)5U * sizeof(uint64_t));
+ libcrux_sha3_generic_keccak_rho_80_04(uu____0, uu____1);
+ libcrux_sha3_generic_keccak_pi_80_04(self);
+ libcrux_sha3_generic_keccak_chi_80_04(self);
+ libcrux_sha3_generic_keccak_iota_80_04(self, i0);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_block_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 72
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_block_80_c6(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *blocks,
+ size_t start) {
+ libcrux_sha3_simd_portable_load_block_a1_f8(self, blocks, start);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last
+with const generics
+- RATE= 72
+- DELIMITER= 6
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_last_96(
+ uint64_t *state, Eurydice_slice blocks, size_t start, size_t len) {
+ uint8_t buffer[72U] = {0U};
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(buffer, (size_t)0U, len, uint8_t *),
+ Eurydice_slice_subslice3(blocks, start, start + len, uint8_t *), uint8_t);
+ buffer[len] = 6U;
+ size_t uu____0 = (size_t)72U - (size_t)1U;
+ buffer[uu____0] = (uint32_t)buffer[uu____0] | 128U;
+ libcrux_sha3_simd_portable_load_block_f8(
+ state, Eurydice_array_to_slice((size_t)72U, buffer, uint8_t), (size_t)0U);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last_a1
+with const generics
+- RATE= 72
+- DELIMITER= 6
+*/
+static inline void libcrux_sha3_simd_portable_load_last_a1_96(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_96(self->st, input[0U], start, len);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_final_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 72
+- DELIM= 6
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_final_80_9e(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *last,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_a1_96(self, last, start, len);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.store_block
+with const generics
+- RATE= 72
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_store_block_f8(
+ uint64_t *s, Eurydice_slice out, size_t start, size_t len) {
+ size_t octets = len / (size_t)8U;
+ for (size_t i = (size_t)0U; i < octets; i++) {
+ size_t i0 = i;
+ Eurydice_slice uu____0 = Eurydice_slice_subslice3(
+ out, start + (size_t)8U * i0, start + (size_t)8U * i0 + (size_t)8U,
+ uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, i0 / (size_t)5U, i0 % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____0, Eurydice_array_to_slice((size_t)8U, ret, uint8_t), uint8_t);
+ }
+ size_t remaining = len % (size_t)8U;
+ if (remaining > (size_t)0U) {
+ Eurydice_slice uu____1 = Eurydice_slice_subslice3(
+ out, start + len - remaining, start + len, uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, octets / (size_t)5U,
+ octets % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____1,
+ Eurydice_array_to_subslice3(ret, (size_t)0U, remaining, uint8_t *),
+ uint8_t);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Squeeze1 for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.squeeze_13
+with const generics
+- RATE= 72
+*/
+static inline void libcrux_sha3_simd_portable_squeeze_13_f8(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice out,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_store_block_f8(self->st, out, start, len);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.portable.keccak1
+with const generics
+- RATE= 72
+- DELIM= 6
+*/
+static inline void libcrux_sha3_generic_keccak_portable_keccak1_96(
+ Eurydice_slice data, Eurydice_slice out) {
+ libcrux_sha3_generic_keccak_KeccakState_17 s =
+ libcrux_sha3_generic_keccak_new_80_04();
+ size_t data_len = Eurydice_slice_len(data, uint8_t);
+ for (size_t i = (size_t)0U; i < data_len / (size_t)72U; i++) {
+ size_t i0 = i;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_block_80_c6(&s, buf, i0 * (size_t)72U);
+ }
+ size_t rem = data_len % (size_t)72U;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_final_80_9e(&s, buf, data_len - rem, rem);
+ size_t outlen = Eurydice_slice_len(out, uint8_t);
+ size_t blocks = outlen / (size_t)72U;
+ size_t last = outlen - outlen % (size_t)72U;
+ if (blocks == (size_t)0U) {
+ libcrux_sha3_simd_portable_squeeze_13_f8(&s, out, (size_t)0U, outlen);
+ } else {
+ libcrux_sha3_simd_portable_squeeze_13_f8(&s, out, (size_t)0U, (size_t)72U);
+ for (size_t i = (size_t)1U; i < blocks; i++) {
+ size_t i0 = i;
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_f8(&s, out, i0 * (size_t)72U,
+ (size_t)72U);
+ }
+ if (last < outlen) {
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_f8(&s, out, last, outlen - last);
+ }
+ }
+}
+
+/**
+ A portable SHA3 512 implementation.
+*/
+static KRML_MUSTINLINE void libcrux_sha3_portable_sha512(Eurydice_slice digest,
+ Eurydice_slice data) {
+ libcrux_sha3_generic_keccak_portable_keccak1_96(data, digest);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_block
+with const generics
+- RATE= 136
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_block_5b(
+ uint64_t *state, Eurydice_slice blocks, size_t start) {
+ uint64_t state_flat[25U] = {0U};
+ for (size_t i = (size_t)0U; i < (size_t)136U / (size_t)8U; i++) {
+ size_t i0 = i;
+ size_t offset = start + (size_t)8U * i0;
+ uint8_t uu____0[8U];
+ Result_15 dst;
+ Eurydice_slice_to_array2(
+ &dst,
+ Eurydice_slice_subslice3(blocks, offset, offset + (size_t)8U,
+ uint8_t *),
+ Eurydice_slice, uint8_t[8U], TryFromSliceError);
+ unwrap_26_68(dst, uu____0);
+ state_flat[i0] = core_num__u64__from_le_bytes(uu____0);
+ }
+ for (size_t i = (size_t)0U; i < (size_t)136U / (size_t)8U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_traits_set_ij_04(
+ state, i0 / (size_t)5U, i0 % (size_t)5U,
+ libcrux_sha3_traits_get_ij_04(state, i0 / (size_t)5U,
+ i0 % (size_t)5U)[0U] ^
+ state_flat[i0]);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_block_a1
+with const generics
+- RATE= 136
+*/
+static inline void libcrux_sha3_simd_portable_load_block_a1_5b(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start) {
+ libcrux_sha3_simd_portable_load_block_5b(self->st, input[0U], start);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_block_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 136
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_block_80_c60(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *blocks,
+ size_t start) {
+ libcrux_sha3_simd_portable_load_block_a1_5b(self, blocks, start);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last
+with const generics
+- RATE= 136
+- DELIMITER= 6
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_last_ad(
+ uint64_t *state, Eurydice_slice blocks, size_t start, size_t len) {
+ uint8_t buffer[136U] = {0U};
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(buffer, (size_t)0U, len, uint8_t *),
+ Eurydice_slice_subslice3(blocks, start, start + len, uint8_t *), uint8_t);
+ buffer[len] = 6U;
+ size_t uu____0 = (size_t)136U - (size_t)1U;
+ buffer[uu____0] = (uint32_t)buffer[uu____0] | 128U;
+ libcrux_sha3_simd_portable_load_block_5b(
+ state, Eurydice_array_to_slice((size_t)136U, buffer, uint8_t),
+ (size_t)0U);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last_a1
+with const generics
+- RATE= 136
+- DELIMITER= 6
+*/
+static inline void libcrux_sha3_simd_portable_load_last_a1_ad(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_ad(self->st, input[0U], start, len);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_final_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 136
+- DELIM= 6
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_final_80_9e0(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *last,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_a1_ad(self, last, start, len);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.store_block
+with const generics
+- RATE= 136
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_store_block_5b(
+ uint64_t *s, Eurydice_slice out, size_t start, size_t len) {
+ size_t octets = len / (size_t)8U;
+ for (size_t i = (size_t)0U; i < octets; i++) {
+ size_t i0 = i;
+ Eurydice_slice uu____0 = Eurydice_slice_subslice3(
+ out, start + (size_t)8U * i0, start + (size_t)8U * i0 + (size_t)8U,
+ uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, i0 / (size_t)5U, i0 % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____0, Eurydice_array_to_slice((size_t)8U, ret, uint8_t), uint8_t);
+ }
+ size_t remaining = len % (size_t)8U;
+ if (remaining > (size_t)0U) {
+ Eurydice_slice uu____1 = Eurydice_slice_subslice3(
+ out, start + len - remaining, start + len, uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, octets / (size_t)5U,
+ octets % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____1,
+ Eurydice_array_to_subslice3(ret, (size_t)0U, remaining, uint8_t *),
+ uint8_t);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Squeeze1 for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.squeeze_13
+with const generics
+- RATE= 136
+*/
+static inline void libcrux_sha3_simd_portable_squeeze_13_5b(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice out,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_store_block_5b(self->st, out, start, len);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.portable.keccak1
+with const generics
+- RATE= 136
+- DELIM= 6
+*/
+static inline void libcrux_sha3_generic_keccak_portable_keccak1_ad(
+ Eurydice_slice data, Eurydice_slice out) {
+ libcrux_sha3_generic_keccak_KeccakState_17 s =
+ libcrux_sha3_generic_keccak_new_80_04();
+ size_t data_len = Eurydice_slice_len(data, uint8_t);
+ for (size_t i = (size_t)0U; i < data_len / (size_t)136U; i++) {
+ size_t i0 = i;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_block_80_c60(&s, buf, i0 * (size_t)136U);
+ }
+ size_t rem = data_len % (size_t)136U;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_final_80_9e0(&s, buf, data_len - rem, rem);
+ size_t outlen = Eurydice_slice_len(out, uint8_t);
+ size_t blocks = outlen / (size_t)136U;
+ size_t last = outlen - outlen % (size_t)136U;
+ if (blocks == (size_t)0U) {
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, (size_t)0U, outlen);
+ } else {
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, (size_t)0U, (size_t)136U);
+ for (size_t i = (size_t)1U; i < blocks; i++) {
+ size_t i0 = i;
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, i0 * (size_t)136U,
+ (size_t)136U);
+ }
+ if (last < outlen) {
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, last, outlen - last);
+ }
+ }
+}
+
+/**
+ A portable SHA3 256 implementation.
+*/
+static KRML_MUSTINLINE void libcrux_sha3_portable_sha256(Eurydice_slice digest,
+ Eurydice_slice data) {
+ libcrux_sha3_generic_keccak_portable_keccak1_ad(data, digest);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last
+with const generics
+- RATE= 136
+- DELIMITER= 31
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_last_ad0(
+ uint64_t *state, Eurydice_slice blocks, size_t start, size_t len) {
+ uint8_t buffer[136U] = {0U};
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(buffer, (size_t)0U, len, uint8_t *),
+ Eurydice_slice_subslice3(blocks, start, start + len, uint8_t *), uint8_t);
+ buffer[len] = 31U;
+ size_t uu____0 = (size_t)136U - (size_t)1U;
+ buffer[uu____0] = (uint32_t)buffer[uu____0] | 128U;
+ libcrux_sha3_simd_portable_load_block_5b(
+ state, Eurydice_array_to_slice((size_t)136U, buffer, uint8_t),
+ (size_t)0U);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last_a1
+with const generics
+- RATE= 136
+- DELIMITER= 31
+*/
+static inline void libcrux_sha3_simd_portable_load_last_a1_ad0(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_ad0(self->st, input[0U], start, len);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_final_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 136
+- DELIM= 31
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_final_80_9e1(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *last,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_a1_ad0(self, last, start, len);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.portable.keccak1
+with const generics
+- RATE= 136
+- DELIM= 31
+*/
+static inline void libcrux_sha3_generic_keccak_portable_keccak1_ad0(
+ Eurydice_slice data, Eurydice_slice out) {
+ libcrux_sha3_generic_keccak_KeccakState_17 s =
+ libcrux_sha3_generic_keccak_new_80_04();
+ size_t data_len = Eurydice_slice_len(data, uint8_t);
+ for (size_t i = (size_t)0U; i < data_len / (size_t)136U; i++) {
+ size_t i0 = i;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_block_80_c60(&s, buf, i0 * (size_t)136U);
+ }
+ size_t rem = data_len % (size_t)136U;
+ Eurydice_slice buf[1U] = {data};
+ libcrux_sha3_generic_keccak_absorb_final_80_9e1(&s, buf, data_len - rem, rem);
+ size_t outlen = Eurydice_slice_len(out, uint8_t);
+ size_t blocks = outlen / (size_t)136U;
+ size_t last = outlen - outlen % (size_t)136U;
+ if (blocks == (size_t)0U) {
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, (size_t)0U, outlen);
+ } else {
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, (size_t)0U, (size_t)136U);
+ for (size_t i = (size_t)1U; i < blocks; i++) {
+ size_t i0 = i;
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, i0 * (size_t)136U,
+ (size_t)136U);
+ }
+ if (last < outlen) {
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(&s);
+ libcrux_sha3_simd_portable_squeeze_13_5b(&s, out, last, outlen - last);
+ }
+ }
+}
+
+/**
+ A portable SHAKE256 implementation.
+*/
+static KRML_MUSTINLINE void libcrux_sha3_portable_shake256(
+ Eurydice_slice digest, Eurydice_slice data) {
+ libcrux_sha3_generic_keccak_portable_keccak1_ad0(data, digest);
+}
+
+typedef libcrux_sha3_generic_keccak_KeccakState_17
+ libcrux_sha3_portable_KeccakState;
+
+/**
+ Create a new SHAKE-128 state object.
+*/
+static KRML_MUSTINLINE libcrux_sha3_generic_keccak_KeccakState_17
+libcrux_sha3_portable_incremental_shake128_init(void) {
+ return libcrux_sha3_generic_keccak_new_80_04();
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_block
+with const generics
+- RATE= 168
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_block_3a(
+ uint64_t *state, Eurydice_slice blocks, size_t start) {
+ uint64_t state_flat[25U] = {0U};
+ for (size_t i = (size_t)0U; i < (size_t)168U / (size_t)8U; i++) {
+ size_t i0 = i;
+ size_t offset = start + (size_t)8U * i0;
+ uint8_t uu____0[8U];
+ Result_15 dst;
+ Eurydice_slice_to_array2(
+ &dst,
+ Eurydice_slice_subslice3(blocks, offset, offset + (size_t)8U,
+ uint8_t *),
+ Eurydice_slice, uint8_t[8U], TryFromSliceError);
+ unwrap_26_68(dst, uu____0);
+ state_flat[i0] = core_num__u64__from_le_bytes(uu____0);
+ }
+ for (size_t i = (size_t)0U; i < (size_t)168U / (size_t)8U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_traits_set_ij_04(
+ state, i0 / (size_t)5U, i0 % (size_t)5U,
+ libcrux_sha3_traits_get_ij_04(state, i0 / (size_t)5U,
+ i0 % (size_t)5U)[0U] ^
+ state_flat[i0]);
+ }
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last
+with const generics
+- RATE= 168
+- DELIMITER= 31
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_load_last_c6(
+ uint64_t *state, Eurydice_slice blocks, size_t start, size_t len) {
+ uint8_t buffer[168U] = {0U};
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(buffer, (size_t)0U, len, uint8_t *),
+ Eurydice_slice_subslice3(blocks, start, start + len, uint8_t *), uint8_t);
+ buffer[len] = 31U;
+ size_t uu____0 = (size_t)168U - (size_t)1U;
+ buffer[uu____0] = (uint32_t)buffer[uu____0] | 128U;
+ libcrux_sha3_simd_portable_load_block_3a(
+ state, Eurydice_array_to_slice((size_t)168U, buffer, uint8_t),
+ (size_t)0U);
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Absorb<1usize> for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.load_last_a1
+with const generics
+- RATE= 168
+- DELIMITER= 31
+*/
+static inline void libcrux_sha3_simd_portable_load_last_a1_c6(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *input,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_c6(self->st, input[0U], start, len);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.generic_keccak.absorb_final_80
+with types uint64_t
+with const generics
+- N= 1
+- RATE= 168
+- DELIM= 31
+*/
+static KRML_MUSTINLINE void libcrux_sha3_generic_keccak_absorb_final_80_9e2(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice *last,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_load_last_a1_c6(self, last, start, len);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+}
+
+/**
+ Absorb
+*/
+static KRML_MUSTINLINE void
+libcrux_sha3_portable_incremental_shake128_absorb_final(
+ libcrux_sha3_generic_keccak_KeccakState_17 *s, Eurydice_slice data0) {
+ libcrux_sha3_generic_keccak_KeccakState_17 *uu____0 = s;
+ Eurydice_slice uu____1[1U] = {data0};
+ libcrux_sha3_generic_keccak_absorb_final_80_9e2(
+ uu____0, uu____1, (size_t)0U, Eurydice_slice_len(data0, uint8_t));
+}
+
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.store_block
+with const generics
+- RATE= 168
+*/
+static KRML_MUSTINLINE void libcrux_sha3_simd_portable_store_block_3a(
+ uint64_t *s, Eurydice_slice out, size_t start, size_t len) {
+ size_t octets = len / (size_t)8U;
+ for (size_t i = (size_t)0U; i < octets; i++) {
+ size_t i0 = i;
+ Eurydice_slice uu____0 = Eurydice_slice_subslice3(
+ out, start + (size_t)8U * i0, start + (size_t)8U * i0 + (size_t)8U,
+ uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, i0 / (size_t)5U, i0 % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____0, Eurydice_array_to_slice((size_t)8U, ret, uint8_t), uint8_t);
+ }
+ size_t remaining = len % (size_t)8U;
+ if (remaining > (size_t)0U) {
+ Eurydice_slice uu____1 = Eurydice_slice_subslice3(
+ out, start + len - remaining, start + len, uint8_t *);
+ uint8_t ret[8U];
+ core_num__u64__to_le_bytes(
+ libcrux_sha3_traits_get_ij_04(s, octets / (size_t)5U,
+ octets % (size_t)5U)[0U],
+ ret);
+ Eurydice_slice_copy(
+ uu____1,
+ Eurydice_array_to_subslice3(ret, (size_t)0U, remaining, uint8_t *),
+ uint8_t);
+ }
+}
+
+/**
+This function found in impl {libcrux_sha3::traits::Squeeze1 for
+libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of libcrux_sha3.simd.portable.squeeze_13
+with const generics
+- RATE= 168
+*/
+static inline void libcrux_sha3_simd_portable_squeeze_13_3a(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice out,
+ size_t start, size_t len) {
+ libcrux_sha3_simd_portable_store_block_3a(self->st, out, start, len);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of
+libcrux_sha3.generic_keccak.portable.squeeze_first_three_blocks_b4 with const
+generics
+- RATE= 168
+*/
+static KRML_MUSTINLINE void
+libcrux_sha3_generic_keccak_portable_squeeze_first_three_blocks_b4_3a(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice out) {
+ libcrux_sha3_simd_portable_squeeze_13_3a(self, out, (size_t)0U, (size_t)168U);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+ libcrux_sha3_simd_portable_squeeze_13_3a(self, out, (size_t)168U,
+ (size_t)168U);
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+ libcrux_sha3_simd_portable_squeeze_13_3a(self, out, (size_t)2U * (size_t)168U,
+ (size_t)168U);
+}
+
+/**
+ Squeeze three blocks
+*/
+static KRML_MUSTINLINE void
+libcrux_sha3_portable_incremental_shake128_squeeze_first_three_blocks(
+ libcrux_sha3_generic_keccak_KeccakState_17 *s, Eurydice_slice out0) {
+ libcrux_sha3_generic_keccak_portable_squeeze_first_three_blocks_b4_3a(s,
+ out0);
+}
+
+/**
+This function found in impl {libcrux_sha3::generic_keccak::KeccakState[core::marker::Sized,
+libcrux_sha3::simd::portable::{libcrux_sha3::traits::KeccakItem<1usize> for
+u64}]}
+*/
+/**
+A monomorphic instance of
+libcrux_sha3.generic_keccak.portable.squeeze_next_block_b4 with const generics
+- RATE= 168
+*/
+static KRML_MUSTINLINE void
+libcrux_sha3_generic_keccak_portable_squeeze_next_block_b4_3a(
+ libcrux_sha3_generic_keccak_KeccakState_17 *self, Eurydice_slice out,
+ size_t start) {
+ libcrux_sha3_generic_keccak_keccakf1600_80_04(self);
+ libcrux_sha3_simd_portable_squeeze_13_3a(self, out, start, (size_t)168U);
+}
+
+/**
+ Squeeze another block
+*/
+static KRML_MUSTINLINE void
+libcrux_sha3_portable_incremental_shake128_squeeze_next_block(
+ libcrux_sha3_generic_keccak_KeccakState_17 *s, Eurydice_slice out0) {
+ libcrux_sha3_generic_keccak_portable_squeeze_next_block_b4_3a(s, out0,
+ (size_t)0U);
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#define libcrux_sha3_portable_H_DEFINED
+#endif /* libcrux_sha3_portable_H */
+
+/* from libcrux/libcrux-ml-kem/extracts/c_header_only/generated/libcrux_mlkem768_portable.h */
+/*
+ * SPDX-FileCopyrightText: 2025 Cryspen Sarl
+ *
+ * SPDX-License-Identifier: MIT or Apache-2.0
+ *
+ * This code was generated with the following revisions:
+ * Charon: 667d2fc98984ff7f3df989c2367e6c1fa4a000e7
+ * Eurydice: 2381cbc416ef2ad0b561c362c500bc84f36b6785
+ * Karamel: 80f5435f2fc505973c469a4afcc8d875cddd0d8b
+ * F*: 71d8221589d4d438af3706d89cb653cf53e18aab
+ * Libcrux: 68dfed5a4a9e40277f62828471c029afed1ecdcc
+ */
+
+#ifndef libcrux_mlkem768_portable_H
+#define libcrux_mlkem768_portable_H
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+static inline void libcrux_ml_kem_hash_functions_portable_G(
+ Eurydice_slice input, uint8_t ret[64U]) {
+ uint8_t digest[64U] = {0U};
+ libcrux_sha3_portable_sha512(
+ Eurydice_array_to_slice((size_t)64U, digest, uint8_t), input);
+ memcpy(ret, digest, (size_t)64U * sizeof(uint8_t));
+}
+
+static inline void libcrux_ml_kem_hash_functions_portable_H(
+ Eurydice_slice input, uint8_t ret[32U]) {
+ uint8_t digest[32U] = {0U};
+ libcrux_sha3_portable_sha256(
+ Eurydice_array_to_slice((size_t)32U, digest, uint8_t), input);
+ memcpy(ret, digest, (size_t)32U * sizeof(uint8_t));
+}
+
+static const int16_t libcrux_ml_kem_polynomial_ZETAS_TIMES_MONTGOMERY_R[128U] =
+ {(int16_t)-1044, (int16_t)-758, (int16_t)-359, (int16_t)-1517,
+ (int16_t)1493, (int16_t)1422, (int16_t)287, (int16_t)202,
+ (int16_t)-171, (int16_t)622, (int16_t)1577, (int16_t)182,
+ (int16_t)962, (int16_t)-1202, (int16_t)-1474, (int16_t)1468,
+ (int16_t)573, (int16_t)-1325, (int16_t)264, (int16_t)383,
+ (int16_t)-829, (int16_t)1458, (int16_t)-1602, (int16_t)-130,
+ (int16_t)-681, (int16_t)1017, (int16_t)732, (int16_t)608,
+ (int16_t)-1542, (int16_t)411, (int16_t)-205, (int16_t)-1571,
+ (int16_t)1223, (int16_t)652, (int16_t)-552, (int16_t)1015,
+ (int16_t)-1293, (int16_t)1491, (int16_t)-282, (int16_t)-1544,
+ (int16_t)516, (int16_t)-8, (int16_t)-320, (int16_t)-666,
+ (int16_t)-1618, (int16_t)-1162, (int16_t)126, (int16_t)1469,
+ (int16_t)-853, (int16_t)-90, (int16_t)-271, (int16_t)830,
+ (int16_t)107, (int16_t)-1421, (int16_t)-247, (int16_t)-951,
+ (int16_t)-398, (int16_t)961, (int16_t)-1508, (int16_t)-725,
+ (int16_t)448, (int16_t)-1065, (int16_t)677, (int16_t)-1275,
+ (int16_t)-1103, (int16_t)430, (int16_t)555, (int16_t)843,
+ (int16_t)-1251, (int16_t)871, (int16_t)1550, (int16_t)105,
+ (int16_t)422, (int16_t)587, (int16_t)177, (int16_t)-235,
+ (int16_t)-291, (int16_t)-460, (int16_t)1574, (int16_t)1653,
+ (int16_t)-246, (int16_t)778, (int16_t)1159, (int16_t)-147,
+ (int16_t)-777, (int16_t)1483, (int16_t)-602, (int16_t)1119,
+ (int16_t)-1590, (int16_t)644, (int16_t)-872, (int16_t)349,
+ (int16_t)418, (int16_t)329, (int16_t)-156, (int16_t)-75,
+ (int16_t)817, (int16_t)1097, (int16_t)603, (int16_t)610,
+ (int16_t)1322, (int16_t)-1285, (int16_t)-1465, (int16_t)384,
+ (int16_t)-1215, (int16_t)-136, (int16_t)1218, (int16_t)-1335,
+ (int16_t)-874, (int16_t)220, (int16_t)-1187, (int16_t)-1659,
+ (int16_t)-1185, (int16_t)-1530, (int16_t)-1278, (int16_t)794,
+ (int16_t)-1510, (int16_t)-854, (int16_t)-870, (int16_t)478,
+ (int16_t)-108, (int16_t)-308, (int16_t)996, (int16_t)991,
+ (int16_t)958, (int16_t)-1460, (int16_t)1522, (int16_t)1628};
+
+static KRML_MUSTINLINE int16_t libcrux_ml_kem_polynomial_zeta(size_t i) {
+ return libcrux_ml_kem_polynomial_ZETAS_TIMES_MONTGOMERY_R[i];
+}
+
+#define LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT ((size_t)16U)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR ((size_t)16U)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_MONTGOMERY_R_SQUARED_MOD_FIELD_MODULUS \
+ ((int16_t)1353)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS ((int16_t)3329)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_INVERSE_OF_MODULUS_MOD_MONTGOMERY_R \
+ (62209U)
+
+typedef struct libcrux_ml_kem_vector_portable_vector_type_PortableVector_s {
+ int16_t elements[16U];
+} libcrux_ml_kem_vector_portable_vector_type_PortableVector;
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_vector_type_from_i16_array(
+ Eurydice_slice array) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lit;
+ int16_t ret[16U];
+ Result_0a dst;
+ Eurydice_slice_to_array2(
+ &dst, Eurydice_slice_subslice3(array, (size_t)0U, (size_t)16U, int16_t *),
+ Eurydice_slice, int16_t[16U], TryFromSliceError);
+ unwrap_26_00(dst, ret);
+ memcpy(lit.elements, ret, (size_t)16U * sizeof(int16_t));
+ return lit;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_from_i16_array_b8(Eurydice_slice array) {
+ return libcrux_ml_kem_vector_portable_vector_type_from_i16_array(
+ libcrux_secrets_int_classify_public_classify_ref_9b_39(array));
+}
+
+typedef struct int16_t_x8_s {
+ int16_t fst;
+ int16_t snd;
+ int16_t thd;
+ int16_t f3;
+ int16_t f4;
+ int16_t f5;
+ int16_t f6;
+ int16_t f7;
+} int16_t_x8;
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_vector_type_zero(void) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lit;
+ int16_t ret[16U];
+ int16_t buf[16U] = {0U};
+ libcrux_secrets_int_public_integers_classify_27_46(buf, ret);
+ memcpy(lit.elements, ret, (size_t)16U * sizeof(int16_t));
+ return lit;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ZERO_b8(void) {
+ return libcrux_ml_kem_vector_portable_vector_type_zero();
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_add(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ size_t uu____0 = i0;
+ lhs.elements[uu____0] = lhs.elements[uu____0] + rhs->elements[i0];
+ }
+ return lhs;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_add_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs) {
+ return libcrux_ml_kem_vector_portable_arithmetic_add(lhs, rhs);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_sub(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ size_t uu____0 = i0;
+ lhs.elements[uu____0] = lhs.elements[uu____0] - rhs->elements[i0];
+ }
+ return lhs;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_sub_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs) {
+ return libcrux_ml_kem_vector_portable_arithmetic_sub(lhs, rhs);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_multiply_by_constant(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec, int16_t c) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ size_t uu____0 = i0;
+ vec.elements[uu____0] = vec.elements[uu____0] * c;
+ }
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_multiply_by_constant_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec, int16_t c) {
+ return libcrux_ml_kem_vector_portable_arithmetic_multiply_by_constant(vec, c);
+}
+
+/**
+ Note: This function is not secret independent
+ Only use with public values.
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_cond_subtract_3329(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ if (libcrux_secrets_int_public_integers_declassify_d8_39(
+ vec.elements[i0]) >= (int16_t)3329) {
+ size_t uu____0 = i0;
+ vec.elements[uu____0] = vec.elements[uu____0] - (int16_t)3329;
+ }
+ }
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_cond_subtract_3329_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector v) {
+ return libcrux_ml_kem_vector_portable_arithmetic_cond_subtract_3329(v);
+}
+
+#define LIBCRUX_ML_KEM_VECTOR_PORTABLE_ARITHMETIC_BARRETT_MULTIPLIER \
+ ((int32_t)20159)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_BARRETT_SHIFT ((int32_t)26)
+
+#define LIBCRUX_ML_KEM_VECTOR_TRAITS_BARRETT_R \
+ ((int32_t)1 << (uint32_t)LIBCRUX_ML_KEM_VECTOR_TRAITS_BARRETT_SHIFT)
+
+/**
+ Signed Barrett Reduction
+
+ Given an input `value`, `barrett_reduce` outputs a representative `result`
+ such that:
+
+ - result ≡ value (mod FIELD_MODULUS)
+ - the absolute value of `result` is bound as follows:
+
+ `|result| ≤ FIELD_MODULUS / 2 · (|value|/BARRETT_R + 1)
+
+ Note: The input bound is 28296 to prevent overflow in the multiplication of
+ quotient by FIELD_MODULUS
+
+*/
+static inline int16_t
+libcrux_ml_kem_vector_portable_arithmetic_barrett_reduce_element(
+ int16_t value) {
+ int32_t t = libcrux_secrets_int_as_i32_f5(value) *
+ LIBCRUX_ML_KEM_VECTOR_PORTABLE_ARITHMETIC_BARRETT_MULTIPLIER +
+ (LIBCRUX_ML_KEM_VECTOR_TRAITS_BARRETT_R >> 1U);
+ int16_t quotient = libcrux_secrets_int_as_i16_36(
+ t >> (uint32_t)LIBCRUX_ML_KEM_VECTOR_TRAITS_BARRETT_SHIFT);
+ return value - quotient * LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS;
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_barrett_reduce(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ int16_t vi =
+ libcrux_ml_kem_vector_portable_arithmetic_barrett_reduce_element(
+ vec.elements[i0]);
+ vec.elements[i0] = vi;
+ }
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_barrett_reduce_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vector) {
+ return libcrux_ml_kem_vector_portable_arithmetic_barrett_reduce(vector);
+}
+
+#define LIBCRUX_ML_KEM_VECTOR_PORTABLE_ARITHMETIC_MONTGOMERY_SHIFT (16U)
+
+/**
+ Signed Montgomery Reduction
+
+ Given an input `value`, `montgomery_reduce` outputs a representative `o`
+ such that:
+
+ - o ≡ value · MONTGOMERY_R^(-1) (mod FIELD_MODULUS)
+ - the absolute value of `o` is bound as follows:
+
+ `|result| ≤ ceil(|value| / MONTGOMERY_R) + 1665
+
+ In particular, if `|value| ≤ FIELD_MODULUS-1 * FIELD_MODULUS-1`, then `|o| <=
+ FIELD_MODULUS-1`. And, if `|value| ≤ pow2 16 * FIELD_MODULUS-1`, then `|o| <=
+ FIELD_MODULUS + 1664
+
+*/
+static inline int16_t
+libcrux_ml_kem_vector_portable_arithmetic_montgomery_reduce_element(
+ int32_t value) {
+ int32_t k =
+ libcrux_secrets_int_as_i32_f5(libcrux_secrets_int_as_i16_36(value)) *
+ libcrux_secrets_int_as_i32_b8(
+ libcrux_secrets_int_public_integers_classify_27_df(
+ LIBCRUX_ML_KEM_VECTOR_TRAITS_INVERSE_OF_MODULUS_MOD_MONTGOMERY_R));
+ int32_t k_times_modulus =
+ libcrux_secrets_int_as_i32_f5(libcrux_secrets_int_as_i16_36(k)) *
+ libcrux_secrets_int_as_i32_f5(
+ libcrux_secrets_int_public_integers_classify_27_39(
+ LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS));
+ int16_t c = libcrux_secrets_int_as_i16_36(
+ k_times_modulus >>
+ (uint32_t)LIBCRUX_ML_KEM_VECTOR_PORTABLE_ARITHMETIC_MONTGOMERY_SHIFT);
+ int16_t value_high = libcrux_secrets_int_as_i16_36(
+ value >>
+ (uint32_t)LIBCRUX_ML_KEM_VECTOR_PORTABLE_ARITHMETIC_MONTGOMERY_SHIFT);
+ return value_high - c;
+}
+
+/**
+ If `fe` is some field element 'x' of the Kyber field and `fer` is congruent to
+ `y · MONTGOMERY_R`, this procedure outputs a value that is congruent to
+ `x · y`, as follows:
+
+ `fe · fer ≡ x · y · MONTGOMERY_R (mod FIELD_MODULUS)`
+
+ `montgomery_reduce` takes the value `x · y · MONTGOMERY_R` and outputs a
+ representative `x · y · MONTGOMERY_R * MONTGOMERY_R^{-1} ≡ x · y (mod
+ FIELD_MODULUS)`.
+*/
+static KRML_MUSTINLINE int16_t
+libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_fe_by_fer(
+ int16_t fe, int16_t fer) {
+ int32_t product =
+ libcrux_secrets_int_as_i32_f5(fe) * libcrux_secrets_int_as_i32_f5(fer);
+ return libcrux_ml_kem_vector_portable_arithmetic_montgomery_reduce_element(
+ product);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_by_constant(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec, int16_t c) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ vec.elements[i0] =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_fe_by_fer(
+ vec.elements[i0], c);
+ }
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vector,
+ int16_t constant) {
+ return libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_by_constant(
+ vector, libcrux_secrets_int_public_integers_classify_27_39(constant));
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_bitwise_and_with_constant(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec, int16_t c) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ size_t uu____0 = i0;
+ vec.elements[uu____0] = vec.elements[uu____0] & c;
+ }
+ return vec;
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.vector.portable.arithmetic.shift_right
+with const generics
+- SHIFT_BY= 15
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_shift_right_ef(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ vec.elements[i0] = vec.elements[i0] >> (uint32_t)(int32_t)15;
+ }
+ return vec;
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_arithmetic_to_unsigned_representative(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector t =
+ libcrux_ml_kem_vector_portable_arithmetic_shift_right_ef(a);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector fm =
+ libcrux_ml_kem_vector_portable_arithmetic_bitwise_and_with_constant(
+ t, LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS);
+ return libcrux_ml_kem_vector_portable_arithmetic_add(a, &fm);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_to_unsigned_representative_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_arithmetic_to_unsigned_representative(
+ a);
+}
+
+/**
+ The `compress_*` functions implement the `Compress` function specified in the
+ NIST FIPS 203 standard (Page 18, Expression 4.5), which is defined as:
+
+ ```plaintext
+ Compress_d: ℤq -> ℤ_{2ᵈ}
+ Compress_d(x) = ⌈(2ᵈ/q)·x⌋
+ ```
+
+ Since `⌈x⌋ = ⌊x + 1/2⌋` we have:
+
+ ```plaintext
+ Compress_d(x) = ⌊(2ᵈ/q)·x + 1/2⌋
+ = ⌊(2^{d+1}·x + q) / 2q⌋
+ ```
+
+ For further information about the function implementations, consult the
+ `implementation_notes.pdf` document in this directory.
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+static inline uint8_t
+libcrux_ml_kem_vector_portable_compress_compress_message_coefficient(
+ uint16_t fe) {
+ int16_t shifted =
+ libcrux_secrets_int_public_integers_classify_27_39((int16_t)1664) -
+ libcrux_secrets_int_as_i16_ca(fe);
+ int16_t mask = shifted >> 15U;
+ int16_t shifted_to_positive = mask ^ shifted;
+ int16_t shifted_positive_in_range = shifted_to_positive - (int16_t)832;
+ int16_t r0 = shifted_positive_in_range >> 15U;
+ int16_t r1 = r0 & (int16_t)1;
+ return libcrux_secrets_int_as_u8_f5(r1);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_compress_1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ a.elements[i0] = libcrux_secrets_int_as_i16_59(
+ libcrux_ml_kem_vector_portable_compress_compress_message_coefficient(
+ libcrux_secrets_int_as_u16_f5(a.elements[i0])));
+ }
+ return a;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_1_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_compress_1(a);
+}
+
+static KRML_MUSTINLINE uint32_t
+libcrux_ml_kem_vector_portable_arithmetic_get_n_least_significant_bits(
+ uint8_t n, uint32_t value) {
+ return value & ((1U << (uint32_t)n) - 1U);
+}
+
+static inline int16_t
+libcrux_ml_kem_vector_portable_compress_compress_ciphertext_coefficient(
+ uint8_t coefficient_bits, uint16_t fe) {
+ uint64_t compressed = libcrux_secrets_int_as_u64_ca(fe)
+ << (uint32_t)coefficient_bits;
+ compressed = compressed + 1664ULL;
+ compressed = compressed * 10321340ULL;
+ compressed = compressed >> 35U;
+ return libcrux_secrets_int_as_i16_b8(
+ libcrux_ml_kem_vector_portable_arithmetic_get_n_least_significant_bits(
+ coefficient_bits, libcrux_secrets_int_as_u32_a3(compressed)));
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_decompress_1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector z =
+ libcrux_ml_kem_vector_portable_vector_type_zero();
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector s =
+ libcrux_ml_kem_vector_portable_arithmetic_sub(z, &a);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector res =
+ libcrux_ml_kem_vector_portable_arithmetic_bitwise_and_with_constant(
+ s, (int16_t)1665);
+ return res;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_decompress_1_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_decompress_1(a);
+}
+
+static KRML_MUSTINLINE void libcrux_ml_kem_vector_portable_ntt_ntt_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *vec,
+ int16_t zeta, size_t i, size_t j) {
+ int16_t t =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_fe_by_fer(
+ vec->elements[j],
+ libcrux_secrets_int_public_integers_classify_27_39(zeta));
+ int16_t a_minus_t = vec->elements[i] - t;
+ int16_t a_plus_t = vec->elements[i] + t;
+ vec->elements[j] = a_minus_t;
+ vec->elements[i] = a_plus_t;
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_ntt_layer_1_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta0, int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)0U,
+ (size_t)2U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)1U,
+ (size_t)3U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)4U,
+ (size_t)6U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)5U,
+ (size_t)7U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta2, (size_t)8U,
+ (size_t)10U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta2, (size_t)9U,
+ (size_t)11U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta3, (size_t)12U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta3, (size_t)13U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_layer_1_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta0,
+ int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ return libcrux_ml_kem_vector_portable_ntt_ntt_layer_1_step(a, zeta0, zeta1,
+ zeta2, zeta3);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_ntt_layer_2_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta0, int16_t zeta1) {
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)0U,
+ (size_t)4U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)1U,
+ (size_t)5U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)2U,
+ (size_t)6U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta0, (size_t)3U,
+ (size_t)7U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)8U,
+ (size_t)12U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)9U,
+ (size_t)13U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)10U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta1, (size_t)11U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_layer_2_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta0,
+ int16_t zeta1) {
+ return libcrux_ml_kem_vector_portable_ntt_ntt_layer_2_step(a, zeta0, zeta1);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_ntt_layer_3_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta) {
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)0U,
+ (size_t)8U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)1U,
+ (size_t)9U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)2U,
+ (size_t)10U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)3U,
+ (size_t)11U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)4U,
+ (size_t)12U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)5U,
+ (size_t)13U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)6U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_ntt_step(&vec, zeta, (size_t)7U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_layer_3_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta) {
+ return libcrux_ml_kem_vector_portable_ntt_ntt_layer_3_step(a, zeta);
+}
+
+static KRML_MUSTINLINE void libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *vec,
+ int16_t zeta, size_t i, size_t j) {
+ int16_t a_minus_b = vec->elements[j] - vec->elements[i];
+ int16_t a_plus_b = vec->elements[j] + vec->elements[i];
+ int16_t o0 = libcrux_ml_kem_vector_portable_arithmetic_barrett_reduce_element(
+ a_plus_b);
+ int16_t o1 =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_multiply_fe_by_fer(
+ a_minus_b, libcrux_secrets_int_public_integers_classify_27_39(zeta));
+ vec->elements[i] = o0;
+ vec->elements[j] = o1;
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_1_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta0, int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)0U,
+ (size_t)2U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)1U,
+ (size_t)3U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)4U,
+ (size_t)6U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)5U,
+ (size_t)7U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta2, (size_t)8U,
+ (size_t)10U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta2, (size_t)9U,
+ (size_t)11U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta3, (size_t)12U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta3, (size_t)13U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_inv_ntt_layer_1_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta0,
+ int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ return libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_1_step(
+ a, zeta0, zeta1, zeta2, zeta3);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_2_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta0, int16_t zeta1) {
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)0U,
+ (size_t)4U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)1U,
+ (size_t)5U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)2U,
+ (size_t)6U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta0, (size_t)3U,
+ (size_t)7U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)8U,
+ (size_t)12U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)9U,
+ (size_t)13U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)10U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta1, (size_t)11U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_inv_ntt_layer_2_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta0,
+ int16_t zeta1) {
+ return libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_2_step(a, zeta0,
+ zeta1);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_3_step(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector vec,
+ int16_t zeta) {
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)0U,
+ (size_t)8U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)1U,
+ (size_t)9U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)2U,
+ (size_t)10U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)3U,
+ (size_t)11U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)4U,
+ (size_t)12U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)5U,
+ (size_t)13U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)6U,
+ (size_t)14U);
+ libcrux_ml_kem_vector_portable_ntt_inv_ntt_step(&vec, zeta, (size_t)7U,
+ (size_t)15U);
+ return vec;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_inv_ntt_layer_3_step_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a, int16_t zeta) {
+ return libcrux_ml_kem_vector_portable_ntt_inv_ntt_layer_3_step(a, zeta);
+}
+
+/**
+ Compute the product of two Kyber binomials with respect to the
+ modulus `X² - zeta`.
+
+ This function almost implements Algorithm 11 of the
+ NIST FIPS 203 standard, which is reproduced below:
+
+ ```plaintext
+ Input: a₀, a₁, b₀, b₁ ∈ ℤq.
+ Input: γ ∈ ℤq.
+ Output: c₀, c₁ ∈ ℤq.
+
+ c₀ ← a₀·b₀ + a₁·b₁·γ
+ c₁ ← a₀·b₁ + a₁·b₀
+ return c₀, c₁
+ ```
+ We say "almost" because the coefficients output by this function are in
+ the Montgomery domain (unlike in the specification).
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *a,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *b, int16_t zeta,
+ size_t i, libcrux_ml_kem_vector_portable_vector_type_PortableVector *out) {
+ int16_t ai = a->elements[(size_t)2U * i];
+ int16_t bi = b->elements[(size_t)2U * i];
+ int16_t aj = a->elements[(size_t)2U * i + (size_t)1U];
+ int16_t bj = b->elements[(size_t)2U * i + (size_t)1U];
+ int32_t ai_bi =
+ libcrux_secrets_int_as_i32_f5(ai) * libcrux_secrets_int_as_i32_f5(bi);
+ int32_t aj_bj_ =
+ libcrux_secrets_int_as_i32_f5(aj) * libcrux_secrets_int_as_i32_f5(bj);
+ int16_t aj_bj =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_reduce_element(
+ aj_bj_);
+ int32_t aj_bj_zeta = libcrux_secrets_int_as_i32_f5(aj_bj) *
+ libcrux_secrets_int_as_i32_f5(zeta);
+ int32_t ai_bi_aj_bj = ai_bi + aj_bj_zeta;
+ int16_t o0 =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_reduce_element(
+ ai_bi_aj_bj);
+ int32_t ai_bj =
+ libcrux_secrets_int_as_i32_f5(ai) * libcrux_secrets_int_as_i32_f5(bj);
+ int32_t aj_bi =
+ libcrux_secrets_int_as_i32_f5(aj) * libcrux_secrets_int_as_i32_f5(bi);
+ int32_t ai_bj_aj_bi = ai_bj + aj_bi;
+ int16_t o1 =
+ libcrux_ml_kem_vector_portable_arithmetic_montgomery_reduce_element(
+ ai_bj_aj_bi);
+ out->elements[(size_t)2U * i] = o0;
+ out->elements[(size_t)2U * i + (size_t)1U] = o1;
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_ntt_multiply(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs,
+ int16_t zeta0, int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ int16_t nzeta0 = -zeta0;
+ int16_t nzeta1 = -zeta1;
+ int16_t nzeta2 = -zeta2;
+ int16_t nzeta3 = -zeta3;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector out =
+ libcrux_ml_kem_vector_portable_vector_type_zero();
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(zeta0),
+ (size_t)0U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(nzeta0),
+ (size_t)1U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(zeta1),
+ (size_t)2U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(nzeta1),
+ (size_t)3U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(zeta2),
+ (size_t)4U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(nzeta2),
+ (size_t)5U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(zeta3),
+ (size_t)6U, &out);
+ libcrux_ml_kem_vector_portable_ntt_ntt_multiply_binomials(
+ lhs, rhs, libcrux_secrets_int_public_integers_classify_27_39(nzeta3),
+ (size_t)7U, &out);
+ return out;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_ntt_multiply_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *lhs,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector *rhs,
+ int16_t zeta0, int16_t zeta1, int16_t zeta2, int16_t zeta3) {
+ return libcrux_ml_kem_vector_portable_ntt_ntt_multiply(lhs, rhs, zeta0, zeta1,
+ zeta2, zeta3);
+}
+
+static KRML_MUSTINLINE void
+libcrux_ml_kem_vector_portable_serialize_serialize_1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector v,
+ uint8_t ret[2U]) {
+ uint8_t result0 =
+ (((((((uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[0U]) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[1U]) << 1U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[2U]) << 2U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[3U]) << 3U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[4U]) << 4U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[5U]) << 5U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[6U]) << 6U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[7U]) << 7U;
+ uint8_t result1 =
+ (((((((uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[8U]) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[9U]) << 1U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[10U]) << 2U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[11U]) << 3U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[12U]) << 4U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[13U]) << 5U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[14U]) << 6U) |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(v.elements[15U]) << 7U;
+ ret[0U] = result0;
+ ret[1U] = result1;
+}
+
+static inline void libcrux_ml_kem_vector_portable_serialize_1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[2U]) {
+ uint8_t ret0[2U];
+ libcrux_ml_kem_vector_portable_serialize_serialize_1(a, ret0);
+ libcrux_secrets_int_public_integers_declassify_d8_d4(ret0, ret);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline void libcrux_ml_kem_vector_portable_serialize_1_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[2U]) {
+ libcrux_ml_kem_vector_portable_serialize_1(a, ret);
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_serialize_deserialize_1(Eurydice_slice v) {
+ int16_t result0 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) & 1U);
+ int16_t result1 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 1U &
+ 1U);
+ int16_t result2 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 2U &
+ 1U);
+ int16_t result3 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 3U &
+ 1U);
+ int16_t result4 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 4U &
+ 1U);
+ int16_t result5 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 5U &
+ 1U);
+ int16_t result6 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 6U &
+ 1U);
+ int16_t result7 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)0U, uint8_t, uint8_t *) >> 7U &
+ 1U);
+ int16_t result8 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) & 1U);
+ int16_t result9 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 1U &
+ 1U);
+ int16_t result10 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 2U &
+ 1U);
+ int16_t result11 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 3U &
+ 1U);
+ int16_t result12 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 4U &
+ 1U);
+ int16_t result13 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 5U &
+ 1U);
+ int16_t result14 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 6U &
+ 1U);
+ int16_t result15 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(v, (size_t)1U, uint8_t, uint8_t *) >> 7U &
+ 1U);
+ return (
+ KRML_CLITERAL(libcrux_ml_kem_vector_portable_vector_type_PortableVector){
+ .elements = {result0, result1, result2, result3, result4, result5,
+ result6, result7, result8, result9, result10, result11,
+ result12, result13, result14, result15}});
+}
+
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_1(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_serialize_deserialize_1(
+ libcrux_secrets_int_classify_public_classify_ref_9b_90(a));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_1_b8(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_deserialize_1(a);
+}
+
+typedef struct uint8_t_x4_s {
+ uint8_t fst;
+ uint8_t snd;
+ uint8_t thd;
+ uint8_t f3;
+} uint8_t_x4;
+
+static KRML_MUSTINLINE uint8_t_x4
+libcrux_ml_kem_vector_portable_serialize_serialize_4_int(Eurydice_slice v) {
+ uint8_t result0 = (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)1U, int16_t, int16_t *))
+ << 4U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(Eurydice_slice_index(
+ v, (size_t)0U, int16_t, int16_t *));
+ uint8_t result1 = (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)3U, int16_t, int16_t *))
+ << 4U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(Eurydice_slice_index(
+ v, (size_t)2U, int16_t, int16_t *));
+ uint8_t result2 = (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)5U, int16_t, int16_t *))
+ << 4U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(Eurydice_slice_index(
+ v, (size_t)4U, int16_t, int16_t *));
+ uint8_t result3 = (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)7U, int16_t, int16_t *))
+ << 4U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(Eurydice_slice_index(
+ v, (size_t)6U, int16_t, int16_t *));
+ return (KRML_CLITERAL(uint8_t_x4){
+ .fst = result0, .snd = result1, .thd = result2, .f3 = result3});
+}
+
+static KRML_MUSTINLINE void
+libcrux_ml_kem_vector_portable_serialize_serialize_4(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector v,
+ uint8_t ret[8U]) {
+ uint8_t_x4 result0_3 =
+ libcrux_ml_kem_vector_portable_serialize_serialize_4_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)0U, (size_t)8U,
+ int16_t *));
+ uint8_t_x4 result4_7 =
+ libcrux_ml_kem_vector_portable_serialize_serialize_4_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)8U, (size_t)16U,
+ int16_t *));
+ ret[0U] = result0_3.fst;
+ ret[1U] = result0_3.snd;
+ ret[2U] = result0_3.thd;
+ ret[3U] = result0_3.f3;
+ ret[4U] = result4_7.fst;
+ ret[5U] = result4_7.snd;
+ ret[6U] = result4_7.thd;
+ ret[7U] = result4_7.f3;
+}
+
+static inline void libcrux_ml_kem_vector_portable_serialize_4(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[8U]) {
+ uint8_t ret0[8U];
+ libcrux_ml_kem_vector_portable_serialize_serialize_4(a, ret0);
+ libcrux_secrets_int_public_integers_declassify_d8_76(ret0, ret);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline void libcrux_ml_kem_vector_portable_serialize_4_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[8U]) {
+ libcrux_ml_kem_vector_portable_serialize_4(a, ret);
+}
+
+static KRML_MUSTINLINE int16_t_x8
+libcrux_ml_kem_vector_portable_serialize_deserialize_4_int(
+ Eurydice_slice bytes) {
+ int16_t v0 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)0U, uint8_t, uint8_t *) &
+ 15U);
+ int16_t v1 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)0U, uint8_t, uint8_t *) >>
+ 4U &
+ 15U);
+ int16_t v2 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)1U, uint8_t, uint8_t *) &
+ 15U);
+ int16_t v3 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)1U, uint8_t, uint8_t *) >>
+ 4U &
+ 15U);
+ int16_t v4 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)2U, uint8_t, uint8_t *) &
+ 15U);
+ int16_t v5 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)2U, uint8_t, uint8_t *) >>
+ 4U &
+ 15U);
+ int16_t v6 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)3U, uint8_t, uint8_t *) &
+ 15U);
+ int16_t v7 = libcrux_secrets_int_as_i16_59(
+ (uint32_t)Eurydice_slice_index(bytes, (size_t)3U, uint8_t, uint8_t *) >>
+ 4U &
+ 15U);
+ return (KRML_CLITERAL(int16_t_x8){.fst = v0,
+ .snd = v1,
+ .thd = v2,
+ .f3 = v3,
+ .f4 = v4,
+ .f5 = v5,
+ .f6 = v6,
+ .f7 = v7});
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_serialize_deserialize_4(Eurydice_slice bytes) {
+ int16_t_x8 v0_7 = libcrux_ml_kem_vector_portable_serialize_deserialize_4_int(
+ Eurydice_slice_subslice3(bytes, (size_t)0U, (size_t)4U, uint8_t *));
+ int16_t_x8 v8_15 = libcrux_ml_kem_vector_portable_serialize_deserialize_4_int(
+ Eurydice_slice_subslice3(bytes, (size_t)4U, (size_t)8U, uint8_t *));
+ return (
+ KRML_CLITERAL(libcrux_ml_kem_vector_portable_vector_type_PortableVector){
+ .elements = {v0_7.fst, v0_7.snd, v0_7.thd, v0_7.f3, v0_7.f4, v0_7.f5,
+ v0_7.f6, v0_7.f7, v8_15.fst, v8_15.snd, v8_15.thd,
+ v8_15.f3, v8_15.f4, v8_15.f5, v8_15.f6, v8_15.f7}});
+}
+
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_4(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_serialize_deserialize_4(
+ libcrux_secrets_int_classify_public_classify_ref_9b_90(a));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_4_b8(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_deserialize_4(a);
+}
+
+typedef struct uint8_t_x5_s {
+ uint8_t fst;
+ uint8_t snd;
+ uint8_t thd;
+ uint8_t f3;
+ uint8_t f4;
+} uint8_t_x5;
+
+static KRML_MUSTINLINE uint8_t_x5
+libcrux_ml_kem_vector_portable_serialize_serialize_10_int(Eurydice_slice v) {
+ uint8_t r0 = libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)0U, int16_t, int16_t *) & (int16_t)255);
+ uint8_t r1 =
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)1U, int16_t, int16_t *) & (int16_t)63)
+ << 2U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)0U, int16_t, int16_t *) >> 8U &
+ (int16_t)3);
+ uint8_t r2 =
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)2U, int16_t, int16_t *) & (int16_t)15)
+ << 4U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)1U, int16_t, int16_t *) >> 6U &
+ (int16_t)15);
+ uint8_t r3 =
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)3U, int16_t, int16_t *) & (int16_t)3)
+ << 6U |
+ (uint32_t)libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)2U, int16_t, int16_t *) >> 4U &
+ (int16_t)63);
+ uint8_t r4 = libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)3U, int16_t, int16_t *) >> 2U &
+ (int16_t)255);
+ return (KRML_CLITERAL(uint8_t_x5){
+ .fst = r0, .snd = r1, .thd = r2, .f3 = r3, .f4 = r4});
+}
+
+static KRML_MUSTINLINE void
+libcrux_ml_kem_vector_portable_serialize_serialize_10(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector v,
+ uint8_t ret[20U]) {
+ uint8_t_x5 r0_4 = libcrux_ml_kem_vector_portable_serialize_serialize_10_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)0U, (size_t)4U,
+ int16_t *));
+ uint8_t_x5 r5_9 = libcrux_ml_kem_vector_portable_serialize_serialize_10_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)4U, (size_t)8U,
+ int16_t *));
+ uint8_t_x5 r10_14 = libcrux_ml_kem_vector_portable_serialize_serialize_10_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)8U, (size_t)12U,
+ int16_t *));
+ uint8_t_x5 r15_19 = libcrux_ml_kem_vector_portable_serialize_serialize_10_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)12U, (size_t)16U,
+ int16_t *));
+ ret[0U] = r0_4.fst;
+ ret[1U] = r0_4.snd;
+ ret[2U] = r0_4.thd;
+ ret[3U] = r0_4.f3;
+ ret[4U] = r0_4.f4;
+ ret[5U] = r5_9.fst;
+ ret[6U] = r5_9.snd;
+ ret[7U] = r5_9.thd;
+ ret[8U] = r5_9.f3;
+ ret[9U] = r5_9.f4;
+ ret[10U] = r10_14.fst;
+ ret[11U] = r10_14.snd;
+ ret[12U] = r10_14.thd;
+ ret[13U] = r10_14.f3;
+ ret[14U] = r10_14.f4;
+ ret[15U] = r15_19.fst;
+ ret[16U] = r15_19.snd;
+ ret[17U] = r15_19.thd;
+ ret[18U] = r15_19.f3;
+ ret[19U] = r15_19.f4;
+}
+
+static inline void libcrux_ml_kem_vector_portable_serialize_10(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[20U]) {
+ uint8_t ret0[20U];
+ libcrux_ml_kem_vector_portable_serialize_serialize_10(a, ret0);
+ libcrux_secrets_int_public_integers_declassify_d8_57(ret0, ret);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline void libcrux_ml_kem_vector_portable_serialize_10_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[20U]) {
+ libcrux_ml_kem_vector_portable_serialize_10(a, ret);
+}
+
+static KRML_MUSTINLINE int16_t_x8
+libcrux_ml_kem_vector_portable_serialize_deserialize_10_int(
+ Eurydice_slice bytes) {
+ int16_t r0 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)1U, uint8_t, uint8_t *)) &
+ (int16_t)3)
+ << 8U |
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)0U, uint8_t, uint8_t *)) &
+ (int16_t)255));
+ int16_t r1 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)2U, uint8_t, uint8_t *)) &
+ (int16_t)15)
+ << 6U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)1U, uint8_t, uint8_t *)) >>
+ 2U);
+ int16_t r2 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)3U, uint8_t, uint8_t *)) &
+ (int16_t)63)
+ << 4U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)2U, uint8_t, uint8_t *)) >>
+ 4U);
+ int16_t r3 = libcrux_secrets_int_as_i16_f5(
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)4U, uint8_t, uint8_t *))
+ << 2U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)3U, uint8_t, uint8_t *)) >>
+ 6U);
+ int16_t r4 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)6U, uint8_t, uint8_t *)) &
+ (int16_t)3)
+ << 8U |
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)5U, uint8_t, uint8_t *)) &
+ (int16_t)255));
+ int16_t r5 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)7U, uint8_t, uint8_t *)) &
+ (int16_t)15)
+ << 6U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)6U, uint8_t, uint8_t *)) >>
+ 2U);
+ int16_t r6 = libcrux_secrets_int_as_i16_f5(
+ (libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)8U, uint8_t, uint8_t *)) &
+ (int16_t)63)
+ << 4U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)7U, uint8_t, uint8_t *)) >>
+ 4U);
+ int16_t r7 = libcrux_secrets_int_as_i16_f5(
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)9U, uint8_t, uint8_t *))
+ << 2U |
+ libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)8U, uint8_t, uint8_t *)) >>
+ 6U);
+ return (KRML_CLITERAL(int16_t_x8){.fst = r0,
+ .snd = r1,
+ .thd = r2,
+ .f3 = r3,
+ .f4 = r4,
+ .f5 = r5,
+ .f6 = r6,
+ .f7 = r7});
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_serialize_deserialize_10(Eurydice_slice bytes) {
+ int16_t_x8 v0_7 = libcrux_ml_kem_vector_portable_serialize_deserialize_10_int(
+ Eurydice_slice_subslice3(bytes, (size_t)0U, (size_t)10U, uint8_t *));
+ int16_t_x8 v8_15 =
+ libcrux_ml_kem_vector_portable_serialize_deserialize_10_int(
+ Eurydice_slice_subslice3(bytes, (size_t)10U, (size_t)20U, uint8_t *));
+ return (
+ KRML_CLITERAL(libcrux_ml_kem_vector_portable_vector_type_PortableVector){
+ .elements = {v0_7.fst, v0_7.snd, v0_7.thd, v0_7.f3, v0_7.f4, v0_7.f5,
+ v0_7.f6, v0_7.f7, v8_15.fst, v8_15.snd, v8_15.thd,
+ v8_15.f3, v8_15.f4, v8_15.f5, v8_15.f6, v8_15.f7}});
+}
+
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_10(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_serialize_deserialize_10(
+ libcrux_secrets_int_classify_public_classify_ref_9b_90(a));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_10_b8(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_deserialize_10(a);
+}
+
+typedef struct uint8_t_x3_s {
+ uint8_t fst;
+ uint8_t snd;
+ uint8_t thd;
+} uint8_t_x3;
+
+static KRML_MUSTINLINE uint8_t_x3
+libcrux_ml_kem_vector_portable_serialize_serialize_12_int(Eurydice_slice v) {
+ uint8_t r0 = libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)0U, int16_t, int16_t *) & (int16_t)255);
+ uint8_t r1 = libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)0U, int16_t, int16_t *) >> 8U |
+ (Eurydice_slice_index(v, (size_t)1U, int16_t, int16_t *) & (int16_t)15)
+ << 4U);
+ uint8_t r2 = libcrux_secrets_int_as_u8_f5(
+ Eurydice_slice_index(v, (size_t)1U, int16_t, int16_t *) >> 4U &
+ (int16_t)255);
+ return (KRML_CLITERAL(uint8_t_x3){.fst = r0, .snd = r1, .thd = r2});
+}
+
+static KRML_MUSTINLINE void
+libcrux_ml_kem_vector_portable_serialize_serialize_12(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector v,
+ uint8_t ret[24U]) {
+ uint8_t_x3 r0_2 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)0U, (size_t)2U,
+ int16_t *));
+ uint8_t_x3 r3_5 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)2U, (size_t)4U,
+ int16_t *));
+ uint8_t_x3 r6_8 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)4U, (size_t)6U,
+ int16_t *));
+ uint8_t_x3 r9_11 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)6U, (size_t)8U,
+ int16_t *));
+ uint8_t_x3 r12_14 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)8U, (size_t)10U,
+ int16_t *));
+ uint8_t_x3 r15_17 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)10U, (size_t)12U,
+ int16_t *));
+ uint8_t_x3 r18_20 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)12U, (size_t)14U,
+ int16_t *));
+ uint8_t_x3 r21_23 = libcrux_ml_kem_vector_portable_serialize_serialize_12_int(
+ Eurydice_array_to_subslice3(v.elements, (size_t)14U, (size_t)16U,
+ int16_t *));
+ ret[0U] = r0_2.fst;
+ ret[1U] = r0_2.snd;
+ ret[2U] = r0_2.thd;
+ ret[3U] = r3_5.fst;
+ ret[4U] = r3_5.snd;
+ ret[5U] = r3_5.thd;
+ ret[6U] = r6_8.fst;
+ ret[7U] = r6_8.snd;
+ ret[8U] = r6_8.thd;
+ ret[9U] = r9_11.fst;
+ ret[10U] = r9_11.snd;
+ ret[11U] = r9_11.thd;
+ ret[12U] = r12_14.fst;
+ ret[13U] = r12_14.snd;
+ ret[14U] = r12_14.thd;
+ ret[15U] = r15_17.fst;
+ ret[16U] = r15_17.snd;
+ ret[17U] = r15_17.thd;
+ ret[18U] = r18_20.fst;
+ ret[19U] = r18_20.snd;
+ ret[20U] = r18_20.thd;
+ ret[21U] = r21_23.fst;
+ ret[22U] = r21_23.snd;
+ ret[23U] = r21_23.thd;
+}
+
+static inline void libcrux_ml_kem_vector_portable_serialize_12(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[24U]) {
+ uint8_t ret0[24U];
+ libcrux_ml_kem_vector_portable_serialize_serialize_12(a, ret0);
+ libcrux_secrets_int_public_integers_declassify_d8_d2(ret0, ret);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline void libcrux_ml_kem_vector_portable_serialize_12_b8(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ uint8_t ret[24U]) {
+ libcrux_ml_kem_vector_portable_serialize_12(a, ret);
+}
+
+typedef struct int16_t_x2_s {
+ int16_t fst;
+ int16_t snd;
+} int16_t_x2;
+
+static KRML_MUSTINLINE int16_t_x2
+libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice bytes) {
+ int16_t byte0 = libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)0U, uint8_t, uint8_t *));
+ int16_t byte1 = libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)1U, uint8_t, uint8_t *));
+ int16_t byte2 = libcrux_secrets_int_as_i16_59(
+ Eurydice_slice_index(bytes, (size_t)2U, uint8_t, uint8_t *));
+ int16_t r0 = (byte1 & (int16_t)15) << 8U | (byte0 & (int16_t)255);
+ int16_t r1 = byte2 << 4U | (byte1 >> 4U & (int16_t)15);
+ return (KRML_CLITERAL(int16_t_x2){.fst = r0, .snd = r1});
+}
+
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_serialize_deserialize_12(Eurydice_slice bytes) {
+ int16_t_x2 v0_1 = libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)0U, (size_t)3U, uint8_t *));
+ int16_t_x2 v2_3 = libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)3U, (size_t)6U, uint8_t *));
+ int16_t_x2 v4_5 = libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)6U, (size_t)9U, uint8_t *));
+ int16_t_x2 v6_7 = libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)9U, (size_t)12U, uint8_t *));
+ int16_t_x2 v8_9 = libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)12U, (size_t)15U, uint8_t *));
+ int16_t_x2 v10_11 =
+ libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)15U, (size_t)18U, uint8_t *));
+ int16_t_x2 v12_13 =
+ libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)18U, (size_t)21U, uint8_t *));
+ int16_t_x2 v14_15 =
+ libcrux_ml_kem_vector_portable_serialize_deserialize_12_int(
+ Eurydice_slice_subslice3(bytes, (size_t)21U, (size_t)24U, uint8_t *));
+ return (
+ KRML_CLITERAL(libcrux_ml_kem_vector_portable_vector_type_PortableVector){
+ .elements = {v0_1.fst, v0_1.snd, v2_3.fst, v2_3.snd, v4_5.fst,
+ v4_5.snd, v6_7.fst, v6_7.snd, v8_9.fst, v8_9.snd,
+ v10_11.fst, v10_11.snd, v12_13.fst, v12_13.snd,
+ v14_15.fst, v14_15.snd}});
+}
+
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_12(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_serialize_deserialize_12(
+ libcrux_secrets_int_classify_public_classify_ref_9b_90(a));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_deserialize_12_b8(Eurydice_slice a) {
+ return libcrux_ml_kem_vector_portable_deserialize_12(a);
+}
+
+static KRML_MUSTINLINE size_t
+libcrux_ml_kem_vector_portable_sampling_rej_sample(Eurydice_slice a,
+ Eurydice_slice result) {
+ size_t sampled = (size_t)0U;
+ for (size_t i = (size_t)0U; i < Eurydice_slice_len(a, uint8_t) / (size_t)3U;
+ i++) {
+ size_t i0 = i;
+ int16_t b1 = (int16_t)Eurydice_slice_index(a, i0 * (size_t)3U + (size_t)0U,
+ uint8_t, uint8_t *);
+ int16_t b2 = (int16_t)Eurydice_slice_index(a, i0 * (size_t)3U + (size_t)1U,
+ uint8_t, uint8_t *);
+ int16_t b3 = (int16_t)Eurydice_slice_index(a, i0 * (size_t)3U + (size_t)2U,
+ uint8_t, uint8_t *);
+ int16_t d1 = (b2 & (int16_t)15) << 8U | b1;
+ int16_t d2 = b3 << 4U | b2 >> 4U;
+ if (d1 < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS) {
+ if (sampled < (size_t)16U) {
+ Eurydice_slice_index(result, sampled, int16_t, int16_t *) = d1;
+ sampled++;
+ }
+ }
+ if (d2 < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS) {
+ if (sampled < (size_t)16U) {
+ Eurydice_slice_index(result, sampled, int16_t, int16_t *) = d2;
+ sampled++;
+ }
+ }
+ }
+ return sampled;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+static inline size_t libcrux_ml_kem_vector_portable_rej_sample_b8(
+ Eurydice_slice a, Eurydice_slice out) {
+ return libcrux_ml_kem_vector_portable_sampling_rej_sample(a, out);
+}
+
+#define LIBCRUX_ML_KEM_MLKEM768_VECTOR_U_COMPRESSION_FACTOR ((size_t)10U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_C1_BLOCK_SIZE \
+ (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT * \
+ LIBCRUX_ML_KEM_MLKEM768_VECTOR_U_COMPRESSION_FACTOR / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_RANK ((size_t)3U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_C1_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_C1_BLOCK_SIZE * LIBCRUX_ML_KEM_MLKEM768_RANK)
+
+#define LIBCRUX_ML_KEM_MLKEM768_VECTOR_V_COMPRESSION_FACTOR ((size_t)4U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_C2_SIZE \
+ (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT * \
+ LIBCRUX_ML_KEM_MLKEM768_VECTOR_V_COMPRESSION_FACTOR / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_CIPHERTEXT_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_C1_SIZE + LIBCRUX_ML_KEM_MLKEM768_C2_SIZE)
+
+#define LIBCRUX_ML_KEM_MLKEM768_T_AS_NTT_ENCODED_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_RANK * \
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT * \
+ LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_COEFFICIENT / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_PUBLIC_KEY_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_T_AS_NTT_ENCODED_SIZE + (size_t)32U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_SECRET_KEY_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_RANK * \
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT * \
+ LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_COEFFICIENT / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_ETA1 ((size_t)2U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_ETA1_RANDOMNESS_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_ETA1 * (size_t)64U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_ETA2 ((size_t)2U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_ETA2_RANDOMNESS_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_ETA2 * (size_t)64U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_IMPLICIT_REJECTION_HASH_INPUT_SIZE \
+ (LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE + \
+ LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_CIPHERTEXT_SIZE)
+
+typedef libcrux_ml_kem_types_MlKemPrivateKey_d9
+ libcrux_ml_kem_mlkem768_MlKem768PrivateKey;
+
+typedef libcrux_ml_kem_types_MlKemPublicKey_30
+ libcrux_ml_kem_mlkem768_MlKem768PublicKey;
+
+#define LIBCRUX_ML_KEM_MLKEM768_RANKED_BYTES_PER_RING_ELEMENT \
+ (LIBCRUX_ML_KEM_MLKEM768_RANK * \
+ LIBCRUX_ML_KEM_CONSTANTS_BITS_PER_RING_ELEMENT / (size_t)8U)
+
+#define LIBCRUX_ML_KEM_MLKEM768_SECRET_KEY_SIZE \
+ (LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_SECRET_KEY_SIZE + \
+ LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_PUBLIC_KEY_SIZE + \
+ LIBCRUX_ML_KEM_CONSTANTS_H_DIGEST_SIZE + \
+ LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE)
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.PolynomialRingElement
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+
+*/
+typedef struct libcrux_ml_kem_polynomial_PolynomialRingElement_1d_s {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficients[16U];
+} libcrux_ml_kem_polynomial_PolynomialRingElement_1d;
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.ind_cpa.unpacked.IndCpaPrivateKeyUnpacked with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- $3size_t
+*/
+typedef struct libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0_s {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d secret_as_ntt[3U];
+} libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0;
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.ZERO_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_ZERO_d6_ea(void) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d lit;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ repeat_expression[16U];
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ repeat_expression[i] = libcrux_ml_kem_vector_portable_ZERO_b8();
+ }
+ memcpy(lit.coefficients, repeat_expression,
+ (size_t)16U *
+ sizeof(libcrux_ml_kem_vector_portable_vector_type_PortableVector));
+ return lit;
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]> for libcrux_ml_kem::ind_cpa::decrypt::closure[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.decrypt.call_mut_0b
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- VECTOR_U_ENCODED_SIZE= 960
+- U_COMPRESSION_FACTOR= 10
+- V_COMPRESSION_FACTOR= 4
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_ind_cpa_decrypt_call_mut_0b_42(void **_, size_t tupled_args) {
+ return libcrux_ml_kem_polynomial_ZERO_d6_ea();
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_to_uncompressed_ring_element with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_to_uncompressed_ring_element_ea(
+ Eurydice_slice serialized) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(serialized, uint8_t) / (size_t)24U; i++) {
+ size_t i0 = i;
+ Eurydice_slice bytes =
+ Eurydice_slice_subslice3(serialized, i0 * (size_t)24U,
+ i0 * (size_t)24U + (size_t)24U, uint8_t *);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_deserialize_12_b8(bytes);
+ re.coefficients[i0] = uu____0;
+ }
+ return re;
+}
+
+/**
+ Call [`deserialize_to_uncompressed_ring_element`] for each ring element.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.deserialize_vector
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_deserialize_vector_1b(
+ Eurydice_slice secret_key,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *secret_as_ntt) {
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d uu____0 =
+ libcrux_ml_kem_serialize_deserialize_to_uncompressed_ring_element_ea(
+ Eurydice_slice_subslice3(
+ secret_key,
+ i0 * LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT,
+ (i0 + (size_t)1U) *
+ LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT,
+ uint8_t *));
+ secret_as_ntt[i0] = uu____0;
+ }
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]> for
+libcrux_ml_kem::ind_cpa::deserialize_then_decompress_u::closure[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.ind_cpa.deserialize_then_decompress_u.call_mut_35 with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- U_COMPRESSION_FACTOR= 10
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_ind_cpa_deserialize_then_decompress_u_call_mut_35_6c(
+ void **_, size_t tupled_args) {
+ return libcrux_ml_kem_polynomial_ZERO_d6_ea();
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.vector.portable.compress.decompress_ciphertext_coefficient with
+const generics
+- COEFFICIENT_BITS= 10
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_decompress_ciphertext_coefficient_ef(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ int32_t decompressed =
+ libcrux_secrets_int_as_i32_f5(a.elements[i0]) *
+ libcrux_secrets_int_as_i32_f5(
+ libcrux_secrets_int_public_integers_classify_27_39(
+ LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS));
+ decompressed = (decompressed << 1U) + ((int32_t)1 << (uint32_t)(int32_t)10);
+ decompressed = decompressed >> (uint32_t)((int32_t)10 + (int32_t)1);
+ a.elements[i0] = libcrux_secrets_int_as_i16_36(decompressed);
+ }
+ return a;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.vector.portable.decompress_ciphertext_coefficient_b8 with const
+generics
+- COEFFICIENT_BITS= 10
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_decompress_ciphertext_coefficient_b8_ef(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_decompress_ciphertext_coefficient_ef(
+ a);
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_then_decompress_10 with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_then_decompress_10_ea(
+ Eurydice_slice serialized) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(serialized, uint8_t) / (size_t)20U; i++) {
+ size_t i0 = i;
+ Eurydice_slice bytes =
+ Eurydice_slice_subslice3(serialized, i0 * (size_t)20U,
+ i0 * (size_t)20U + (size_t)20U, uint8_t *);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_vector_portable_deserialize_10_b8(bytes);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_decompress_ciphertext_coefficient_b8_ef(
+ coefficient);
+ re.coefficients[i0] = uu____0;
+ }
+ return re;
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_then_decompress_ring_element_u with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- COMPRESSION_FACTOR= 10
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_then_decompress_ring_element_u_0a(
+ Eurydice_slice serialized) {
+ return libcrux_ml_kem_serialize_deserialize_then_decompress_10_ea(serialized);
+}
+
+typedef struct libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2_s {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector fst;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector snd;
+} libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2;
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_layer_int_vec_step
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2
+ libcrux_ml_kem_ntt_ntt_layer_int_vec_step_ea(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector b,
+ int16_t zeta_r) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector t =
+ libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(b,
+ zeta_r);
+ b = libcrux_ml_kem_vector_portable_sub_b8(a, &t);
+ a = libcrux_ml_kem_vector_portable_add_b8(a, &t);
+ return (KRML_CLITERAL(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2){.fst = a,
+ .snd = b});
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_at_layer_4_plus
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re,
+ size_t layer, size_t _initial_coefficient_bound) {
+ size_t step = (size_t)1U << (uint32_t)layer;
+ for (size_t i0 = (size_t)0U; i0 < (size_t)128U >> (uint32_t)layer; i0++) {
+ size_t round = i0;
+ zeta_i[0U] = zeta_i[0U] + (size_t)1U;
+ size_t offset = round * step * (size_t)2U;
+ size_t offset_vec = offset / (size_t)16U;
+ size_t step_vec = step / (size_t)16U;
+ for (size_t i = offset_vec; i < offset_vec + step_vec; i++) {
+ size_t j = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2 uu____0 =
+ libcrux_ml_kem_ntt_ntt_layer_int_vec_step_ea(
+ re->coefficients[j], re->coefficients[j + step_vec],
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U]));
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector x = uu____0.fst;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector y = uu____0.snd;
+ re->coefficients[j] = x;
+ re->coefficients[j + step_vec] = y;
+ }
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_at_layer_3
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_at_layer_3_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re,
+ size_t _initial_coefficient_bound) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] + (size_t)1U;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_ntt_layer_3_step_b8(
+ re->coefficients[round],
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U]));
+ re->coefficients[round] = uu____0;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_at_layer_2
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_at_layer_2_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re,
+ size_t _initial_coefficient_bound) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] + (size_t)1U;
+ re->coefficients[round] =
+ libcrux_ml_kem_vector_portable_ntt_layer_2_step_b8(
+ re->coefficients[round], libcrux_ml_kem_polynomial_zeta(zeta_i[0U]),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] + (size_t)1U));
+ zeta_i[0U] = zeta_i[0U] + (size_t)1U;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_at_layer_1
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_at_layer_1_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re,
+ size_t _initial_coefficient_bound) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] + (size_t)1U;
+ re->coefficients[round] =
+ libcrux_ml_kem_vector_portable_ntt_layer_1_step_b8(
+ re->coefficients[round], libcrux_ml_kem_polynomial_zeta(zeta_i[0U]),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] + (size_t)1U),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] + (size_t)2U),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] + (size_t)3U));
+ zeta_i[0U] = zeta_i[0U] + (size_t)3U;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.poly_barrett_reduce
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_poly_barrett_reduce_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_barrett_reduce_b8(
+ myself->coefficients[i0]);
+ myself->coefficients[i0] = uu____0;
+ }
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.poly_barrett_reduce_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_poly_barrett_reduce_d6_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self) {
+ libcrux_ml_kem_polynomial_poly_barrett_reduce_ea(self);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_vector_u
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- VECTOR_U_COMPRESSION_FACTOR= 10
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_vector_u_0a(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ size_t zeta_i = (size_t)0U;
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)7U,
+ (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)6U,
+ (size_t)2U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)5U,
+ (size_t)3U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)4U,
+ (size_t)4U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_3_ea(&zeta_i, re, (size_t)5U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_2_ea(&zeta_i, re, (size_t)6U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_1_ea(&zeta_i, re, (size_t)7U * (size_t)3328U);
+ libcrux_ml_kem_polynomial_poly_barrett_reduce_d6_ea(re);
+}
+
+/**
+ Call [`deserialize_then_decompress_ring_element_u`] on each ring element
+ in the `ciphertext`.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.deserialize_then_decompress_u
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- U_COMPRESSION_FACTOR= 10
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_ind_cpa_deserialize_then_decompress_u_6c(
+ uint8_t *ciphertext,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d ret[3U]) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d u_as_ntt[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ u_as_ntt[i] =
+ libcrux_ml_kem_ind_cpa_deserialize_then_decompress_u_call_mut_35_6c(
+ &lvalue, i);
+ }
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(
+ Eurydice_array_to_slice((size_t)1088U, ciphertext, uint8_t),
+ uint8_t) /
+ (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT *
+ (size_t)10U / (size_t)8U);
+ i++) {
+ size_t i0 = i;
+ Eurydice_slice u_bytes = Eurydice_array_to_subslice3(
+ ciphertext,
+ i0 * (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT *
+ (size_t)10U / (size_t)8U),
+ i0 * (LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT *
+ (size_t)10U / (size_t)8U) +
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT *
+ (size_t)10U / (size_t)8U,
+ uint8_t *);
+ u_as_ntt[i0] =
+ libcrux_ml_kem_serialize_deserialize_then_decompress_ring_element_u_0a(
+ u_bytes);
+ libcrux_ml_kem_ntt_ntt_vector_u_0a(&u_as_ntt[i0]);
+ }
+ memcpy(
+ ret, u_as_ntt,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.vector.portable.compress.decompress_ciphertext_coefficient with
+const generics
+- COEFFICIENT_BITS= 4
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_decompress_ciphertext_coefficient_d1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ int32_t decompressed =
+ libcrux_secrets_int_as_i32_f5(a.elements[i0]) *
+ libcrux_secrets_int_as_i32_f5(
+ libcrux_secrets_int_public_integers_classify_27_39(
+ LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_MODULUS));
+ decompressed = (decompressed << 1U) + ((int32_t)1 << (uint32_t)(int32_t)4);
+ decompressed = decompressed >> (uint32_t)((int32_t)4 + (int32_t)1);
+ a.elements[i0] = libcrux_secrets_int_as_i16_36(decompressed);
+ }
+ return a;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.vector.portable.decompress_ciphertext_coefficient_b8 with const
+generics
+- COEFFICIENT_BITS= 4
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_decompress_ciphertext_coefficient_b8_d1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_decompress_ciphertext_coefficient_d1(
+ a);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.serialize.deserialize_then_decompress_4
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_then_decompress_4_ea(
+ Eurydice_slice serialized) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(serialized, uint8_t) / (size_t)8U; i++) {
+ size_t i0 = i;
+ Eurydice_slice bytes = Eurydice_slice_subslice3(
+ serialized, i0 * (size_t)8U, i0 * (size_t)8U + (size_t)8U, uint8_t *);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_vector_portable_deserialize_4_b8(bytes);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_decompress_ciphertext_coefficient_b8_d1(
+ coefficient);
+ re.coefficients[i0] = uu____0;
+ }
+ return re;
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_then_decompress_ring_element_v with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+- COMPRESSION_FACTOR= 4
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_then_decompress_ring_element_v_89(
+ Eurydice_slice serialized) {
+ return libcrux_ml_kem_serialize_deserialize_then_decompress_4_ea(serialized);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.ZERO
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_ZERO_ea(void) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d lit;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ repeat_expression[16U];
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ repeat_expression[i] = libcrux_ml_kem_vector_portable_ZERO_b8();
+ }
+ memcpy(lit.coefficients, repeat_expression,
+ (size_t)16U *
+ sizeof(libcrux_ml_kem_vector_portable_vector_type_PortableVector));
+ return lit;
+}
+
+/**
+ Given two `KyberPolynomialRingElement`s in their NTT representations,
+ compute their product. Given two polynomials in the NTT domain `f^` and `ĵ`,
+ the `iᵗʰ` coefficient of the product `k̂` is determined by the calculation:
+
+ ```plaintext
+ ĥ[2·i] + ĥ[2·i + 1]X = (f^[2·i] + f^[2·i + 1]X)·(ĝ[2·i] + ĝ[2·i + 1]X) mod (X²
+ - ζ^(2·BitRev₇(i) + 1))
+ ```
+
+ This function almost implements Algorithm 10 of the
+ NIST FIPS 203 standard, which is reproduced below:
+
+ ```plaintext
+ Input: Two arrays fˆ ∈ ℤ₂₅₆ and ĝ ∈ ℤ₂₅₆.
+ Output: An array ĥ ∈ ℤq.
+
+ for(i ← 0; i < 128; i++)
+ (ĥ[2i], ĥ[2i+1]) ← BaseCaseMultiply(fˆ[2i], fˆ[2i+1], ĝ[2i], ĝ[2i+1],
+ ζ^(2·BitRev₇(i) + 1)) end for return ĥ
+ ```
+ We say "almost" because the coefficients of the ring element output by
+ this function are in the Montgomery domain.
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.ntt_multiply
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_ntt_multiply_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *rhs) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d out =
+ libcrux_ml_kem_polynomial_ZERO_ea();
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_ntt_multiply_b8(
+ &myself->coefficients[i0], &rhs->coefficients[i0],
+ libcrux_ml_kem_polynomial_zeta((size_t)64U + (size_t)4U * i0),
+ libcrux_ml_kem_polynomial_zeta((size_t)64U + (size_t)4U * i0 +
+ (size_t)1U),
+ libcrux_ml_kem_polynomial_zeta((size_t)64U + (size_t)4U * i0 +
+ (size_t)2U),
+ libcrux_ml_kem_polynomial_zeta((size_t)64U + (size_t)4U * i0 +
+ (size_t)3U));
+ out.coefficients[i0] = uu____0;
+ }
+ return out;
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.ntt_multiply_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_ntt_multiply_d6_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *rhs) {
+ return libcrux_ml_kem_polynomial_ntt_multiply_ea(self, rhs);
+}
+
+/**
+ Given two polynomial ring elements `lhs` and `rhs`, compute the pointwise
+ sum of their constituent coefficients.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_to_ring_element
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_add_to_ring_element_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *rhs) {
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(
+ Eurydice_array_to_slice(
+ (size_t)16U, myself->coefficients,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector),
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector);
+ i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_add_b8(myself->coefficients[i0],
+ &rhs->coefficients[i0]);
+ myself->coefficients[i0] = uu____0;
+ }
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_to_ring_element_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_add_to_ring_element_d6_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *rhs) {
+ libcrux_ml_kem_polynomial_add_to_ring_element_1b(self, rhs);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.invert_ntt.invert_ntt_at_layer_1
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_1_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] - (size_t)1U;
+ re->coefficients[round] =
+ libcrux_ml_kem_vector_portable_inv_ntt_layer_1_step_b8(
+ re->coefficients[round], libcrux_ml_kem_polynomial_zeta(zeta_i[0U]),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] - (size_t)1U),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] - (size_t)2U),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] - (size_t)3U));
+ zeta_i[0U] = zeta_i[0U] - (size_t)3U;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.invert_ntt.invert_ntt_at_layer_2
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_2_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] - (size_t)1U;
+ re->coefficients[round] =
+ libcrux_ml_kem_vector_portable_inv_ntt_layer_2_step_b8(
+ re->coefficients[round], libcrux_ml_kem_polynomial_zeta(zeta_i[0U]),
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U] - (size_t)1U));
+ zeta_i[0U] = zeta_i[0U] - (size_t)1U;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.invert_ntt.invert_ntt_at_layer_3
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_3_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t round = i;
+ zeta_i[0U] = zeta_i[0U] - (size_t)1U;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_inv_ntt_layer_3_step_b8(
+ re->coefficients[round],
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U]));
+ re->coefficients[round] = uu____0;
+ }
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.invert_ntt.inv_ntt_layer_int_vec_step_reduce with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2
+ libcrux_ml_kem_invert_ntt_inv_ntt_layer_int_vec_step_reduce_ea(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a,
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector b,
+ int16_t zeta_r) {
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a_minus_b =
+ libcrux_ml_kem_vector_portable_sub_b8(b, &a);
+ a = libcrux_ml_kem_vector_portable_barrett_reduce_b8(
+ libcrux_ml_kem_vector_portable_add_b8(a, &b));
+ b = libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(
+ a_minus_b, zeta_r);
+ return (KRML_CLITERAL(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2){.fst = a,
+ .snd = b});
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.invert_ntt.invert_ntt_at_layer_4_plus
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_4_plus_ea(
+ size_t *zeta_i, libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re,
+ size_t layer) {
+ size_t step = (size_t)1U << (uint32_t)layer;
+ for (size_t i0 = (size_t)0U; i0 < (size_t)128U >> (uint32_t)layer; i0++) {
+ size_t round = i0;
+ zeta_i[0U] = zeta_i[0U] - (size_t)1U;
+ size_t offset = round * step * (size_t)2U;
+ size_t offset_vec =
+ offset / LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR;
+ size_t step_vec =
+ step / LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR;
+ for (size_t i = offset_vec; i < offset_vec + step_vec; i++) {
+ size_t j = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector_x2 uu____0 =
+ libcrux_ml_kem_invert_ntt_inv_ntt_layer_int_vec_step_reduce_ea(
+ re->coefficients[j], re->coefficients[j + step_vec],
+ libcrux_ml_kem_polynomial_zeta(zeta_i[0U]));
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector x = uu____0.fst;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector y = uu____0.snd;
+ re->coefficients[j] = x;
+ re->coefficients[j + step_vec] = y;
+ }
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.invert_ntt.invert_ntt_montgomery
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_invert_ntt_invert_ntt_montgomery_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ size_t zeta_i =
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT / (size_t)2U;
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_1_ea(&zeta_i, re);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_2_ea(&zeta_i, re);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_3_ea(&zeta_i, re);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_4_plus_ea(&zeta_i, re,
+ (size_t)4U);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_4_plus_ea(&zeta_i, re,
+ (size_t)5U);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_4_plus_ea(&zeta_i, re,
+ (size_t)6U);
+ libcrux_ml_kem_invert_ntt_invert_ntt_at_layer_4_plus_ea(&zeta_i, re,
+ (size_t)7U);
+ libcrux_ml_kem_polynomial_poly_barrett_reduce_d6_ea(re);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.subtract_reduce
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_subtract_reduce_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d b) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ coefficient_normal_form =
+ libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(
+ b.coefficients[i0], (int16_t)1441);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector diff =
+ libcrux_ml_kem_vector_portable_sub_b8(myself->coefficients[i0],
+ &coefficient_normal_form);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector red =
+ libcrux_ml_kem_vector_portable_barrett_reduce_b8(diff);
+ b.coefficients[i0] = red;
+ }
+ return b;
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.subtract_reduce_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_subtract_reduce_d6_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d b) {
+ return libcrux_ml_kem_polynomial_subtract_reduce_ea(self, b);
+}
+
+/**
+ The following functions compute various expressions involving
+ vectors and matrices. The computation of these expressions has been
+ abstracted away into these functions in order to save on loop iterations.
+ Compute v − InverseNTT(sᵀ ◦ NTT(u))
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.matrix.compute_message
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_matrix_compute_message_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *v,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *secret_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *u_as_ntt) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d product =
+ libcrux_ml_kem_polynomial_ntt_multiply_d6_ea(&secret_as_ntt[i0],
+ &u_as_ntt[i0]);
+ libcrux_ml_kem_polynomial_add_to_ring_element_d6_1b(&result, &product);
+ }
+ libcrux_ml_kem_invert_ntt_invert_ntt_montgomery_1b(&result);
+ return libcrux_ml_kem_polynomial_subtract_reduce_d6_ea(v, result);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.serialize.to_unsigned_field_modulus
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_serialize_to_unsigned_field_modulus_ea(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_to_unsigned_representative_b8(a);
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.compress_then_serialize_message with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_compress_then_serialize_message_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re, uint8_t ret[32U]) {
+ uint8_t serialized[32U] = {0U};
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_serialize_to_unsigned_field_modulus_ea(
+ re.coefficients[i0]);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ coefficient_compressed =
+ libcrux_ml_kem_vector_portable_compress_1_b8(coefficient);
+ uint8_t bytes[2U];
+ libcrux_ml_kem_vector_portable_serialize_1_b8(coefficient_compressed,
+ bytes);
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(serialized, (size_t)2U * i0,
+ (size_t)2U * i0 + (size_t)2U, uint8_t *),
+ Eurydice_array_to_slice((size_t)2U, bytes, uint8_t), uint8_t);
+ }
+ memcpy(ret, serialized, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+ This function implements Algorithm 14 of the
+ NIST FIPS 203 specification; this is the Kyber CPA-PKE decryption algorithm.
+
+ Algorithm 14 is reproduced below:
+
+ ```plaintext
+ Input: decryption key dkₚₖₑ ∈ 𝔹^{384k}.
+ Input: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}.
+ Output: message m ∈ 𝔹^{32}.
+
+ c₁ ← c[0 : 32dᵤk]
+ c₂ ← c[32dᵤk : 32(dᵤk + dᵥ)]
+ u ← Decompress_{dᵤ}(ByteDecode_{dᵤ}(c₁))
+ v ← Decompress_{dᵥ}(ByteDecode_{dᵥ}(c₂))
+ ŝ ← ByteDecode₁₂(dkₚₖₑ)
+ w ← v - NTT-¹(ŝᵀ ◦ NTT(u))
+ m ← ByteEncode₁(Compress₁(w))
+ return m
+ ```
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.decrypt_unpacked
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- VECTOR_U_ENCODED_SIZE= 960
+- U_COMPRESSION_FACTOR= 10
+- V_COMPRESSION_FACTOR= 4
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_decrypt_unpacked_42(
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0 *secret_key,
+ uint8_t *ciphertext, uint8_t ret[32U]) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d u_as_ntt[3U];
+ libcrux_ml_kem_ind_cpa_deserialize_then_decompress_u_6c(ciphertext, u_as_ntt);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d v =
+ libcrux_ml_kem_serialize_deserialize_then_decompress_ring_element_v_89(
+ Eurydice_array_to_subslice_from((size_t)1088U, ciphertext,
+ (size_t)960U, uint8_t, size_t,
+ uint8_t[]));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d message =
+ libcrux_ml_kem_matrix_compute_message_1b(&v, secret_key->secret_as_ntt,
+ u_as_ntt);
+ uint8_t ret0[32U];
+ libcrux_ml_kem_serialize_compress_then_serialize_message_ea(message, ret0);
+ memcpy(ret, ret0, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.decrypt
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- VECTOR_U_ENCODED_SIZE= 960
+- U_COMPRESSION_FACTOR= 10
+- V_COMPRESSION_FACTOR= 4
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_decrypt_42(
+ Eurydice_slice secret_key, uint8_t *ciphertext, uint8_t ret[32U]) {
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0
+ secret_key_unpacked;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d ret0[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ ret0[i] = libcrux_ml_kem_ind_cpa_decrypt_call_mut_0b_42(&lvalue, i);
+ }
+ memcpy(
+ secret_key_unpacked.secret_as_ntt, ret0,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ libcrux_ml_kem_ind_cpa_deserialize_vector_1b(
+ secret_key, secret_key_unpacked.secret_as_ntt);
+ uint8_t ret1[32U];
+ libcrux_ml_kem_ind_cpa_decrypt_unpacked_42(&secret_key_unpacked, ciphertext,
+ ret1);
+ memcpy(ret, ret1, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.G_4a
+with const generics
+- K= 3
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_G_4a_e0(
+ Eurydice_slice input, uint8_t ret[64U]) {
+ libcrux_ml_kem_hash_functions_portable_G(input, ret);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRF
+with const generics
+- LEN= 32
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRF_9e(
+ Eurydice_slice input, uint8_t ret[32U]) {
+ uint8_t digest[32U] = {0U};
+ libcrux_sha3_portable_shake256(
+ Eurydice_array_to_slice((size_t)32U, digest, uint8_t), input);
+ memcpy(ret, digest, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRF_4a
+with const generics
+- K= 3
+- LEN= 32
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRF_4a_41(
+ Eurydice_slice input, uint8_t ret[32U]) {
+ libcrux_ml_kem_hash_functions_portable_PRF_9e(input, ret);
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.ind_cpa.unpacked.IndCpaPublicKeyUnpacked with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- $3size_t
+*/
+typedef struct libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0_s {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d t_as_ntt[3U];
+ uint8_t seed_for_A[32U];
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d A[3U][3U];
+} libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0;
+
+/**
+This function found in impl {core::default::Default for
+libcrux_ml_kem::ind_cpa::unpacked::IndCpaPublicKeyUnpacked[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.unpacked.default_8b
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static inline libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0
+libcrux_ml_kem_ind_cpa_unpacked_default_8b_1b(void) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d uu____0[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ uu____0[i] = libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ }
+ uint8_t uu____1[32U] = {0U};
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0 lit;
+ memcpy(
+ lit.t_as_ntt, uu____0,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ memcpy(lit.seed_for_A, uu____1, (size_t)32U * sizeof(uint8_t));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d repeat_expression0[3U][3U];
+ for (size_t i0 = (size_t)0U; i0 < (size_t)3U; i0++) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d repeat_expression[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ repeat_expression[i] = libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ }
+ memcpy(repeat_expression0[i0], repeat_expression,
+ (size_t)3U *
+ sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ }
+ memcpy(lit.A, repeat_expression0,
+ (size_t)3U *
+ sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d[3U]));
+ return lit;
+}
+
+/**
+ Only use with public values.
+
+ This MUST NOT be used with secret inputs, like its caller
+ `deserialize_ring_elements_reduced`.
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_to_reduced_ring_element with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_to_reduced_ring_element_ea(
+ Eurydice_slice serialized) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(serialized, uint8_t) / (size_t)24U; i++) {
+ size_t i0 = i;
+ Eurydice_slice bytes =
+ Eurydice_slice_subslice3(serialized, i0 * (size_t)24U,
+ i0 * (size_t)24U + (size_t)24U, uint8_t *);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_vector_portable_deserialize_12_b8(bytes);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_cond_subtract_3329_b8(coefficient);
+ re.coefficients[i0] = uu____0;
+ }
+ return re;
+}
+
+/**
+ See [deserialize_ring_elements_reduced_out].
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_ring_elements_reduced with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_deserialize_ring_elements_reduced_1b(
+ Eurydice_slice public_key,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *deserialized_pk) {
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(public_key, uint8_t) /
+ LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT;
+ i++) {
+ size_t i0 = i;
+ Eurydice_slice ring_element = Eurydice_slice_subslice3(
+ public_key, i0 * LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT,
+ i0 * LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT +
+ LIBCRUX_ML_KEM_CONSTANTS_BYTES_PER_RING_ELEMENT,
+ uint8_t *);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d uu____0 =
+ libcrux_ml_kem_serialize_deserialize_to_reduced_ring_element_ea(
+ ring_element);
+ deserialized_pk[i0] = uu____0;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PortableHash
+with const generics
+- $3size_t
+*/
+typedef struct libcrux_ml_kem_hash_functions_portable_PortableHash_88_s {
+ libcrux_sha3_generic_keccak_KeccakState_17 shake128_state[3U];
+} libcrux_ml_kem_hash_functions_portable_PortableHash_88;
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_init_absorb_final with const
+generics
+- K= 3
+*/
+static inline libcrux_ml_kem_hash_functions_portable_PortableHash_88
+libcrux_ml_kem_hash_functions_portable_shake128_init_absorb_final_e0(
+ uint8_t (*input)[34U]) {
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 shake128_state;
+ libcrux_sha3_generic_keccak_KeccakState_17 repeat_expression[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ repeat_expression[i] = libcrux_sha3_portable_incremental_shake128_init();
+ }
+ memcpy(shake128_state.shake128_state, repeat_expression,
+ (size_t)3U * sizeof(libcrux_sha3_generic_keccak_KeccakState_17));
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_portable_incremental_shake128_absorb_final(
+ &shake128_state.shake128_state[i0],
+ Eurydice_array_to_slice((size_t)34U, input[i0], uint8_t));
+ }
+ return shake128_state;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_init_absorb_final_4a with const
+generics
+- K= 3
+*/
+static inline libcrux_ml_kem_hash_functions_portable_PortableHash_88
+libcrux_ml_kem_hash_functions_portable_shake128_init_absorb_final_4a_e0(
+ uint8_t (*input)[34U]) {
+ return libcrux_ml_kem_hash_functions_portable_shake128_init_absorb_final_e0(
+ input);
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_squeeze_first_three_blocks with
+const generics
+- K= 3
+*/
+static inline void
+libcrux_ml_kem_hash_functions_portable_shake128_squeeze_first_three_blocks_e0(
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 *st,
+ uint8_t ret[3U][504U]) {
+ uint8_t out[3U][504U] = {{0U}};
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_portable_incremental_shake128_squeeze_first_three_blocks(
+ &st->shake128_state[i0],
+ Eurydice_array_to_slice((size_t)504U, out[i0], uint8_t));
+ }
+ memcpy(ret, out, (size_t)3U * sizeof(uint8_t[504U]));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_squeeze_first_three_blocks_4a
+with const generics
+- K= 3
+*/
+static inline void
+libcrux_ml_kem_hash_functions_portable_shake128_squeeze_first_three_blocks_4a_e0(
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 *self,
+ uint8_t ret[3U][504U]) {
+ libcrux_ml_kem_hash_functions_portable_shake128_squeeze_first_three_blocks_e0(
+ self, ret);
+}
+
+/**
+ If `bytes` contains a set of uniformly random bytes, this function
+ uniformly samples a ring element `â` that is treated as being the NTT
+ representation of the corresponding polynomial `a`.
+
+ Since rejection sampling is used, it is possible the supplied bytes are
+ not enough to sample the element, in which case an `Err` is returned and the
+ caller must try again with a fresh set of bytes.
+
+ This function partially implements Algorithm
+ 6 of the NIST FIPS 203 standard, We say "partially" because this
+ implementation only accepts a finite set of bytes as input and returns an error
+ if the set is not enough; Algorithm 6 of the FIPS 203 standard on the other
+ hand samples from an infinite stream of bytes until the ring element is filled.
+ Algorithm 6 is reproduced below:
+
+ ```plaintext
+ Input: byte stream B ∈ 𝔹*.
+ Output: array â ∈ ℤ₂₅₆.
+
+ i ← 0
+ j ← 0
+ while j < 256 do
+ d₁ ← B[i] + 256·(B[i+1] mod 16)
+ d₂ ← ⌊B[i+1]/16⌋ + 16·B[i+2]
+ if d₁ < q then
+ â[j] ← d₁
+ j ← j + 1
+ end if
+ if d₂ < q and j < 256 then
+ â[j] ← d₂
+ j ← j + 1
+ end if
+ i ← i + 3
+ end while
+ return â
+ ```
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.sampling.sample_from_uniform_distribution_next with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+- N= 504
+*/
+static KRML_MUSTINLINE bool
+libcrux_ml_kem_sampling_sample_from_uniform_distribution_next_89(
+ uint8_t (*randomness)[504U], size_t *sampled_coefficients,
+ int16_t (*out)[272U]) {
+ for (size_t i0 = (size_t)0U; i0 < (size_t)3U; i0++) {
+ size_t i1 = i0;
+ for (size_t i = (size_t)0U; i < (size_t)504U / (size_t)24U; i++) {
+ size_t r = i;
+ if (sampled_coefficients[i1] <
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT) {
+ size_t sampled = libcrux_ml_kem_vector_portable_rej_sample_b8(
+ Eurydice_array_to_subslice3(randomness[i1], r * (size_t)24U,
+ r * (size_t)24U + (size_t)24U,
+ uint8_t *),
+ Eurydice_array_to_subslice3(out[i1], sampled_coefficients[i1],
+ sampled_coefficients[i1] + (size_t)16U,
+ int16_t *));
+ size_t uu____0 = i1;
+ sampled_coefficients[uu____0] = sampled_coefficients[uu____0] + sampled;
+ }
+ }
+ }
+ bool done = true;
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ if (sampled_coefficients[i0] >=
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT) {
+ sampled_coefficients[i0] =
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT;
+ } else {
+ done = false;
+ }
+ }
+ return done;
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_squeeze_next_block with const
+generics
+- K= 3
+*/
+static inline void
+libcrux_ml_kem_hash_functions_portable_shake128_squeeze_next_block_e0(
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 *st,
+ uint8_t ret[3U][168U]) {
+ uint8_t out[3U][168U] = {{0U}};
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_portable_incremental_shake128_squeeze_next_block(
+ &st->shake128_state[i0],
+ Eurydice_array_to_slice((size_t)168U, out[i0], uint8_t));
+ }
+ memcpy(ret, out, (size_t)3U * sizeof(uint8_t[168U]));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.hash_functions.portable.shake128_squeeze_next_block_4a with const
+generics
+- K= 3
+*/
+static inline void
+libcrux_ml_kem_hash_functions_portable_shake128_squeeze_next_block_4a_e0(
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 *self,
+ uint8_t ret[3U][168U]) {
+ libcrux_ml_kem_hash_functions_portable_shake128_squeeze_next_block_e0(self,
+ ret);
+}
+
+/**
+ If `bytes` contains a set of uniformly random bytes, this function
+ uniformly samples a ring element `â` that is treated as being the NTT
+ representation of the corresponding polynomial `a`.
+
+ Since rejection sampling is used, it is possible the supplied bytes are
+ not enough to sample the element, in which case an `Err` is returned and the
+ caller must try again with a fresh set of bytes.
+
+ This function partially implements Algorithm
+ 6 of the NIST FIPS 203 standard, We say "partially" because this
+ implementation only accepts a finite set of bytes as input and returns an error
+ if the set is not enough; Algorithm 6 of the FIPS 203 standard on the other
+ hand samples from an infinite stream of bytes until the ring element is filled.
+ Algorithm 6 is reproduced below:
+
+ ```plaintext
+ Input: byte stream B ∈ 𝔹*.
+ Output: array â ∈ ℤ₂₅₆.
+
+ i ← 0
+ j ← 0
+ while j < 256 do
+ d₁ ← B[i] + 256·(B[i+1] mod 16)
+ d₂ ← ⌊B[i+1]/16⌋ + 16·B[i+2]
+ if d₁ < q then
+ â[j] ← d₁
+ j ← j + 1
+ end if
+ if d₂ < q and j < 256 then
+ â[j] ← d₂
+ j ← j + 1
+ end if
+ i ← i + 3
+ end while
+ return â
+ ```
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.sampling.sample_from_uniform_distribution_next with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+- N= 168
+*/
+static KRML_MUSTINLINE bool
+libcrux_ml_kem_sampling_sample_from_uniform_distribution_next_890(
+ uint8_t (*randomness)[168U], size_t *sampled_coefficients,
+ int16_t (*out)[272U]) {
+ for (size_t i0 = (size_t)0U; i0 < (size_t)3U; i0++) {
+ size_t i1 = i0;
+ for (size_t i = (size_t)0U; i < (size_t)168U / (size_t)24U; i++) {
+ size_t r = i;
+ if (sampled_coefficients[i1] <
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT) {
+ size_t sampled = libcrux_ml_kem_vector_portable_rej_sample_b8(
+ Eurydice_array_to_subslice3(randomness[i1], r * (size_t)24U,
+ r * (size_t)24U + (size_t)24U,
+ uint8_t *),
+ Eurydice_array_to_subslice3(out[i1], sampled_coefficients[i1],
+ sampled_coefficients[i1] + (size_t)16U,
+ int16_t *));
+ size_t uu____0 = i1;
+ sampled_coefficients[uu____0] = sampled_coefficients[uu____0] + sampled;
+ }
+ }
+ }
+ bool done = true;
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ if (sampled_coefficients[i0] >=
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT) {
+ sampled_coefficients[i0] =
+ LIBCRUX_ML_KEM_CONSTANTS_COEFFICIENTS_IN_RING_ELEMENT;
+ } else {
+ done = false;
+ }
+ }
+ return done;
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.from_i16_array
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_from_i16_array_ea(Eurydice_slice a) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result =
+ libcrux_ml_kem_polynomial_ZERO_ea();
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_from_i16_array_b8(
+ Eurydice_slice_subslice3(a, i0 * (size_t)16U,
+ (i0 + (size_t)1U) * (size_t)16U,
+ int16_t *));
+ result.coefficients[i0] = uu____0;
+ }
+ return result;
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.from_i16_array_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_from_i16_array_d6_ea(Eurydice_slice a) {
+ return libcrux_ml_kem_polynomial_from_i16_array_ea(a);
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(@Array),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@2]> for libcrux_ml_kem::sampling::sample_from_xof::closure[TraitClause@0, TraitClause@1, TraitClause@2, TraitClause@3]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.sampling.sample_from_xof.call_mut_e7
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_sampling_sample_from_xof_call_mut_e7_2b(
+ void **_, int16_t tupled_args[272U]) {
+ int16_t s[272U];
+ memcpy(s, tupled_args, (size_t)272U * sizeof(int16_t));
+ return libcrux_ml_kem_polynomial_from_i16_array_d6_ea(
+ Eurydice_array_to_subslice3(s, (size_t)0U, (size_t)256U, int16_t *));
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.sampling.sample_from_xof
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_sampling_sample_from_xof_2b(
+ uint8_t (*seeds)[34U],
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d ret[3U]) {
+ size_t sampled_coefficients[3U] = {0U};
+ int16_t out[3U][272U] = {{0U}};
+ libcrux_ml_kem_hash_functions_portable_PortableHash_88 xof_state =
+ libcrux_ml_kem_hash_functions_portable_shake128_init_absorb_final_4a_e0(
+ seeds);
+ uint8_t randomness0[3U][504U];
+ libcrux_ml_kem_hash_functions_portable_shake128_squeeze_first_three_blocks_4a_e0(
+ &xof_state, randomness0);
+ bool done = libcrux_ml_kem_sampling_sample_from_uniform_distribution_next_89(
+ randomness0, sampled_coefficients, out);
+ while (true) {
+ if (done) {
+ break;
+ } else {
+ uint8_t randomness[3U][168U];
+ libcrux_ml_kem_hash_functions_portable_shake128_squeeze_next_block_4a_e0(
+ &xof_state, randomness);
+ done = libcrux_ml_kem_sampling_sample_from_uniform_distribution_next_890(
+ randomness, sampled_coefficients, out);
+ }
+ }
+ /* Passing arrays by value in Rust generates a copy in C */
+ int16_t copy_of_out[3U][272U];
+ memcpy(copy_of_out, out, (size_t)3U * sizeof(int16_t[272U]));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d ret0[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ ret0[i] = libcrux_ml_kem_sampling_sample_from_xof_call_mut_e7_2b(
+ &lvalue, copy_of_out[i]);
+ }
+ memcpy(
+ ret, ret0,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.matrix.sample_matrix_A
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_matrix_sample_matrix_A_2b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d (*A_transpose)[3U],
+ uint8_t *seed, bool transpose) {
+ for (size_t i0 = (size_t)0U; i0 < (size_t)3U; i0++) {
+ size_t i1 = i0;
+ uint8_t seeds[3U][34U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ core_array__core__clone__Clone_for__Array_T__N___clone(
+ (size_t)34U, seed, seeds[i], uint8_t, void *);
+ }
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t j = i;
+ seeds[j][32U] = (uint8_t)i1;
+ seeds[j][33U] = (uint8_t)j;
+ }
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d sampled[3U];
+ libcrux_ml_kem_sampling_sample_from_xof_2b(seeds, sampled);
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(
+ Eurydice_array_to_slice(
+ (size_t)3U, sampled,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d),
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d);
+ i++) {
+ size_t j = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d sample = sampled[j];
+ if (transpose) {
+ A_transpose[j][i1] = sample;
+ } else {
+ A_transpose[i1][j] = sample;
+ }
+ }
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.build_unpacked_public_key_mut
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- T_AS_NTT_ENCODED_SIZE= 1152
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_ind_cpa_build_unpacked_public_key_mut_3f(
+ Eurydice_slice public_key,
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0
+ *unpacked_public_key) {
+ Eurydice_slice uu____0 = Eurydice_slice_subslice_to(
+ public_key, (size_t)1152U, uint8_t, size_t, uint8_t[]);
+ libcrux_ml_kem_serialize_deserialize_ring_elements_reduced_1b(
+ uu____0, unpacked_public_key->t_as_ntt);
+ Eurydice_slice seed = Eurydice_slice_subslice_from(
+ public_key, (size_t)1152U, uint8_t, size_t, uint8_t[]);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d(*uu____1)[3U] =
+ unpacked_public_key->A;
+ uint8_t ret[34U];
+ libcrux_ml_kem_utils_into_padded_array_b6(seed, ret);
+ libcrux_ml_kem_matrix_sample_matrix_A_2b(uu____1, ret, false);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.build_unpacked_public_key
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- T_AS_NTT_ENCODED_SIZE= 1152
+*/
+static KRML_MUSTINLINE
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0
+ libcrux_ml_kem_ind_cpa_build_unpacked_public_key_3f(
+ Eurydice_slice public_key) {
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0
+ unpacked_public_key = libcrux_ml_kem_ind_cpa_unpacked_default_8b_1b();
+ libcrux_ml_kem_ind_cpa_build_unpacked_public_key_mut_3f(public_key,
+ &unpacked_public_key);
+ return unpacked_public_key;
+}
+
+/**
+A monomorphic instance of K.
+with types libcrux_ml_kem_polynomial_PolynomialRingElement
+libcrux_ml_kem_vector_portable_vector_type_PortableVector[3size_t],
+libcrux_ml_kem_polynomial_PolynomialRingElement
+libcrux_ml_kem_vector_portable_vector_type_PortableVector
+
+*/
+typedef struct tuple_ed_s {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d fst[3U];
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d snd;
+} tuple_ed;
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@2]> for libcrux_ml_kem::ind_cpa::encrypt_c1::closure[TraitClause@0, TraitClause@1, TraitClause@2,
+TraitClause@3]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt_c1.call_mut_f1
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- C1_LEN= 960
+- U_COMPRESSION_FACTOR= 10
+- BLOCK_LEN= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_ind_cpa_encrypt_c1_call_mut_f1_85(void **_, size_t tupled_args) {
+ return libcrux_ml_kem_polynomial_ZERO_d6_ea();
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRFxN
+with const generics
+- K= 3
+- LEN= 128
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRFxN_41(
+ uint8_t (*input)[33U], uint8_t ret[3U][128U]) {
+ uint8_t out[3U][128U] = {{0U}};
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_sha3_portable_shake256(
+ Eurydice_array_to_slice((size_t)128U, out[i0], uint8_t),
+ Eurydice_array_to_slice((size_t)33U, input[i0], uint8_t));
+ }
+ memcpy(ret, out, (size_t)3U * sizeof(uint8_t[128U]));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRFxN_4a
+with const generics
+- K= 3
+- LEN= 128
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRFxN_4a_41(
+ uint8_t (*input)[33U], uint8_t ret[3U][128U]) {
+ libcrux_ml_kem_hash_functions_portable_PRFxN_41(input, ret);
+}
+
+/**
+ Given a series of uniformly random bytes in `randomness`, for some number
+ `eta`, the `sample_from_binomial_distribution_{eta}` functions sample a ring
+ element from a binomial distribution centered at 0 that uses two sets of `eta`
+ coin flips. If, for example, `eta = ETA`, each ring coefficient is a value `v`
+ such such that `v ∈ {-ETA, -ETA + 1, ..., 0, ..., ETA + 1, ETA}` and:
+
+ ```plaintext
+ - If v < 0, Pr[v] = Pr[-v]
+ - If v >= 0, Pr[v] = BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) / 2 ^ (2 * ETA)
+ ```
+
+ The values `v < 0` are mapped to the appropriate `KyberFieldElement`.
+
+ The expected value is:
+
+ ```plaintext
+ E[X] = (-ETA)Pr[-ETA] + (-(ETA - 1))Pr[-(ETA - 1)] + ... + (ETA - 1)Pr[ETA - 1]
+ + (ETA)Pr[ETA] = 0 since Pr[-v] = Pr[v] when v < 0.
+ ```
+
+ And the variance is:
+
+ ```plaintext
+ Var(X) = E[(X - E[X])^2]
+ = E[X^2]
+ = sum_(v=-ETA to ETA)v^2 * (BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) /
+ 2^(2 * ETA)) = ETA / 2
+ ```
+
+ This function implements Algorithm 7 of the NIST FIPS 203
+ standard, which is reproduced below:
+
+ ```plaintext
+ Input: byte array B ∈ 𝔹^{64η}.
+ Output: array f ∈ ℤ₂₅₆.
+
+ b ← BytesToBits(B)
+ for (i ← 0; i < 256; i++)
+ x ← ∑(j=0 to η - 1) b[2iη + j]
+ y ← ∑(j=0 to η - 1) b[2iη + η + j]
+ f[i] ← x−y mod q
+ end for
+ return f
+ ```
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.sampling.sample_from_binomial_distribution_2 with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_sampling_sample_from_binomial_distribution_2_ea(
+ Eurydice_slice randomness) {
+ int16_t sampled_i16s[256U] = {0U};
+ for (size_t i0 = (size_t)0U;
+ i0 < Eurydice_slice_len(randomness, uint8_t) / (size_t)4U; i0++) {
+ size_t chunk_number = i0;
+ Eurydice_slice byte_chunk = Eurydice_slice_subslice3(
+ randomness, chunk_number * (size_t)4U,
+ chunk_number * (size_t)4U + (size_t)4U, uint8_t *);
+ uint32_t random_bits_as_u32 =
+ (((uint32_t)Eurydice_slice_index(byte_chunk, (size_t)0U, uint8_t,
+ uint8_t *) |
+ (uint32_t)Eurydice_slice_index(byte_chunk, (size_t)1U, uint8_t,
+ uint8_t *)
+ << 8U) |
+ (uint32_t)Eurydice_slice_index(byte_chunk, (size_t)2U, uint8_t,
+ uint8_t *)
+ << 16U) |
+ (uint32_t)Eurydice_slice_index(byte_chunk, (size_t)3U, uint8_t,
+ uint8_t *)
+ << 24U;
+ uint32_t even_bits = random_bits_as_u32 & 1431655765U;
+ uint32_t odd_bits = random_bits_as_u32 >> 1U & 1431655765U;
+ uint32_t coin_toss_outcomes = even_bits + odd_bits;
+ for (uint32_t i = 0U; i < 32U / 4U; i++) {
+ uint32_t outcome_set = i;
+ uint32_t outcome_set0 = outcome_set * 4U;
+ int16_t outcome_1 =
+ (int16_t)(coin_toss_outcomes >> (uint32_t)outcome_set0 & 3U);
+ int16_t outcome_2 =
+ (int16_t)(coin_toss_outcomes >> (uint32_t)(outcome_set0 + 2U) & 3U);
+ size_t offset = (size_t)(outcome_set0 >> 2U);
+ sampled_i16s[(size_t)8U * chunk_number + offset] = outcome_1 - outcome_2;
+ }
+ }
+ return libcrux_ml_kem_polynomial_from_i16_array_d6_ea(
+ Eurydice_array_to_slice((size_t)256U, sampled_i16s, int16_t));
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.sampling.sample_from_binomial_distribution with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- ETA= 2
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_sampling_sample_from_binomial_distribution_a0(
+ Eurydice_slice randomness) {
+ return libcrux_ml_kem_sampling_sample_from_binomial_distribution_2_ea(
+ randomness);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_at_layer_7
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ntt_ntt_at_layer_7_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ size_t step = LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT / (size_t)2U;
+ for (size_t i = (size_t)0U; i < step; i++) {
+ size_t j = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector t =
+ libcrux_ml_kem_vector_portable_multiply_by_constant_b8(
+ re->coefficients[j + step], (int16_t)-1600);
+ re->coefficients[j + step] =
+ libcrux_ml_kem_vector_portable_sub_b8(re->coefficients[j], &t);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____1 =
+ libcrux_ml_kem_vector_portable_add_b8(re->coefficients[j], &t);
+ re->coefficients[j] = uu____1;
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ntt.ntt_binomially_sampled_ring_element
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_ntt_ntt_binomially_sampled_ring_element_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re) {
+ libcrux_ml_kem_ntt_ntt_at_layer_7_ea(re);
+ size_t zeta_i = (size_t)1U;
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)6U,
+ (size_t)11207U);
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(&zeta_i, re, (size_t)5U,
+ (size_t)11207U + (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_4_plus_ea(
+ &zeta_i, re, (size_t)4U, (size_t)11207U + (size_t)2U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_3_ea(
+ &zeta_i, re, (size_t)11207U + (size_t)3U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_2_ea(
+ &zeta_i, re, (size_t)11207U + (size_t)4U * (size_t)3328U);
+ libcrux_ml_kem_ntt_ntt_at_layer_1_ea(
+ &zeta_i, re, (size_t)11207U + (size_t)5U * (size_t)3328U);
+ libcrux_ml_kem_polynomial_poly_barrett_reduce_d6_ea(re);
+}
+
+/**
+ Sample a vector of ring elements from a centered binomial distribution and
+ convert them into their NTT representations.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.sample_vector_cbd_then_ntt
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- ETA= 2
+- ETA_RANDOMNESS_SIZE= 128
+*/
+static KRML_MUSTINLINE uint8_t
+libcrux_ml_kem_ind_cpa_sample_vector_cbd_then_ntt_3b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re_as_ntt,
+ uint8_t *prf_input, uint8_t domain_separator) {
+ uint8_t prf_inputs[3U][33U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ core_array__core__clone__Clone_for__Array_T__N___clone(
+ (size_t)33U, prf_input, prf_inputs[i], uint8_t, void *);
+ }
+ domain_separator =
+ libcrux_ml_kem_utils_prf_input_inc_e0(prf_inputs, domain_separator);
+ uint8_t prf_outputs[3U][128U];
+ libcrux_ml_kem_hash_functions_portable_PRFxN_4a_41(prf_inputs, prf_outputs);
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ re_as_ntt[i0] =
+ libcrux_ml_kem_sampling_sample_from_binomial_distribution_a0(
+ Eurydice_array_to_slice((size_t)128U, prf_outputs[i0], uint8_t));
+ libcrux_ml_kem_ntt_ntt_binomially_sampled_ring_element_ea(&re_as_ntt[i0]);
+ }
+ return domain_separator;
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@2]> for libcrux_ml_kem::ind_cpa::encrypt_c1::closure#1[TraitClause@0, TraitClause@1, TraitClause@2,
+TraitClause@3]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt_c1.call_mut_dd
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- C1_LEN= 960
+- U_COMPRESSION_FACTOR= 10
+- BLOCK_LEN= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_ind_cpa_encrypt_c1_call_mut_dd_85(void **_, size_t tupled_args) {
+ return libcrux_ml_kem_polynomial_ZERO_d6_ea();
+}
+
+/**
+ Sample a vector of ring elements from a centered binomial distribution.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.sample_ring_element_cbd
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- ETA2_RANDOMNESS_SIZE= 128
+- ETA2= 2
+*/
+static KRML_MUSTINLINE uint8_t
+libcrux_ml_kem_ind_cpa_sample_ring_element_cbd_3b(
+ uint8_t *prf_input, uint8_t domain_separator,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error_1) {
+ uint8_t prf_inputs[3U][33U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ core_array__core__clone__Clone_for__Array_T__N___clone(
+ (size_t)33U, prf_input, prf_inputs[i], uint8_t, void *);
+ }
+ domain_separator =
+ libcrux_ml_kem_utils_prf_input_inc_e0(prf_inputs, domain_separator);
+ uint8_t prf_outputs[3U][128U];
+ libcrux_ml_kem_hash_functions_portable_PRFxN_4a_41(prf_inputs, prf_outputs);
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d uu____0 =
+ libcrux_ml_kem_sampling_sample_from_binomial_distribution_a0(
+ Eurydice_array_to_slice((size_t)128U, prf_outputs[i0], uint8_t));
+ error_1[i0] = uu____0;
+ }
+ return domain_separator;
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRF
+with const generics
+- LEN= 128
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRF_a6(
+ Eurydice_slice input, uint8_t ret[128U]) {
+ uint8_t digest[128U] = {0U};
+ libcrux_sha3_portable_shake256(
+ Eurydice_array_to_slice((size_t)128U, digest, uint8_t), input);
+ memcpy(ret, digest, (size_t)128U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.PRF_4a
+with const generics
+- K= 3
+- LEN= 128
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_PRF_4a_410(
+ Eurydice_slice input, uint8_t ret[128U]) {
+ libcrux_ml_kem_hash_functions_portable_PRF_a6(input, ret);
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]> for libcrux_ml_kem::matrix::compute_vector_u::closure[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.matrix.compute_vector_u.call_mut_a8
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static inline libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_matrix_compute_vector_u_call_mut_a8_1b(void **_,
+ size_t tupled_args) {
+ return libcrux_ml_kem_polynomial_ZERO_d6_ea();
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_error_reduce
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_add_error_reduce_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t j = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ coefficient_normal_form =
+ libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(
+ myself->coefficients[j], (int16_t)1441);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector sum =
+ libcrux_ml_kem_vector_portable_add_b8(coefficient_normal_form,
+ &error->coefficients[j]);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector red =
+ libcrux_ml_kem_vector_portable_barrett_reduce_b8(sum);
+ myself->coefficients[j] = red;
+ }
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_error_reduce_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_polynomial_add_error_reduce_d6_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error) {
+ libcrux_ml_kem_polynomial_add_error_reduce_ea(self, error);
+}
+
+/**
+ Compute u := InvertNTT(Aᵀ ◦ r̂) + e₁
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.matrix.compute_vector_u
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_matrix_compute_vector_u_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d (*a_as_ntt)[3U],
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *r_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error_1,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d ret[3U]) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ result[i] =
+ libcrux_ml_kem_matrix_compute_vector_u_call_mut_a8_1b(&lvalue, i);
+ }
+ for (size_t i0 = (size_t)0U;
+ i0 < Eurydice_slice_len(
+ Eurydice_array_to_slice(
+ (size_t)3U, a_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d[3U]),
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d[3U]);
+ i0++) {
+ size_t i1 = i0;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *row = a_as_ntt[i1];
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(
+ Eurydice_array_to_slice(
+ (size_t)3U, row,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d),
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d);
+ i++) {
+ size_t j = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *a_element = &row[j];
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d product =
+ libcrux_ml_kem_polynomial_ntt_multiply_d6_ea(a_element, &r_as_ntt[j]);
+ libcrux_ml_kem_polynomial_add_to_ring_element_d6_1b(&result[i1],
+ &product);
+ }
+ libcrux_ml_kem_invert_ntt_invert_ntt_montgomery_1b(&result[i1]);
+ libcrux_ml_kem_polynomial_add_error_reduce_d6_ea(&result[i1], &error_1[i1]);
+ }
+ memcpy(
+ ret, result,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.vector.portable.compress.compress
+with const generics
+- COEFFICIENT_BITS= 10
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_compress_ef(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ int16_t uu____0 = libcrux_secrets_int_as_i16_f5(
+ libcrux_ml_kem_vector_portable_compress_compress_ciphertext_coefficient(
+ (uint8_t)(int32_t)10,
+ libcrux_secrets_int_as_u16_f5(a.elements[i0])));
+ a.elements[i0] = uu____0;
+ }
+ return a;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.vector.portable.compress_b8
+with const generics
+- COEFFICIENT_BITS= 10
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_b8_ef(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_compress_ef(a);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.serialize.compress_then_serialize_10
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- OUT_LEN= 320
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_compress_then_serialize_10_ff(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re, uint8_t ret[320U]) {
+ uint8_t serialized[320U] = {0U};
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_vector_portable_compress_b8_ef(
+ libcrux_ml_kem_serialize_to_unsigned_field_modulus_ea(
+ re->coefficients[i0]));
+ uint8_t bytes[20U];
+ libcrux_ml_kem_vector_portable_serialize_10_b8(coefficient, bytes);
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(serialized, (size_t)20U * i0,
+ (size_t)20U * i0 + (size_t)20U, uint8_t *),
+ Eurydice_array_to_slice((size_t)20U, bytes, uint8_t), uint8_t);
+ }
+ memcpy(ret, serialized, (size_t)320U * sizeof(uint8_t));
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.compress_then_serialize_ring_element_u with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- COMPRESSION_FACTOR= 10
+- OUT_LEN= 320
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_compress_then_serialize_ring_element_u_fe(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *re, uint8_t ret[320U]) {
+ uint8_t uu____0[320U];
+ libcrux_ml_kem_serialize_compress_then_serialize_10_ff(re, uu____0);
+ memcpy(ret, uu____0, (size_t)320U * sizeof(uint8_t));
+}
+
+/**
+ Call [`compress_then_serialize_ring_element_u`] on each ring element.
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.compress_then_serialize_u
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- OUT_LEN= 960
+- COMPRESSION_FACTOR= 10
+- BLOCK_LEN= 320
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_compress_then_serialize_u_43(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d input[3U],
+ Eurydice_slice out) {
+ for (size_t i = (size_t)0U;
+ i < Eurydice_slice_len(
+ Eurydice_array_to_slice(
+ (size_t)3U, input,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d),
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d);
+ i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re = input[i0];
+ Eurydice_slice uu____0 = Eurydice_slice_subslice3(
+ out, i0 * ((size_t)960U / (size_t)3U),
+ (i0 + (size_t)1U) * ((size_t)960U / (size_t)3U), uint8_t *);
+ uint8_t ret[320U];
+ libcrux_ml_kem_serialize_compress_then_serialize_ring_element_u_fe(&re,
+ ret);
+ Eurydice_slice_copy(
+ uu____0, Eurydice_array_to_slice((size_t)320U, ret, uint8_t), uint8_t);
+ }
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt_c1
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- C1_LEN= 960
+- U_COMPRESSION_FACTOR= 10
+- BLOCK_LEN= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static KRML_MUSTINLINE tuple_ed libcrux_ml_kem_ind_cpa_encrypt_c1_85(
+ Eurydice_slice randomness,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d (*matrix)[3U],
+ Eurydice_slice ciphertext) {
+ uint8_t prf_input[33U];
+ libcrux_ml_kem_utils_into_padded_array_c8(randomness, prf_input);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d r_as_ntt[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ r_as_ntt[i] = libcrux_ml_kem_ind_cpa_encrypt_c1_call_mut_f1_85(&lvalue, i);
+ }
+ uint8_t domain_separator0 =
+ libcrux_ml_kem_ind_cpa_sample_vector_cbd_then_ntt_3b(r_as_ntt, prf_input,
+ 0U);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d error_1[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ /* original Rust expression is not an lvalue in C */
+ void *lvalue = (void *)0U;
+ error_1[i] = libcrux_ml_kem_ind_cpa_encrypt_c1_call_mut_dd_85(&lvalue, i);
+ }
+ uint8_t domain_separator = libcrux_ml_kem_ind_cpa_sample_ring_element_cbd_3b(
+ prf_input, domain_separator0, error_1);
+ prf_input[32U] = domain_separator;
+ uint8_t prf_output[128U];
+ libcrux_ml_kem_hash_functions_portable_PRF_4a_410(
+ Eurydice_array_to_slice((size_t)33U, prf_input, uint8_t), prf_output);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d error_2 =
+ libcrux_ml_kem_sampling_sample_from_binomial_distribution_a0(
+ Eurydice_array_to_slice((size_t)128U, prf_output, uint8_t));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d u[3U];
+ libcrux_ml_kem_matrix_compute_vector_u_1b(matrix, r_as_ntt, error_1, u);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d uu____0[3U];
+ memcpy(
+ uu____0, u,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ libcrux_ml_kem_ind_cpa_compress_then_serialize_u_43(uu____0, ciphertext);
+ /* Passing arrays by value in Rust generates a copy in C */
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d copy_of_r_as_ntt[3U];
+ memcpy(
+ copy_of_r_as_ntt, r_as_ntt,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ tuple_ed lit;
+ memcpy(
+ lit.fst, copy_of_r_as_ntt,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ lit.snd = error_2;
+ return lit;
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.deserialize_then_decompress_message with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_serialize_deserialize_then_decompress_message_ea(
+ uint8_t *serialized) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U; i < (size_t)16U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ coefficient_compressed =
+ libcrux_ml_kem_vector_portable_deserialize_1_b8(
+ Eurydice_array_to_subslice3(serialized, (size_t)2U * i0,
+ (size_t)2U * i0 + (size_t)2U,
+ uint8_t *));
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector uu____0 =
+ libcrux_ml_kem_vector_portable_decompress_1_b8(coefficient_compressed);
+ re.coefficients[i0] = uu____0;
+ }
+ return re;
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_message_error_reduce
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_add_message_error_reduce_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *myself,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *message,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector
+ coefficient_normal_form =
+ libcrux_ml_kem_vector_portable_montgomery_multiply_by_constant_b8(
+ result.coefficients[i0], (int16_t)1441);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector sum1 =
+ libcrux_ml_kem_vector_portable_add_b8(myself->coefficients[i0],
+ &message->coefficients[i0]);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector sum2 =
+ libcrux_ml_kem_vector_portable_add_b8(coefficient_normal_form, &sum1);
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector red =
+ libcrux_ml_kem_vector_portable_barrett_reduce_b8(sum2);
+ result.coefficients[i0] = red;
+ }
+ return result;
+}
+
+/**
+This function found in impl
+{libcrux_ml_kem::polynomial::PolynomialRingElement[TraitClause@0,
+TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.polynomial.add_message_error_reduce_d6
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_polynomial_add_message_error_reduce_d6_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *self,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *message,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result) {
+ return libcrux_ml_kem_polynomial_add_message_error_reduce_ea(self, message,
+ result);
+}
+
+/**
+ Compute InverseNTT(tᵀ ◦ r̂) + e₂ + message
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.matrix.compute_ring_element_v
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_polynomial_PolynomialRingElement_1d
+libcrux_ml_kem_matrix_compute_ring_element_v_1b(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *t_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *r_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error_2,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *message) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d result =
+ libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d product =
+ libcrux_ml_kem_polynomial_ntt_multiply_d6_ea(&t_as_ntt[i0],
+ &r_as_ntt[i0]);
+ libcrux_ml_kem_polynomial_add_to_ring_element_d6_1b(&result, &product);
+ }
+ libcrux_ml_kem_invert_ntt_invert_ntt_montgomery_1b(&result);
+ return libcrux_ml_kem_polynomial_add_message_error_reduce_d6_ea(
+ error_2, message, result);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.vector.portable.compress.compress
+with const generics
+- COEFFICIENT_BITS= 4
+*/
+static KRML_MUSTINLINE libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_compress_d1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_VECTOR_TRAITS_FIELD_ELEMENTS_IN_VECTOR; i++) {
+ size_t i0 = i;
+ int16_t uu____0 = libcrux_secrets_int_as_i16_f5(
+ libcrux_ml_kem_vector_portable_compress_compress_ciphertext_coefficient(
+ (uint8_t)(int32_t)4,
+ libcrux_secrets_int_as_u16_f5(a.elements[i0])));
+ a.elements[i0] = uu____0;
+ }
+ return a;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::vector::traits::Operations for
+libcrux_ml_kem::vector::portable::vector_type::PortableVector}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.vector.portable.compress_b8
+with const generics
+- COEFFICIENT_BITS= 4
+*/
+static inline libcrux_ml_kem_vector_portable_vector_type_PortableVector
+libcrux_ml_kem_vector_portable_compress_b8_d1(
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector a) {
+ return libcrux_ml_kem_vector_portable_compress_compress_d1(a);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.serialize.compress_then_serialize_4
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_compress_then_serialize_4_ea(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re,
+ Eurydice_slice serialized) {
+ for (size_t i = (size_t)0U;
+ i < LIBCRUX_ML_KEM_POLYNOMIAL_VECTORS_IN_RING_ELEMENT; i++) {
+ size_t i0 = i;
+ libcrux_ml_kem_vector_portable_vector_type_PortableVector coefficient =
+ libcrux_ml_kem_vector_portable_compress_b8_d1(
+ libcrux_ml_kem_serialize_to_unsigned_field_modulus_ea(
+ re.coefficients[i0]));
+ uint8_t bytes[8U];
+ libcrux_ml_kem_vector_portable_serialize_4_b8(coefficient, bytes);
+ Eurydice_slice_copy(
+ Eurydice_slice_subslice3(serialized, (size_t)8U * i0,
+ (size_t)8U * i0 + (size_t)8U, uint8_t *),
+ Eurydice_array_to_slice((size_t)8U, bytes, uint8_t), uint8_t);
+ }
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.serialize.compress_then_serialize_ring_element_v with types
+libcrux_ml_kem_vector_portable_vector_type_PortableVector with const generics
+- K= 3
+- COMPRESSION_FACTOR= 4
+- OUT_LEN= 128
+*/
+static KRML_MUSTINLINE void
+libcrux_ml_kem_serialize_compress_then_serialize_ring_element_v_6c(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d re, Eurydice_slice out) {
+ libcrux_ml_kem_serialize_compress_then_serialize_4_ea(re, out);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt_c2
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+- V_COMPRESSION_FACTOR= 4
+- C2_LEN= 128
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_encrypt_c2_6c(
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *t_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *r_as_ntt,
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *error_2,
+ uint8_t *message, Eurydice_slice ciphertext) {
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d message_as_ring_element =
+ libcrux_ml_kem_serialize_deserialize_then_decompress_message_ea(message);
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d v =
+ libcrux_ml_kem_matrix_compute_ring_element_v_1b(
+ t_as_ntt, r_as_ntt, error_2, &message_as_ring_element);
+ libcrux_ml_kem_serialize_compress_then_serialize_ring_element_v_6c(
+ v, ciphertext);
+}
+
+/**
+ This function implements Algorithm 13 of the
+ NIST FIPS 203 specification; this is the Kyber CPA-PKE encryption algorithm.
+
+ Algorithm 13 is reproduced below:
+
+ ```plaintext
+ Input: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}.
+ Input: message m ∈ 𝔹^{32}.
+ Input: encryption randomness r ∈ 𝔹^{32}.
+ Output: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}.
+
+ N ← 0
+ t̂ ← ByteDecode₁₂(ekₚₖₑ[0:384k])
+ ρ ← ekₚₖₑ[384k: 384k + 32]
+ for (i ← 0; i < k; i++)
+ for(j ← 0; j < k; j++)
+ Â[i,j] ← SampleNTT(XOF(ρ, i, j))
+ end for
+ end for
+ for(i ← 0; i < k; i++)
+ r[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(r,N))
+ N ← N + 1
+ end for
+ for(i ← 0; i < k; i++)
+ e₁[i] ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N))
+ N ← N + 1
+ end for
+ e₂ ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N))
+ r̂ ← NTT(r)
+ u ← NTT-¹(Âᵀ ◦ r̂) + e₁
+ μ ← Decompress₁(ByteDecode₁(m)))
+ v ← NTT-¹(t̂ᵀ ◦ rˆ) + e₂ + μ
+ c₁ ← ByteEncode_{dᵤ}(Compress_{dᵤ}(u))
+ c₂ ← ByteEncode_{dᵥ}(Compress_{dᵥ}(v))
+ return c ← (c₁ ‖ c₂)
+ ```
+
+ The NIST FIPS 203 standard can be found at
+ .
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt_unpacked
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_LEN= 960
+- C2_LEN= 128
+- U_COMPRESSION_FACTOR= 10
+- V_COMPRESSION_FACTOR= 4
+- BLOCK_LEN= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_encrypt_unpacked_2a(
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0 *public_key,
+ uint8_t *message, Eurydice_slice randomness, uint8_t ret[1088U]) {
+ uint8_t ciphertext[1088U] = {0U};
+ tuple_ed uu____0 = libcrux_ml_kem_ind_cpa_encrypt_c1_85(
+ randomness, public_key->A,
+ Eurydice_array_to_subslice3(ciphertext, (size_t)0U, (size_t)960U,
+ uint8_t *));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d r_as_ntt[3U];
+ memcpy(
+ r_as_ntt, uu____0.fst,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d error_2 = uu____0.snd;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *uu____1 =
+ public_key->t_as_ntt;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *uu____2 = r_as_ntt;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d *uu____3 = &error_2;
+ uint8_t *uu____4 = message;
+ libcrux_ml_kem_ind_cpa_encrypt_c2_6c(
+ uu____1, uu____2, uu____3, uu____4,
+ Eurydice_array_to_subslice_from((size_t)1088U, ciphertext, (size_t)960U,
+ uint8_t, size_t, uint8_t[]));
+ memcpy(ret, ciphertext, (size_t)1088U * sizeof(uint8_t));
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.encrypt
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]] with const
+generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_LEN= 960
+- C2_LEN= 128
+- U_COMPRESSION_FACTOR= 10
+- V_COMPRESSION_FACTOR= 4
+- BLOCK_LEN= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cpa_encrypt_2a(
+ Eurydice_slice public_key, uint8_t *message, Eurydice_slice randomness,
+ uint8_t ret[1088U]) {
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPublicKeyUnpacked_a0
+ unpacked_public_key =
+ libcrux_ml_kem_ind_cpa_build_unpacked_public_key_3f(public_key);
+ uint8_t ret0[1088U];
+ libcrux_ml_kem_ind_cpa_encrypt_unpacked_2a(&unpacked_public_key, message,
+ randomness, ret0);
+ memcpy(ret, ret0, (size_t)1088U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::variant::Variant for
+libcrux_ml_kem::variant::MlKem}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.variant.kdf_39
+with types libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]]
+with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_variant_kdf_39_d6(
+ Eurydice_slice shared_secret, uint8_t *_, uint8_t ret[32U]) {
+ uint8_t out[32U] = {0U};
+ Eurydice_slice_copy(Eurydice_array_to_slice((size_t)32U, out, uint8_t),
+ shared_secret, uint8_t);
+ memcpy(ret, out, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+ This code verifies on some machines, runs out of memory on others
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cca.decapsulate
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]],
+libcrux_ml_kem_variant_MlKem with const generics
+- K= 3
+- SECRET_KEY_SIZE= 2400
+- CPA_SECRET_KEY_SIZE= 1152
+- PUBLIC_KEY_SIZE= 1184
+- CIPHERTEXT_SIZE= 1088
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_SIZE= 960
+- C2_SIZE= 128
+- VECTOR_U_COMPRESSION_FACTOR= 10
+- VECTOR_V_COMPRESSION_FACTOR= 4
+- C1_BLOCK_SIZE= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+- IMPLICIT_REJECTION_HASH_INPUT_SIZE= 1120
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_ind_cca_decapsulate_62(
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext, uint8_t ret[32U]) {
+ Eurydice_slice_uint8_t_x4 uu____0 =
+ libcrux_ml_kem_types_unpack_private_key_b4(
+ Eurydice_array_to_slice((size_t)2400U, private_key->value, uint8_t));
+ Eurydice_slice ind_cpa_secret_key = uu____0.fst;
+ Eurydice_slice ind_cpa_public_key = uu____0.snd;
+ Eurydice_slice ind_cpa_public_key_hash = uu____0.thd;
+ Eurydice_slice implicit_rejection_value = uu____0.f3;
+ uint8_t decrypted[32U];
+ libcrux_ml_kem_ind_cpa_decrypt_42(ind_cpa_secret_key, ciphertext->value,
+ decrypted);
+ uint8_t to_hash0[64U];
+ libcrux_ml_kem_utils_into_padded_array_24(
+ Eurydice_array_to_slice((size_t)32U, decrypted, uint8_t), to_hash0);
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice_from(
+ (size_t)64U, to_hash0, LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE,
+ uint8_t, size_t, uint8_t[]),
+ ind_cpa_public_key_hash, uint8_t);
+ uint8_t hashed[64U];
+ libcrux_ml_kem_hash_functions_portable_G_4a_e0(
+ Eurydice_array_to_slice((size_t)64U, to_hash0, uint8_t), hashed);
+ Eurydice_slice_uint8_t_x2 uu____1 = Eurydice_slice_split_at(
+ Eurydice_array_to_slice((size_t)64U, hashed, uint8_t),
+ LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE, uint8_t,
+ Eurydice_slice_uint8_t_x2);
+ Eurydice_slice shared_secret0 = uu____1.fst;
+ Eurydice_slice pseudorandomness = uu____1.snd;
+ uint8_t to_hash[1120U];
+ libcrux_ml_kem_utils_into_padded_array_15(implicit_rejection_value, to_hash);
+ Eurydice_slice uu____2 = Eurydice_array_to_subslice_from(
+ (size_t)1120U, to_hash, LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE,
+ uint8_t, size_t, uint8_t[]);
+ Eurydice_slice_copy(uu____2, libcrux_ml_kem_types_as_ref_d3_80(ciphertext),
+ uint8_t);
+ uint8_t implicit_rejection_shared_secret0[32U];
+ libcrux_ml_kem_hash_functions_portable_PRF_4a_41(
+ Eurydice_array_to_slice((size_t)1120U, to_hash, uint8_t),
+ implicit_rejection_shared_secret0);
+ uint8_t expected_ciphertext[1088U];
+ libcrux_ml_kem_ind_cpa_encrypt_2a(ind_cpa_public_key, decrypted,
+ pseudorandomness, expected_ciphertext);
+ uint8_t implicit_rejection_shared_secret[32U];
+ libcrux_ml_kem_variant_kdf_39_d6(
+ Eurydice_array_to_slice((size_t)32U, implicit_rejection_shared_secret0,
+ uint8_t),
+ libcrux_ml_kem_types_as_slice_a9_80(ciphertext),
+ implicit_rejection_shared_secret);
+ uint8_t shared_secret[32U];
+ libcrux_ml_kem_variant_kdf_39_d6(
+ shared_secret0, libcrux_ml_kem_types_as_slice_a9_80(ciphertext),
+ shared_secret);
+ uint8_t ret0[32U];
+ libcrux_ml_kem_constant_time_ops_compare_ciphertexts_select_shared_secret_in_constant_time(
+ libcrux_ml_kem_types_as_ref_d3_80(ciphertext),
+ Eurydice_array_to_slice((size_t)1088U, expected_ciphertext, uint8_t),
+ Eurydice_array_to_slice((size_t)32U, shared_secret, uint8_t),
+ Eurydice_array_to_slice((size_t)32U, implicit_rejection_shared_secret,
+ uint8_t),
+ ret0);
+ memcpy(ret, ret0, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+ Portable decapsulate
+*/
+/**
+A monomorphic instance of
+libcrux_ml_kem.ind_cca.instantiations.portable.decapsulate with const generics
+- K= 3
+- SECRET_KEY_SIZE= 2400
+- CPA_SECRET_KEY_SIZE= 1152
+- PUBLIC_KEY_SIZE= 1184
+- CIPHERTEXT_SIZE= 1088
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_SIZE= 960
+- C2_SIZE= 128
+- VECTOR_U_COMPRESSION_FACTOR= 10
+- VECTOR_V_COMPRESSION_FACTOR= 4
+- C1_BLOCK_SIZE= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+- IMPLICIT_REJECTION_HASH_INPUT_SIZE= 1120
+*/
+static inline void
+libcrux_ml_kem_ind_cca_instantiations_portable_decapsulate_35(
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext, uint8_t ret[32U]) {
+ libcrux_ml_kem_ind_cca_decapsulate_62(private_key, ciphertext, ret);
+}
+
+/**
+ Decapsulate ML-KEM 768
+
+ Generates an [`MlKemSharedSecret`].
+ The input is a reference to an [`MlKem768PrivateKey`] and an
+ [`MlKem768Ciphertext`].
+*/
+void libcrux_ml_kem_mlkem768_portable_decapsulate(
+ libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
+ libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext, uint8_t ret[32U]) {
+ libcrux_ml_kem_ind_cca_instantiations_portable_decapsulate_35(
+ private_key, ciphertext, ret);
+}
+
+/**
+This function found in impl {libcrux_ml_kem::variant::Variant for
+libcrux_ml_kem::variant::MlKem}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.variant.entropy_preprocess_39
+with types libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]]
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_variant_entropy_preprocess_39_9c(
+ Eurydice_slice randomness, uint8_t ret[32U]) {
+ uint8_t out[32U] = {0U};
+ Eurydice_slice_copy(Eurydice_array_to_slice((size_t)32U, out, uint8_t),
+ randomness, uint8_t);
+ memcpy(ret, out, (size_t)32U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {libcrux_ml_kem::hash_functions::Hash for
+libcrux_ml_kem::hash_functions::portable::PortableHash}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.hash_functions.portable.H_4a
+with const generics
+- K= 3
+*/
+static inline void libcrux_ml_kem_hash_functions_portable_H_4a_e0(
+ Eurydice_slice input, uint8_t ret[32U]) {
+ libcrux_ml_kem_hash_functions_portable_H(input, ret);
+}
+
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cca.encapsulate
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector,
+libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]],
+libcrux_ml_kem_variant_MlKem with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- PUBLIC_KEY_SIZE= 1184
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_SIZE= 960
+- C2_SIZE= 128
+- VECTOR_U_COMPRESSION_FACTOR= 10
+- VECTOR_V_COMPRESSION_FACTOR= 4
+- C1_BLOCK_SIZE= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static KRML_MUSTINLINE tuple_c2 libcrux_ml_kem_ind_cca_encapsulate_ca(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *public_key, uint8_t *randomness) {
+ uint8_t randomness0[32U];
+ libcrux_ml_kem_variant_entropy_preprocess_39_9c(
+ Eurydice_array_to_slice((size_t)32U, randomness, uint8_t), randomness0);
+ uint8_t to_hash[64U];
+ libcrux_ml_kem_utils_into_padded_array_24(
+ Eurydice_array_to_slice((size_t)32U, randomness0, uint8_t), to_hash);
+ Eurydice_slice uu____0 = Eurydice_array_to_subslice_from(
+ (size_t)64U, to_hash, LIBCRUX_ML_KEM_CONSTANTS_H_DIGEST_SIZE, uint8_t,
+ size_t, uint8_t[]);
+ uint8_t ret0[32U];
+ libcrux_ml_kem_hash_functions_portable_H_4a_e0(
+ Eurydice_array_to_slice((size_t)1184U,
+ libcrux_ml_kem_types_as_slice_e6_d0(public_key),
+ uint8_t),
+ ret0);
+ Eurydice_slice_copy(
+ uu____0, Eurydice_array_to_slice((size_t)32U, ret0, uint8_t), uint8_t);
+ uint8_t hashed[64U];
+ libcrux_ml_kem_hash_functions_portable_G_4a_e0(
+ Eurydice_array_to_slice((size_t)64U, to_hash, uint8_t), hashed);
+ Eurydice_slice_uint8_t_x2 uu____1 = Eurydice_slice_split_at(
+ Eurydice_array_to_slice((size_t)64U, hashed, uint8_t),
+ LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE, uint8_t,
+ Eurydice_slice_uint8_t_x2);
+ Eurydice_slice shared_secret = uu____1.fst;
+ Eurydice_slice pseudorandomness = uu____1.snd;
+ uint8_t ciphertext[1088U];
+ libcrux_ml_kem_ind_cpa_encrypt_2a(
+ Eurydice_array_to_slice((size_t)1184U,
+ libcrux_ml_kem_types_as_slice_e6_d0(public_key),
+ uint8_t),
+ randomness0, pseudorandomness, ciphertext);
+ /* Passing arrays by value in Rust generates a copy in C */
+ uint8_t copy_of_ciphertext[1088U];
+ memcpy(copy_of_ciphertext, ciphertext, (size_t)1088U * sizeof(uint8_t));
+ tuple_c2 lit;
+ lit.fst = libcrux_ml_kem_types_from_e0_80(copy_of_ciphertext);
+ uint8_t ret[32U];
+ libcrux_ml_kem_variant_kdf_39_d6(shared_secret, ciphertext, ret);
+ memcpy(lit.snd, ret, (size_t)32U * sizeof(uint8_t));
+ return lit;
+}
+
+/**
+A monomorphic instance of
+libcrux_ml_kem.ind_cca.instantiations.portable.encapsulate with const generics
+- K= 3
+- CIPHERTEXT_SIZE= 1088
+- PUBLIC_KEY_SIZE= 1184
+- T_AS_NTT_ENCODED_SIZE= 1152
+- C1_SIZE= 960
+- C2_SIZE= 128
+- VECTOR_U_COMPRESSION_FACTOR= 10
+- VECTOR_V_COMPRESSION_FACTOR= 4
+- C1_BLOCK_SIZE= 320
+- ETA1= 2
+- ETA1_RANDOMNESS_SIZE= 128
+- ETA2= 2
+- ETA2_RANDOMNESS_SIZE= 128
+*/
+static inline tuple_c2
+libcrux_ml_kem_ind_cca_instantiations_portable_encapsulate_cd(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *public_key, uint8_t *randomness) {
+ return libcrux_ml_kem_ind_cca_encapsulate_ca(public_key, randomness);
+}
+
+/**
+ Encapsulate ML-KEM 768
+
+ Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
+ The input is a reference to an [`MlKem768PublicKey`] and [`SHARED_SECRET_SIZE`]
+ bytes of `randomness`.
+*/
+tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate(
+ libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
+ uint8_t randomness[32U]) {
+ return libcrux_ml_kem_ind_cca_instantiations_portable_encapsulate_cd(
+ public_key, randomness);
+}
+
+/**
+This function found in impl {core::default::Default for
+libcrux_ml_kem::ind_cpa::unpacked::IndCpaPrivateKeyUnpacked[TraitClause@0, TraitClause@1]}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.ind_cpa.unpacked.default_70
+with types libcrux_ml_kem_vector_portable_vector_type_PortableVector
+with const generics
+- K= 3
+*/
+static inline libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0
+libcrux_ml_kem_ind_cpa_unpacked_default_70_1b(void) {
+ libcrux_ml_kem_ind_cpa_unpacked_IndCpaPrivateKeyUnpacked_a0 lit;
+ libcrux_ml_kem_polynomial_PolynomialRingElement_1d repeat_expression[3U];
+ for (size_t i = (size_t)0U; i < (size_t)3U; i++) {
+ repeat_expression[i] = libcrux_ml_kem_polynomial_ZERO_d6_ea();
+ }
+ memcpy(
+ lit.secret_as_ntt, repeat_expression,
+ (size_t)3U * sizeof(libcrux_ml_kem_polynomial_PolynomialRingElement_1d));
+ return lit;
+}
+
+/**
+This function found in impl {libcrux_ml_kem::variant::Variant for
+libcrux_ml_kem::variant::MlKem}
+*/
+/**
+A monomorphic instance of libcrux_ml_kem.variant.cpa_keygen_seed_39
+with types libcrux_ml_kem_hash_functions_portable_PortableHash[[$3size_t]]
+with const generics
+- K= 3
+*/
+static KRML_MUSTINLINE void libcrux_ml_kem_variant_cpa_keygen_seed_39_9c(
+ Eurydice_slice key_generation_seed, uint8_t ret[64U]) {
+ uint8_t seed[33U] = {0U};
+ Eurydice_slice_copy(
+ Eurydice_array_to_subslice3(
+ seed, (size_t)0U,
+ LIBCRUX_ML_KEM_CONSTANTS_CPA_PKE_KEY_GENERATION_SEED_SIZE, uint8_t *),
+ key_generation_seed, uint8_t);
+ seed[LIBCRUX_ML_KEM_CONSTANTS_CPA_PKE_KEY_GENERATION_SEED_SIZE] =
+ (uint8_t)(size_t)3U;
+ uint8_t ret0[64U];
+ libcrux_ml_kem_hash_functions_portable_G_4a_e0(
+ Eurydice_array_to_slice((size_t)33U, seed, uint8_t), ret0);
+ memcpy(ret, ret0, (size_t)64U * sizeof(uint8_t));
+}
+
+/**
+This function found in impl {core::ops::function::FnMut<(usize),
+libcrux_ml_kem::polynomial::PolynomialRingElement