ssh: expose both hashes The user may have the data hashed as MD5 or SHA-1, so we should provide both types for consumption.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
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;
}