diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index d09d9331..30e31de4 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -242,6 +242,11 @@ enum ssh_keytypes_e{ SSH_KEYTYPE_ECDSA }; +enum ssh_keycmp_e { + SSH_KEY_CMP_PUBLIC = 0, + SSH_KEY_CMP_PRIVATE +}; + /* Error return codes */ #define SSH_OK 0 /* No error */ #define SSH_ERROR -1 /* Error of some kind */ @@ -458,6 +463,9 @@ LIBSSH_API const char *ssh_key_type_to_char(enum ssh_keytypes_e type); LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name); LIBSSH_API int ssh_key_is_public(const ssh_key k); LIBSSH_API int ssh_key_is_private(const ssh_key k); +LIBSSH_API int ssh_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, ssh_key *pkey); diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index 6be297ed..7a664cf1 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -36,6 +36,9 @@ void _ssh_pki_log(const char *function, 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_dss(ssh_key key, int parameter); +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); /* SSH Private Key Functions */ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey); diff --git a/include/libssh/server.h b/include/libssh/server.h index 9ad53e42..f3e0bb53 100644 --- a/include/libssh/server.h +++ b/include/libssh/server.h @@ -307,6 +307,7 @@ LIBSSH_API const char *ssh_message_auth_password(ssh_message msg); * @return The public key or NULL. * * @see ssh_key_dup() + * @see ssh_key_cmp() * @see ssh_message_get() * @see ssh_message_type() */ diff --git a/src/pki.c b/src/pki.c index c6f0e5f8..c3ff3394 100644 --- a/src/pki.c +++ b/src/pki.c @@ -257,6 +257,40 @@ int ssh_key_is_private(const ssh_key k) { return (k->flags & SSH_KEY_FLAG_PRIVATE); } +/** + * @brief Compare keys if they are equal. + * + * @param[in] k1 The first key to compare. + * + * @param[in] k2 The second key to compare. + * + * @param[in] what What part or type of the key do you want to compare. + * + * @return 0 if equal, 1 if not. + */ +int ssh_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + if (k1 == NULL || k2 == NULL) { + return 1; + } + + if (k1->type != k2->type) { + ssh_pki_log("key types don't macth!"); + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (!ssh_key_is_private(k1) || + !ssh_key_is_private(k2)) { + return 1; + } + } + + return pki_key_compare(k1, k2, what); +} + ssh_signature ssh_signature_new(void) { struct ssh_signature_struct *sig; diff --git a/src/pki_crypto.c b/src/pki_crypto.c index 850ea668..41505a7c 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -267,6 +267,64 @@ int pki_key_generate_dss(ssh_key key, int parameter){ return SSH_OK; } +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + switch (k1->type) { + case SSH_KEYTYPE_DSS: + if (DSA_size(k1->dsa) != DSA_size(k2->dsa)) { + return 1; + } + if (bignum_cmp(k1->dsa->p, k2->dsa->p) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->q, k2->dsa->q) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->g, k2->dsa->g) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->pub_key, k2->dsa->pub_key) != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (bignum_cmp(k1->dsa->priv_key, k2->dsa->priv_key) != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) { + return 1; + } + if (bignum_cmp(k1->rsa->e, k2->rsa->e) != 0) { + return 1; + } + if (bignum_cmp(k1->rsa->n, k2->rsa->n) != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (bignum_cmp(k1->rsa->p, k2->rsa->p) != 0) { + return 1; + } + + if (bignum_cmp(k1->rsa->q, k2->rsa->q) != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + return 1; + } + + return 0; +} + ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase, ssh_auth_callback auth_fn, diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 2bb99d24..6a3a9c68 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -993,6 +993,105 @@ int pki_key_generate_dss(ssh_key key, int parameter){ return pki_key_generate(key, parameter, "dsa", SSH_KEYTYPE_DSS); } +static int _bignum_cmp(const gcry_sexp_t s1, + const gcry_sexp_t s2, + const char *what) +{ + gcry_sexp_t sexp; + bignum b1; + bignum b2; + + sexp = gcry_sexp_find_token(s1, what, 0); + if (sexp == NULL) { + return 1; + } + b1 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b1 == NULL) { + return 1; + } + + sexp = gcry_sexp_find_token(s2, what, 0); + if (sexp == NULL) { + return 1; + } + b2 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b2 == NULL) { + return 1; + } + + if (bignum_cmp(b1, b2) != 0) { + return 1; + } + + return 0; +} + +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + switch (k1->type) { + case SSH_KEYTYPE_DSS: + if (_bignum_cmp(k1->dsa, k2->dsa, "p") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "q") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "g") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "y") != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (_bignum_cmp(k1->dsa, k2->dsa, "x") != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "n") != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (_bignum_cmp(k1->rsa, k2->rsa, "d") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "p") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "q") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "u") != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + return 1; + } + + return 0; +} + ssh_string pki_publickey_to_blob(const ssh_key key) { ssh_buffer buffer; diff --git a/tests/unittests/torture_pki.c b/tests/unittests/torture_pki.c index 9812f605..ca6e04b2 100644 --- a/tests/unittests/torture_pki.c +++ b/tests/unittests/torture_pki.c @@ -517,6 +517,9 @@ static void torture_pki_duplicate_key_rsa(void **state) assert_string_equal(b64_key, b64_key_gen); + rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + ssh_key_free(pubkey); ssh_key_free(privkey); ssh_key_free(privkey_dup); @@ -560,6 +563,9 @@ static void torture_pki_duplicate_key_dsa(void **state) assert_string_equal(b64_key, b64_key_gen); + rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + ssh_key_free(pubkey); ssh_key_free(privkey); ssh_key_free(privkey_dup);