[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] dh: Added ssh_print_hash() and ssh_get_b64_unpadded().


From 1a54e30e1ef21f73547171a140ec5ed6b87f4861 Mon Sep 17 00:00:00 2001
From: Jan-Niklas Burfeind <libssh@xxxxxxxxxxxx>
Date: Thu, 9 Aug 2018 11:00:00 +0200
Subject: [PATCH] dh: Added ssh_print_hash() and ssh_get_b64_unpadded().

The first is capable of producing md5, sha1 and sha256 fingerprint strings.
The latter is the base64 pendant to ssh_get_hexa().

There are now three unittests for the different hash functions.

Signed-off-by: Jan-Niklas Burfeind <libssh@xxxxxxxxxxxx>
---
 include/libssh/libssh.h          |   5 +-
 src/dh.c                         | 103 ++++++++++++++++++++++++++++
 tests/unittests/CMakeLists.txt   |   1 +
 tests/unittests/torture_hashes.c | 114 +++++++++++++++++++++++++++++++
 4 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 tests/unittests/torture_hashes.c

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index e78b8a27..9e927bdc 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -488,6 +488,7 @@ LIBSSH_API int ssh_channel_listen_forward(ssh_session session,
                                           int *bound_port);
 
 LIBSSH_API void ssh_free(ssh_session session);
+LIBSSH_API char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len);
 LIBSSH_API const char *ssh_get_disconnect_message(ssh_session session);
 LIBSSH_API const char *ssh_get_error(void *error);
 LIBSSH_API int ssh_get_error_code(void *error);
@@ -500,7 +501,8 @@ LIBSSH_API int ssh_get_server_publickey(ssh_session session, ssh_key *key);
 
 enum ssh_publickey_hash_type {
     SSH_PUBLICKEY_HASH_SHA1,
-    SSH_PUBLICKEY_HASH_MD5
+    SSH_PUBLICKEY_HASH_MD5,
+    SSH_PUBLICKEY_HASH_SHA256
 };
 LIBSSH_API int ssh_get_publickey_hash(const ssh_key key,
                                       enum ssh_publickey_hash_type type,
@@ -652,6 +654,7 @@ LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key,
 
 LIBSSH_API const char *ssh_pki_key_ecdsa_name(const ssh_key key);
 
+LIBSSH_API void ssh_print_hash(enum ssh_publickey_hash_type type, unsigned char *hash, size_t len);
 LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len);
 LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data);
 LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display);
diff --git a/src/dh.c b/src/dh.c
index e0d2965a..28eeadb3 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -1142,6 +1142,29 @@ int ssh_get_publickey_hash(const ssh_key key,
             *hlen = SHA_DIGEST_LEN;
         }
         break;
+    case SSH_PUBLICKEY_HASH_SHA256:
+        {
+            SHA256CTX ctx;
+
+            h = malloc(SHA256_DIGEST_LEN);
+            if (h == NULL) {
+                rc = -1;
+                goto out;
+            }
+
+            ctx = sha256_init();
+            if (ctx == NULL) {
+                free(h);
+                rc = -1;
+                goto out;
+            }
+
+            sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+            sha256_final(h, ctx);
+
+            *hlen = SHA256_DIGEST_LEN;
+        }
+        break;
     case SSH_PUBLICKEY_HASH_MD5:
         {
             MD5CTX ctx;
@@ -1177,6 +1200,37 @@ out:
     return rc;
 }
 
