diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee76f95c..bd0010e5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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() diff --git a/tests/Makefile.inc b/tests/Makefile.inc index f60db0ca..1351362d 100644 --- a/tests/Makefile.inc +++ b/tests/Makefile.inc @@ -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 \ diff --git a/tests/keys-generate.sh b/tests/keys-generate.sh index 4a59bab5..492ab0e7 100755 --- a/tests/keys-generate.sh +++ b/tests/keys-generate.sh @@ -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 diff --git a/tests/keys/ca_host_ed25519 b/tests/keys/ca_host_ed25519 new file mode 100644 index 00000000..fc8ae5c7 --- /dev/null +++ b/tests/keys/ca_host_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBQPH6E3OfiBQqrXDDQi6Ie4a5qL73t2O6UaKQfERGN0AAAAJixJvUssSb1 +LAAAAAtzc2gtZWQyNTUxOQAAACBQPH6E3OfiBQqrXDDQi6Ie4a5qL73t2O6UaKQfERGN0A +AAAEDev+bTt9bTxlqtJLomzOYzNAmw4i3iZ7eDydUREXkMpFA8foTc5+IFCqtcMNCLoh7h +rmovve3Y7pRopB8REY3QAAAAD2NhX2hvc3RfZWQyNTUxOQECAwQFBg== +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/keys/ca_host_ed25519.pub b/tests/keys/ca_host_ed25519.pub new file mode 100644 index 00000000..8402b62f --- /dev/null +++ b/tests/keys/ca_host_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFA8foTc5+IFCqtcMNCLoh7hrmovve3Y7pRopB8REY3Q ca_host_ed25519 diff --git a/tests/openssh_server/Dockerfile b/tests/openssh_server/Dockerfile index 67dc87a7..1fc86962 100644 --- a/tests/openssh_server/Dockerfile +++ b/tests/openssh_server/Dockerfile @@ -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 diff --git a/tests/openssh_server/ssh_host_ed25519_key-cert.pub b/tests/openssh_server/ssh_host_ed25519_key-cert.pub new file mode 100644 index 00000000..567b0755 --- /dev/null +++ b/tests/openssh_server/ssh_host_ed25519_key-cert.pub @@ -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 diff --git a/tests/session_fixture.c b/tests/session_fixture.c index 629a64ff..bcda4f1a 100644 --- a/tests/session_fixture.c +++ b/tests/session_fixture.c @@ -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(); diff --git a/tests/test_hostkey.c b/tests/test_hostkey.c index 7ebcb3c9..9d822fb8 100644 --- a/tests/test_hostkey.c +++ b/tests/test_hostkey.c @@ -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; } diff --git a/tests/test_hostkey_hash.c b/tests/test_hostkey_hash.c index 9453753e..49b08aba 100644 --- a/tests/test_hostkey_hash.c +++ b/tests/test_hostkey_hash.c @@ -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; } diff --git a/tests/test_read_algos.test b/tests/test_read_algos.test index cfe0eb77..66075f76 100755 --- a/tests/test_read_algos.test +++ b/tests/test_read_algos.test @@ -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' diff --git a/tests/test_read_algos.txt b/tests/test_read_algos.txt index 70c0a60f..38514c31 100644 --- a/tests/test_read_algos.txt +++ b/tests/test_read_algos.txt @@ -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 diff --git a/tests/test_sshd.test b/tests/test_sshd.test index e4a4a55b..dd4acccd 100755 --- a/tests/test_sshd.test +++ b/tests/test_sshd.test @@ -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 \