mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
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:
@@ -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
180
src/pki.c
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user