1
0
mirror of https://github.com/libssh2/libssh2.git synced 2026-10-24 03:48:58 +03:00

tests: add tests for signed hostkey: ssh-ed25519-cert-v01@openssh.com

- add host CA keys.
- add ed25519 signed hostkey for tests.
- make hostkey tests recognize it and skip hash checks.
- make existing tests select hostkey algos.
- add `ssh-ed25519-cert-v01@openssh.com` to the list of tested algos.
  (exclude for unsupported backends.)

Ref: #2434
Inspired-by: #2435
Credits-to: Pier Carlo Cadoppi
Follow-up to 65a276e8fe #2064

Closes #2513
This commit is contained in:
Viktor Szakats
2026-08-06 01:20:43 +02:00
parent 19aa4338f7
commit 6036e57720
13 changed files with 164 additions and 15 deletions

View File

@@ -148,6 +148,8 @@ if(RUN_DOCKER_TESTS)
set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}")
if(_test MATCHES "mac-")
set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "FIXTURE_TEST_MAC=${_test}")
elseif(_test MATCHES "-cert-")
set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "FIXTURE_TEST_HOSTKEY=${_test}")
else()
set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "FIXTURE_TEST_CRYPT=${_test}")
endif()

View File

@@ -72,6 +72,8 @@ EXTRA_DIST = \
.checksrc \
CMakeLists.txt \
keys-generate.sh \
keys/ca_host_ed25519 \
keys/ca_host_ed25519.pub \
keys/ca_user_ecdsa \
keys/ca_user_ecdsa.pub \
keys/ca_user_ed25519 \
@@ -132,6 +134,7 @@ EXTRA_DIST = \
openssh_server/ssh_host_ecdsa_key \
openssh_server/ssh_host_ecdsa_key.pub \
openssh_server/ssh_host_ed25519_key \
openssh_server/ssh_host_ed25519_key-cert.pub \
openssh_server/ssh_host_ed25519_key.pub \
openssh_server/ssh_host_rsa_key \
openssh_server/ssh_host_rsa_key.pub \

View File

