Commit 1e0aa105faff1bd6987665506037f299b8e0e1cb

Carlos Martín Nieto 2014-09-16T03:22:09

ssh: expose both hashes The user may have the data hashed as MD5 or SHA-1, so we should provide both types for consumption.

diff --git a/include/git2/transport.h b/include/git2/transport.h
index 6c568be..39df479 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -24,11 +24,11 @@ GIT_BEGIN_DECL
  * Type of SSH host fingerprint
  */
 typedef enum {
-	/** MD5, 16 bytes */
-	GIT_CERT_SSH_MD5,
-	/** SHA-1, 20 bytes */
-	GIT_CERT_SSH_SHA1,
-} git_cert_ssh_type ;
+	/** MD5 is available */
+	GIT_CERT_SSH_MD5 = (1 << 0),
+	/** SHA-1 is available */
+	GIT_CERT_SSH_SHA1 = (1 << 1),
+} git_cert_ssh_t;
 
 /**
  * Hostkey information taken from libssh2
@@ -43,12 +43,19 @@ typedef struct {
          * A hostkey type from libssh2, either
          * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
          */
-	git_cert_ssh_type type;
+	git_cert_ssh_t type;
+
+        /**
+         * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
+         * have the MD5 hash of the hostkey.
+         */
+	unsigned char hash_md5[16];
+
         /**
-         * Hostkey hash. If the type is MD5, only the first 16 bytes
-         * will be set.
+         * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
+         * have the SHA-1 hash of the hostkey.
          */
-        unsigned char hash[20];
+        unsigned char hash_sha1[20];
 } git_cert_hostkey;
 
 /**
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 7175653..15a45ca 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -487,17 +487,17 @@ static int _git_ssh_setup_conn(
 
 		key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
 		if (key != NULL) {
-			cert.type = GIT_CERT_SSH_SHA1;
-			memcpy(&cert.hash, key, 20);
-		} else {
-			key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
-			if (key != NULL) {
-				cert.type = GIT_CERT_SSH_MD5;
-				memcpy(&cert.hash, key, 16);
-			}
+			cert.type |= GIT_CERT_SSH_SHA1;
+			memcpy(&cert.hash_sha1, key, 20);
 		}
 
-		if (key == NULL) {
+		key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+		if (key != NULL) {
+			cert.type |= GIT_CERT_SSH_MD5;
+			memcpy(&cert.hash_md5, key, 16);
+		}
+
+		if (cert.type == 0) {
 			giterr_set(GITERR_SSH, "unable to get the host key");
 			return -1;
 		}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 0dd746a..42682e8 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -488,13 +488,22 @@ int ssh_certificate_check(git_cert *cert, int valid, void *payload)
 
 	cl_git_pass(git_oid_fromstrp(&expected, expected_str));
 	cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type);
-
 	key = (git_cert_hostkey *) cert;
-	git_oid_fromraw(&actual, key->hash);
 
-	cl_assert_equal_i(GIT_CERT_SSH_SHA1, key->type);
+	/*
+	 * We need to figure out how long our input was to check for
+	 * the type. Here we abuse the fact that both hashes fit into
+	 * our git_oid type.
+	 */
+	if (strlen(expected_str) == 32 && key->type & GIT_CERT_SSH_MD5) {
+		memcpy(&actual.id, key->hash_md5, 16);
+	} else 	if (strlen(expected_str) == 40 && key->type & GIT_CERT_SSH_SHA1) {
+		memcpy(&actual, key->hash_sha1, 20);
+	} else {
+		cl_fail("Cannot find a usable SSH hash");
+	}
 
-	cl_assert(git_oid_equal(&expected, &actual));
+	cl_assert(!memcmp(&expected, &actual, 20));
 
 	return GIT_EUSER;
 }