pki: add ssh_key_type_and_hash_from_signature_name()

Merge ssh_key_type_from_signature_name() and ssh_key_hash_from_name()
into a single function ssh_key_type_and_hash_from_signature_name() to:

- Avoid double string comparisons on the same algorithm name
- Return SSH_ERROR on unknown/NULL input instead of silently returning SSH_DIGEST_AUTO
- Use strlen() before strcmp() to short-circuit string comparisons.

Handle GSSAPI "null" hostkey case in wrapper.c.
Add unit tests for the new function.

Fixes: https://gitlab.com/libssh/libssh-mirror/-/issues/355
Signed-off-by: Haythem666 <haythem.farhat@epfl.ch>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Haythem666
2026-03-13 16:03:58 +01:00
committed by Jakub Jelen
parent 9f7c596ca5
commit 01772c4f79
4 changed files with 226 additions and 57 deletions

View File

@@ -130,14 +130,15 @@ extern "C" {
/* SSH Key Functions */
void ssh_key_clean (ssh_key key);
const char *
ssh_key_get_signature_algorithm(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name);
const char *ssh_key_get_signature_algorithm(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type);
enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_digest_e ssh_key_hash_from_name(const char *name);
int ssh_key_type_and_hash_from_signature_name(const char *name,
enum ssh_keytypes_e *type,
enum ssh_digest_e *hash_type);
#define is_ecdsa_key_type(t) \
((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521)

180
src/pki.c
View File

@@ -449,40 +449,137 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
/* We should never reach this */
return NULL;
}
enum ssh_digest_e ssh_key_hash_from_name(const char *name)
/**
* @brief Convert a signature algorithm name to a key type and hash type.
*
* Looks up the given signature algorithm name and returns both the
* corresponding key type and digest algorithm in a single call,
* avoiding double string comparisons on the same input.
*
* @param[in] name The signature algorithm name to convert (e.g.
* "ssh-rsa", "rsa-sha2-256", "ecdsa-sha2-nistp256").
*
* @param[out] type A pointer to store the resulting key type.
*
* @param[out] hash_type A pointer to store the resulting hash/digest type.
*
* @return SSH_OK on success, SSH_ERROR if name is NULL or
* unknown.
*/
int ssh_key_type_and_hash_from_signature_name(const char *name,
enum ssh_keytypes_e *type,
enum ssh_digest_e *hash_type)
{
if (name == NULL) {
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
size_t len;
if (name == NULL || type == NULL || hash_type == NULL) {
return SSH_ERROR;
}
if (strcmp(name, "ssh-rsa") == 0) {
return SSH_DIGEST_SHA1;
} else if (strcmp(name, "rsa-sha2-256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "rsa-sha2-512") == 0) {
return SSH_DIGEST_SHA512;
} else if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
return SSH_DIGEST_SHA384;
} else if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
return SSH_DIGEST_SHA512;
} else if (strcmp(name, "ssh-ed25519") == 0) {
return SSH_DIGEST_AUTO;
} else if (strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
return SSH_DIGEST_AUTO;
len = strlen(name);
if (len == 7 && strcmp(name, "ssh-rsa") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA1;
return SSH_OK;
}
if (len == 11 && strcmp(name, "ssh-ed25519") == 0) {
*type = SSH_KEYTYPE_ED25519;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 12) {
if (strcmp(name, "rsa-sha2-256") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "rsa-sha2-512") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 19) {
if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
*type = SSH_KEYTYPE_ECDSA_P256;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
*type = SSH_KEYTYPE_ECDSA_P384;
*hash_type = SSH_DIGEST_SHA384;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
*type = SSH_KEYTYPE_ECDSA_P521;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 26 && strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
*type = SSH_KEYTYPE_SK_ED25519;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 28 && strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA1;
return SSH_OK;
}
if (len == 32 && strcmp(name, "ssh-ed25519-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ED25519_CERT01;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 33) {
if (strcmp(name, "rsa-sha2-256-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "rsa-sha2-512-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 34 && strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
*type = SSH_KEYTYPE_SK_ECDSA;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (len == 40) {
if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P256_CERT01;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P384_CERT01;
*hash_type = SSH_DIGEST_SHA384;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P521_CERT01;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
return SSH_ERROR;
}
/**
* @brief Checks the given key against the configured allowed
* public key algorithm types
@@ -700,27 +797,6 @@ ssh_key_get_signature_algorithm(ssh_session session,
return ssh_key_signature_to_char(type, hash_type);
}
/**
* @brief Convert a ssh key algorithm name to a ssh key algorithm type.
*
* @param[in] name The name to convert.
*
* @return The enum ssh key algorithm type.
*/
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
if (name == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
if ((strcmp(name, "rsa-sha2-256") == 0) ||
(strcmp(name, "rsa-sha2-512") == 0)) {
return SSH_KEYTYPE_RSA;
}
/* Otherwise the key type matches the signature type */
return ssh_key_type_from_name(name);
}
/**
* @brief Convert a ssh key name to a ssh key type.
*
@@ -2899,8 +2975,12 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
}
alg = ssh_string_get_char(algorithm);
type = ssh_key_type_from_signature_name(alg);
hash_type = ssh_key_hash_from_name(alg);
rc = ssh_key_type_and_hash_from_signature_name(alg, &type, &hash_type);
if (rc != SSH_OK) {
SSH_BUFFER_FREE(buf);
SSH_STRING_FREE(algorithm);
return SSH_ERROR;
}
SSH_STRING_FREE(algorithm);
blob = ssh_buffer_get_ssh_string(buf);

View File

@@ -437,6 +437,7 @@ int crypt_set_algorithms_server(ssh_session session){
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
int cmp;
int rc;
if (session == NULL) {
return SSH_ERROR;
@@ -580,8 +581,24 @@ int crypt_set_algorithms_server(ssh_session session){
}
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
session->srv.hostkey = ssh_key_type_from_signature_name(method);
session->srv.hostkey_digest = ssh_key_hash_from_name(method);
/* For GSSAPI key exchange, hostkey algorithm may be "null" */
if (strcmp(method, "null") == 0) {
session->srv.hostkey = SSH_KEYTYPE_UNKNOWN;
session->srv.hostkey_digest = SSH_DIGEST_AUTO;
} else {
rc = ssh_key_type_and_hash_from_signature_name(
method,
&session->srv.hostkey,
&session->srv.hostkey_digest);
if (rc != SSH_OK) {
ssh_set_error(session,
SSH_FATAL,
"unknown hostkey algorithm %s",
method);
return SSH_ERROR;
}
}
/* setup DH key exchange type */
switch (session->next_crypto->kex_type) {

View File

@@ -107,6 +107,76 @@ static void torture_pki_signature(void **state)
ssh_signature_free(sig);
}
static void torture_pki_type_and_hash_from_signature_name(void **state)
{
enum ssh_keytypes_e type;
enum ssh_digest_e hash_type;
int rc;
(void)state; /* unused */
/* Test NULL input */
rc = ssh_key_type_and_hash_from_signature_name(NULL, &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
/* Test NULL output pointers */
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", NULL, &hash_type);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, NULL);
assert_int_equal(rc, SSH_ERROR);
/* Test unknown name */
rc = ssh_key_type_and_hash_from_signature_name("unknown-algo",
&type,
&hash_type);
assert_int_equal(rc, SSH_ERROR);
/* Test valid RSA signatures */
rc =
ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, &hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA1);
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-256",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-512",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA512);
/* Test valid ECDSA signatures */
rc = ssh_key_type_and_hash_from_signature_name("ecdsa-sha2-nistp256",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_ECDSA_P256);
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
/* Test valid ED25519 signature */
rc = ssh_key_type_and_hash_from_signature_name("ssh-ed25519",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_ED25519);
assert_int_equal(hash_type, SSH_DIGEST_AUTO);
/* Test that loose key names are rejected */
rc = ssh_key_type_and_hash_from_signature_name("ecdsa", &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_key_type_and_hash_from_signature_name("rsa", &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
}
struct key_attrs {
int sign;
int verify;
@@ -415,6 +485,7 @@ int torture_run_tests(void) {
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_pki_keytype),
cmocka_unit_test(torture_pki_signature),
cmocka_unit_test(torture_pki_type_and_hash_from_signature_name),
cmocka_unit_test_setup_teardown(torture_pki_verify_mismatch,
setup_cert_dir,
teardown_cert_dir),