@@ -10,7 +10,8 @@ set -eu
# tests/openssh_server
[ -d openssh_server ] || mkdir openssh_server
rm openssh_server/*_key || true
rm -f openssh_server/*_key
rm -f openssh_server/*_key-cert.pub
ssh-keygen -t rsa -b 2048 -N '' -m PEM -C '' -f 'openssh_server/ssh_host_rsa_key'
ssh-keygen -t ecdsa -b 256 -N '' -m PEM -C '' -f 'openssh_server/ssh_host_ecdsa_key'
@@ -19,8 +20,11 @@ ssh-keygen -t ed25519 -N '' -m RFC4716 -C ''
# tests/keys
[ -d keys ] || mkdir keys
rm keys/ca_* || true
rm keys/id_* || true
rm -f keys/ca_*
rm -f keys/id_*
# host CAs
ssh-keygen -t ed25519 -N '' -m RFC4716 -C 'ca_host_ed25519' -f 'keys/ca_host_ed25519'
# user CAs
ssh-keygen -t rsa -b 3072 -N '' -m RFC4716 -C 'ca_user_rsa' -f 'keys/ca_user_rsa'
@@ -31,6 +35,9 @@ pw='libssh2'
id='identity'
pr="${1:-libssh2}"
# host certificates
ssh-keygen -h -I 'host_ed25519' -n "${pr}" -s 'keys/ca_host_ed25519' 'openssh_server/ssh_host_ed25519_key.pub'
# inspect PKCS8 private keys with command:
# $ openssl asn1parse -dump -in <id-filename>

View File

@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBQPH6E3OfiBQqrXDDQi6Ie4a5qL73t2O6UaKQfERGN0AAAAJixJvUssSb1
LAAAAAtzc2gtZWQyNTUxOQAAACBQPH6E3OfiBQqrXDDQi6Ie4a5qL73t2O6UaKQfERGN0A
AAAEDev+bTt9bTxlqtJLomzOYzNAmw4i3iZ7eDydUREXkMpFA8foTc5+IFCqtcMNCLoh7h
rmovve3Y7pRopB8REY3QAAAAD2NhX2hvc3RfZWQyNTUxOQECAwQFBg==
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFA8foTc5+IFCqtcMNCLoh7hrmovve3Y7pRopB8REY3Q ca_host_ed25519

View File

@@ -26,6 +26,10 @@ RUN install -m 0600 /tmp/etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
COPY ssh_host_ed25519_key /tmp/etc/ssh/ssh_host_ed25519_key
RUN install -m 0600 /tmp/etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key
# Host certificates
COPY ssh_host_ed25519_key-cert.pub /tmp/etc/ssh/ssh_host_ed25519_key-cert.pub
RUN install -m 0644 /tmp/etc/ssh/ssh_host_ed25519_key-cert.pub /etc/ssh/ssh_host_ed25519_key-cert.pub
# Trusted user CA keys
COPY ca_user_keys.pub /tmp/etc/ssh/ca_user_keys.pub
RUN install -m 0600 /tmp/etc/ssh/ca_user_keys.pub /etc/ssh/ca_user_keys.pub
@@ -39,6 +43,7 @@ RUN sed -i 's/KbdInteractiveAuthentication no/KbdInteractiveAuthentication yes/'
COPY sshd_config /tmp/etc/ssh/sshd_config
RUN cat /tmp/etc/ssh/sshd_config >> /etc/ssh/sshd_config
RUN echo 'TrustedUserCAKeys /etc/ssh/ca_user_keys.pub' >> /etc/ssh/sshd_config
RUN echo 'HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub' >> /etc/ssh/sshd_config
# SSH login fix. Otherwise user is kicked off after login
RUN sed 's/session\s*required\s*pam_loginuid.so/session optional pam_loginuid.so/g' -i /etc/pam.d/sshd

View File

@@ -0,0 +1 @@
ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAING+WjIhgorwyJHfxYyRjA/I7C1wCeRfO6iZ/ea3wJIXAAAAIO7Dhx/ox7Xoi/sg2GrR7j2x5NxvTGKnMH3wifd6UPCxAAAAAAAAAAAAAAACAAAADGhvc3RfZWQyNTUxOQAAAAsAAAAHbGlic3NoMgAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIFA8foTc5+IFCqtcMNCLoh7hrmovve3Y7pRopB8REY3QAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAxbW++zhv7ENBj74RL/91rq5COmlxsM8MjpQj9LUjUytm/hR4ZZj8Now/PGl1bO62zCTXa6BBC6xkcvaZp9D0H openssh_server/ssh_host_ed25519_key.pub

View File

@@ -60,7 +60,7 @@ static int connect_to_server(void)
return LIBSSH2_ERROR_NONE;
}
/* List of crypto protocols for which tests are skipped */
/* List of crypto algorithms for which tests are skipped */
static char const *skip_crypt[] = {
#if !LIBSSH2_3DES
"3des-cbc",
@@ -83,7 +83,7 @@ static char const *skip_crypt[] = {
NULL
};
/* List of MAC protocols for which tests are skipped */
/* List of MAC algorithms for which tests are skipped */
static char const *skip_mac[] = {
#ifndef LIBSSH2_HMAC_SHA1_ENABLE
"hmac-sha1",
@@ -97,12 +97,21 @@ static char const *skip_mac[] = {
NULL
};
/* List of HOSTKEY algorithms for which tests are skipped */
static char const *skip_hostkey[] = {
#if !LIBSSH2_ED25519
"ssh-ed25519-cert-v01@openssh.com",
#endif
NULL
};
LIBSSH2_SESSION *start_session_fixture(int *skipped, int *err)
{
int rc;
const char *crypt = getenv("FIXTURE_TEST_CRYPT");
const char *mac = getenv("FIXTURE_TEST_MAC");
const char *hostkey = getenv("FIXTURE_TEST_HOSTKEY");
*skipped = 0;
*err = LIBSSH2_ERROR_NONE;
@@ -131,6 +140,19 @@ LIBSSH2_SESSION *start_session_fixture(int *skipped, int *err)
}
}
if(hostkey) {
char const * const *sk;
for(sk = skip_hostkey; *sk; ++sk) {
if(!strcmp(*sk, hostkey)) {
fprintf(stderr,
"unsupported HOSTKEY algorithm (%s) skipped.\n",
hostkey);
*skipped = 1;
return NULL;
}
}
}
rc = start_openssh_fixture();
if(rc)
return NULL;
@@ -182,6 +204,26 @@ LIBSSH2_SESSION *start_session_fixture(int *skipped, int *err)
}
}
/* Without an explicit override, limit accepted hostkey types to those
tested (or potentially tested) in test_hostkey.c and
test_hostkey_hash.c. */
if(!hostkey)
hostkey =
"ssh-ed25519,"
"ecdsa-sha2-nistp521,"
"ecdsa-sha2-nistp384,"
"ecdsa-sha2-nistp256,"
"rsa-sha2-256,"
"rsa-sha2-512,"
"ssh-rsa";
if(libssh2_session_method_pref(connected_session,
LIBSSH2_METHOD_HOSTKEY, hostkey)) {
fprintf(stderr, "libssh2_session_method_pref() HOSTKEY failed "
"(probably disabled in the build): '%s'\n", hostkey);
return NULL;
}
libssh2_session_set_blocking(connected_session, 1);
rc = connect_to_server();

