summaryrefslogtreecommitdiff
path: root/src/protocol/internal/ssh_rsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol/internal/ssh_rsa.c')
-rw-r--r--src/protocol/internal/ssh_rsa.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/protocol/internal/ssh_rsa.c b/src/protocol/internal/ssh_rsa.c
new file mode 100644
index 0000000..2bd328c
--- /dev/null
+++ b/src/protocol/internal/ssh_rsa.c
@@ -0,0 +1,127 @@
+#include <string.h>
+#include "ssh_pubkey_crypto.h"
+#include "ssh_rsa.h"
+#include "buffer.h"
+
+static RSA *CreateRSAContext(unsigned char *hostkey, int len)
+{
+ int algo_len, e_len, n_len;
+ RSA *rsa = RSA_new();
+ BIGNUM *rsa_e = BN_new();
+ BIGNUM *rsa_n = BN_new();
+
+ if (len >= 4)
+ algo_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ hostkey += 4;
+ len -= 4;
+
+ if (!(len >= 7 && algo_len == 7 && memcmp(hostkey, "ssh-rsa", 7) == 0))
+ goto fail;
+ hostkey += 7;
+ len -= 7;
+
+ if (len >= 4)
+ e_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ if (len >= 4 + e_len)
+ BN_mpi2bn(hostkey, 4 + e_len, rsa_e);
+ else
+ goto fail;
+ hostkey += 4 + e_len;
+ len -= 4 + e_len;
+
+ if (len >= 4)
+ n_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ if (len >= 4 + n_len)
+ BN_mpi2bn(hostkey, 4 + n_len, rsa_n);
+ else
+ goto fail;
+ hostkey += 4 + n_len;
+ len -= 4 + n_len;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_key(rsa, rsa_n, rsa_e, NULL);
+#else
+ rsa->n = rsa_n;
+ rsa->e = rsa_e;
+#endif
+
+ return rsa;
+fail:
+ BN_clear_free(rsa_e);
+ BN_clear_free(rsa_n);
+ RSA_free(rsa);
+ return NULL;
+}
+
+static inline const uint8_t *get_str(const uint8_t **data, uint32_t *L)
+{
+ const uint8_t *ptr = *data;
+ uint32_t len = be32toh(*(uint32_t *)ptr);
+ *data = ptr + 4 + len;
+ *L = len;
+ return ptr + 4;
+}
+
+int verifyRSAKey(ssh_session *ss, const uint8_t *data, size_t len)
+{
+ const uint8_t *next = data;
+
+ uint32_t K_S_len;
+ const uint8_t *K_S;
+ K_S = get_str(&next, &K_S_len);
+ ss->K_S_len = K_S_len;
+ ss->K_S = (uint8_t *)malloc(K_S_len);
+ memcpy(ss->K_S, K_S, K_S_len);
+
+ uint32_t mpint_f_len;
+ const uint8_t *mpint_f;
+ mpint_f = get_str(&next, &mpint_f_len);
+
+ if (ssh_dh_compute_secret(ss->dh, mpint_f, mpint_f_len) < 0)
+ return -ESECRET;
+
+ uint32_t s_len;
+ const uint8_t *s;
+ s = get_str(&next, &s_len);
+
+ buffer vbuf;
+ buffer_init(&vbuf);
+ buffer_append_string(&vbuf, ss->V_C, strlen(ss->V_C));
+ buffer_append_string(&vbuf, ss->V_S, strlen(ss->V_S));
+ buffer_append_string(&vbuf, ss->I_C, ss->I_C_len);
+ buffer_append_string(&vbuf, ss->I_S, ss->I_S_len);
+ buffer_append_string(&vbuf, (const char *)K_S, K_S_len);
+ buffer_append(&vbuf, ss->dh->mpint_e, ss->dh->e_len);
+ buffer_append_string(&vbuf, (const char *)mpint_f, mpint_f_len);
+ buffer_append(&vbuf, ss->dh->secret, ss->dh->secret_len);
+
+ ssh_dh_hash(ss->dh, buffer_data(&vbuf), ss->H, buffer_len(&vbuf));
+
+ buffer_deinit(&vbuf);
+
+ // Start verify
+ // ssh-rsa specifies SHA-1 hashing
+ unsigned char s_H[SHA_DIGEST_LENGTH];
+ SHA1(ss->H, ss->dh->digest.hashlen, s_H);
+
+ // Ignore the first 15 bytes of the signature of H sent from server:
+ // algorithm_name_length[4], algorithm_name[7]("ssh-rsa") and signature_length[4].
+ RSA *rsactx = CreateRSAContext((unsigned char *)K_S, K_S_len);
+ if (rsactx == NULL)
+ return -ERSA;
+
+ int sig_len = s_len - 15;
+ const uint8_t *sig = s + 15;
+ int res = RSA_verify(NID_sha1, s_H, SHA_DIGEST_LENGTH, sig, sig_len,
+ rsactx);
+
+ RSA_free(rsactx);
+
+ return (res == 1);
+}