+/**
+ * @brief Convert a buffer into an unpadded base64 string.
+ * The caller has to free the memory.
+ *
+ * @param  hash         What should be converted to a base64 string.
+ *
+ * @param  len          Length of the buffer to convert.
+ *
+ * @return              The base64 string or NULL on error.
+ *
+ * @see ssh_string_free_char()
+ */
+char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len) {
+    char *b64_padded, *b64_unpadded;
+    size_t k;
+
+    b64_padded = (char *) bin_to_base64(hash, (int) len);
+    if (b64_padded == NULL) {
+        return NULL;
+    }
+    k=strlen(b64_padded);
+    for (; b64_padded[k-1]=='=';k--);
+
+    b64_unpadded = malloc(k);
+    strncpy(b64_unpadded, b64_padded, k);
+    b64_unpadded[k] = '\0';
+
+    free(b64_padded);
+    return b64_unpadded;
+}
+
 /**
  * @brief Convert a buffer into a colon separated hex string.
  * The caller has to free the memory.
@@ -1214,6 +1268,55 @@ char *ssh_get_hexa(const unsigned char *what, size_t len) {
   return hexa;
 }
 
+/**
+ * @brief Print a hash as a human-readable hex- or base64-string.
+ *
+ * This function prints hex strings if the given hash is a md5 sum.
+ * But prints unpadded base64 strings for sha sums.
+ * Either way, the output is prepended by the hash-type.
+ *
+ * @param  type         Which sort of hash is given.
+ *
+ * @param  hash         What should be converted to a base64 string.
+ *
+ * @param  len          Length of the buffer to convert.
+ */
+void ssh_print_hash(enum ssh_publickey_hash_type type, unsigned char *hash, size_t len) {
+    char *fingerprint=NULL;
+
+    switch (type) {
+        case SSH_PUBLICKEY_HASH_SHA1:
+        case SSH_PUBLICKEY_HASH_SHA256:
+            printf("len: %zu\n", len);
+            fingerprint = ssh_get_b64_unpadded(hash, len);
+            break;
+        case SSH_PUBLICKEY_HASH_MD5:
+            fingerprint = ssh_get_hexa(hash, len);
+        default:
+            break;
+
+    }
+    if (fingerprint == NULL) {
+        return;
+    }
+
+    switch (type) {
+        case SSH_PUBLICKEY_HASH_MD5:
+            fprintf(stderr, "%s:%s\n", "MD5", fingerprint);
+            break;
+        case SSH_PUBLICKEY_HASH_SHA1:
+            fprintf(stderr, "%s:%s\n", "SHA1", fingerprint);
+            break;
+        case SSH_PUBLICKEY_HASH_SHA256:
+            fprintf(stderr, "%s:%s\n", "SHA256", fingerprint);
+            break;
+        default:
+            break;
+    }
+
+    free(fingerprint);
+}
+
 /**
  * @brief Print a buffer as colon separated hex string.
  *
diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt
index ae942950..3255e72f 100644
--- a/tests/unittests/CMakeLists.txt
+++ b/tests/unittests/CMakeLists.txt
@@ -12,6 +12,7 @@ add_cmocka_test(torture_config torture_config.c ${TORTURE_LIBRARY})
 add_cmocka_test(torture_options torture_options.c ${TORTURE_LIBRARY})
 add_cmocka_test(torture_isipaddr torture_isipaddr.c ${TORTURE_LIBRARY})
 add_cmocka_test(torture_knownhosts_parsing torture_knownhosts_parsing.c ${TORTURE_LIBRARY})
+add_cmocka_test(torture_hashes torture_hashes.c ${TORTURE_LIBRARY})
 
 if (CMAKE_USE_PTHREADS_INIT)
     add_cmocka_test(torture_rand torture_rand.c ${TORTURE_LIBRARY})
diff --git a/tests/unittests/torture_hashes.c b/tests/unittests/torture_hashes.c
new file mode 100644
index 00000000..d638012f
--- /dev/null
+++ b/tests/unittests/torture_hashes.c
@@ -0,0 +1,114 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "torture_key.h"
+#include "legacy.c"
+#include "dh.c"
+
+static int setup_rsa_key(void **state)
+{
+    int rc=0;
+    enum ssh_keytypes_e type;
+    char *b64_key, *p;
+    ssh_key key;
+
+    const char *q;
+
+    b64_key = strdup(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0));
+    assert_true(b64_key != NULL);
+
+    q = p = b64_key;
+    while (*p != ' ') p++;
+    *p = '\0';
+
+    type = ssh_key_type_from_name(q);
+    assert_true(type == SSH_KEYTYPE_RSA);
+
+    q = ++p;
+    while (*p != ' ') p++;
+    *p = '\0';
+
+    rc = ssh_pki_import_pubkey_base64(q, type, &key);
+    assert_true(rc == 0);
+
+    free(b64_key);
+    *state = key;
+
+    return 0;
+}
+
+static int teardown(void **state)
+{
+    ssh_key_free(*state);
+    return 0;
+}
+
+static void torture_md5_hash(void **state) {
+    int rc=0;
+    unsigned char *hash = NULL;
+    char *hexa;
+    size_t hlen;
+    ssh_key pubkey = *state;
+
+    rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen);
+    assert_true(rc == 0);
+
+    hexa = ssh_get_hexa(hash, hlen);
+    assert_string_equal(hexa, "50:15:a0:9b:92:bf:33:1c:01:c5:8c:fe:18:fa:ce:78");
+
+    ssh_string_free_char(hexa);
+}
+
+static void torture_sha1_hash(void **state) {
+    int rc=0;
+    unsigned char *hash = NULL;
+    char *sha1;
+    size_t hlen;
+    ssh_key pubkey = *state;
+
+    rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen);
+    assert_true(rc == 0);
+
+    sha1 = ssh_get_b64_unpadded(hash, hlen);
+    assert_string_equal(sha1, "6wP+houujQmxLBiFugTcoeoODCM");
+
+    ssh_string_free_char(sha1);
+}
+
+static void torture_sha256_hash(void **state) {
+    int rc=0;
+    unsigned char *hash = NULL;
+    char *sha256;
+    size_t hlen;
+    ssh_key pubkey = *state;
+
+    rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen);
+    assert_true(rc == 0);
+
+    sha256 = ssh_get_b64_unpadded(hash, hlen);
+    assert_string_equal(sha256, "jXstVLLe84fSDo1kEYGn6iumnPCSorhaiWxnJz8VTII");
+
+    ssh_string_free_char(sha256);
+
+}
+
+int torture_run_tests(void) {
+    int rc;
+    struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(torture_md5_hash,
+                                        setup_rsa_key,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_sha1_hash,
+                                        setup_rsa_key,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_sha256_hash,
+                                        setup_rsa_key,
+                                        teardown),
+    };
+
+    torture_filter_tests(tests);
+    rc = cmocka_run_group_tests(tests, NULL, NULL);
+    return rc;
+}
-- 
2.18.0


Archive administrator: postmaster@lists.cynapses.org