View File

@@ -23,31 +23,58 @@ static const char EXPECTED_ED25519_HOSTKEY[] =
int test(LIBSSH2_SESSION *session)
{
int rc;
size_t len;
size_t len, len_str;
int type;
size_t expected_len = 0;
char *expected_hostkey = NULL;
const char *hostkey;
const char *hostkey_str;
const char *hostkey = libssh2_session_hostkey(session, &len, &type);
hostkey = libssh2_session_hostkey(session, &len, &type);
if(!hostkey) {
print_last_session_error("libssh2_session_hostkey()");
return 1;
}
if(type == LIBSSH2_HOSTKEY_TYPE_ED25519)
if(len < 4) {
print_last_session_error("libssh2_session_hostkey() "
"hostkey missing length");
return 1;
}
len_str =
((uint32_t)((unsigned char)hostkey[0]) << 24) |
((uint32_t)((unsigned char)hostkey[1]) << 16) |
((uint32_t)((unsigned char)hostkey[2]) << 8) |
((uint32_t)((unsigned char)hostkey[3]));
hostkey_str = hostkey + 4;
if(len_str > len - 4) {
print_last_session_error("libssh2_session_hostkey() "
"hostkey too short");
return 1;
}
if(SSH2_IS_LITERAL(hostkey_str, len_str, "ssh-ed25519"))
rc = ssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_ED25519_HOSTKEY,
sizeof(EXPECTED_ED25519_HOSTKEY) - 1);
else if(type == LIBSSH2_HOSTKEY_TYPE_ECDSA_256)
else if(SSH2_IS_LITERAL(hostkey_str, len_str, "ecdsa-sha2-nistp256"))
rc = ssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_ECDSA_HOSTKEY,
sizeof(EXPECTED_ECDSA_HOSTKEY) - 1);
else if(type == LIBSSH2_HOSTKEY_TYPE_RSA)
else if(SSH2_IS_LITERAL(hostkey_str, len_str, "ssh-rsa"))
rc = ssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_RSA_HOSTKEY,
sizeof(EXPECTED_RSA_HOSTKEY) - 1);
else if(SSH2_IS_LITERAL(hostkey_str, len_str,
"ssh-ed25519-cert-v01@openssh.com")) {
fprintf(stderr, "Signed hostkey: %.*s\n", (int)len_str, hostkey_str);
return 0;
}
else {
fprintf(stderr, "Unexpected type of hostkey: %d\n", type);
fprintf(stderr, "Unexpected type of hostkey: %d: %.*s\n",
type, (int)len_str, hostkey_str);
return 1;
}

View File

@@ -67,6 +67,7 @@ int test(LIBSSH2_SESSION *session)
char buf[SHA256_HASH_SIZE * 2 + 1];
const char *hostkey;
const char *hostkey_str;
#if LIBSSH2_MD5
const char *md5_hash;
#endif
@@ -74,6 +75,7 @@ int test(LIBSSH2_SESSION *session)
const char *sha256_hash;
int type;
size_t len;
size_t len_str;
/* these are the host keys under test, they are currently unused */
(void)EXPECTED_RSA_HOSTKEY;
@@ -86,7 +88,33 @@ int test(LIBSSH2_SESSION *session)
return 1;
}
if(type == LIBSSH2_HOSTKEY_TYPE_ED25519) {
if(len < 4) {
print_last_session_error("libssh2_session_hostkey() "
"hostkey missing length");
return 1;
}
len_str =
((uint32_t)((unsigned char)hostkey[0]) << 24) |
((uint32_t)((unsigned char)hostkey[1]) << 16) |
((uint32_t)((unsigned char)hostkey[2]) << 8) |
((uint32_t)((unsigned char)hostkey[3]));
hostkey_str = hostkey + 4;
if(len_str > len - 4) {
print_last_session_error("libssh2_session_hostkey() "
"hostkey too short");
return 1;
}
if(SSH2_IS_LITERAL(hostkey_str, len_str, "ssh-ed25519")) {
if(type != LIBSSH2_HOSTKEY_TYPE_ED25519) {
fprintf(stderr,
"libssh2_session_hostkey() type mismatch %.*s != %d\n",
(int)len_str, hostkey_str, LIBSSH2_HOSTKEY_TYPE_ED25519);
return 1;
}
sha256_hash = libssh2_hostkey_hash(session,
LIBSSH2_HOSTKEY_HASH_SHA256);
@@ -105,7 +133,14 @@ int test(LIBSSH2_SESSION *session)
return 1;
}
}
else if(type == LIBSSH2_HOSTKEY_TYPE_ECDSA_256) {
else if(SSH2_IS_LITERAL(hostkey_str, len_str, "ecdsa-sha2-nistp256")) {
if(type != LIBSSH2_HOSTKEY_TYPE_ECDSA_256) {
fprintf(stderr,
"libssh2_session_hostkey() type mismatch %.*s != %d\n",
(int)len_str, hostkey_str, LIBSSH2_HOSTKEY_TYPE_ECDSA_256);
return 1;
}
#if LIBSSH2_MD5
md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
@@ -158,7 +193,14 @@ int test(LIBSSH2_SESSION *session)
return 1;
}
}
else if(type == LIBSSH2_HOSTKEY_TYPE_RSA) {
else if(SSH2_IS_LITERAL(hostkey_str, len_str, "ssh-rsa")) {
if(type != LIBSSH2_HOSTKEY_TYPE_RSA) {
fprintf(stderr,
"libssh2_session_hostkey() type mismatch %.*s != %d\n",
(int)len_str, hostkey_str, LIBSSH2_HOSTKEY_TYPE_RSA);
return 1;
}
#if LIBSSH2_MD5
md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
@@ -211,8 +253,12 @@ int test(LIBSSH2_SESSION *session)
return 1;
}
}
else if(SSH2_IS_LITERAL(hostkey_str, len_str,
"ssh-ed25519-cert-v01@openssh.com"))
fprintf(stderr, "Signed hostkey: %.*s\n", (int)len_str, hostkey_str);
else {
fprintf(stderr, "Unexpected type of hostkey: %d\n", type);
fprintf(stderr, "Unexpected type of hostkey: %d: %.*s\n",
type, (int)len_str, hostkey_str);
return 1;
}

View File

@@ -23,6 +23,12 @@ while read -r test; do
else
res='not ok'
fi
elif [[ "${test}" = *'-cert-'* ]]; then
if FIXTURE_TEST_HOSTKEY="${test}" ${LIBSSH2_TEST_EXE_RUNNER:-} "${testbin}"; then
res='ok'
else
res='not ok'
fi
else
if FIXTURE_TEST_CRYPT="${test}" ${LIBSSH2_TEST_EXE_RUNNER:-} "${testbin}"; then
res='ok'

View File

@@ -17,3 +17,4 @@ hmac-sha2-256
hmac-sha2-256-etm@openssh.com
hmac-sha2-512
hmac-sha2-512-etm@openssh.com
ssh-ed25519-cert-v01@openssh.com

View File

@@ -86,6 +86,7 @@ chmod go-rwx \
-o 'PidFile sshd.pid' \
-o "AuthorizedKeysFile ${PUBKEY} ${d}/openssh_server/authorized_keys" \
-o "TrustedUserCAKeys ${cakeys}" \
-o "HostCertificate ${d}/openssh_server/ssh_host_ed25519_key-cert.pub" \
-o 'KbdInteractiveAuthentication yes' \
-o 'PermitRootLogin yes' \
-D \