diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2016-06-02 11:51:02 +0200 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2017-06-08 10:45:54 +0200 |
commit | ef8bc65388506eb1a6eb62d78ffab05984b9ebfa (patch) | |
tree | a5f1fdc2f0b3f671ffc0a963585e223f046408ba | |
parent | 1fcf3768035b28a74914cff65b3af0e0f53186fb (diff) | |
download | libssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.tar.gz libssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.tar.xz libssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.zip |
dh-gex: moduli file parsing
-rw-r--r-- | include/libssh/libcrypto.h | 3 | ||||
-rw-r--r-- | src/dh-gex.c | 178 |
2 files changed, 180 insertions, 1 deletions
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h index 407ae708..0d803c67 100644 --- a/include/libssh/libcrypto.h +++ b/include/libssh/libcrypto.h @@ -80,7 +80,8 @@ typedef BN_CTX* bignum_CTX; BN_bin2bn(data,datalen,(*dest)); \ } while(0) #define bignum_bn2dec(num) BN_bn2dec(num) -#define bignum_dec2bn(bn,data) BN_dec2bn(data,bn) +#define bignum_dec2bn(data, bn) BN_dec2bn(bn, data) +#define bignum_hex2bn(data, bn) BN_hex2bn(bn, data) #define bignum_bn2hex(num, dest) (*dest)=(unsigned char *)BN_bn2hex(num) #define bignum_rand_range(rnd, max) BN_rand_range(rnd, max) #define bignum_ctx_new() BN_CTX_new() diff --git a/src/dh-gex.c b/src/dh-gex.c index 40887c7c..35d7ec97 100644 --- a/src/dh-gex.c +++ b/src/dh-gex.c @@ -21,6 +21,9 @@ * MA 02111-1307, USA. */ +#include <errno.h> +#include <string.h> +#include <stdio.h> #include "libssh/dh-gex.h" #include "libssh/libssh.h" #include "libssh/ssh2.h" @@ -201,3 +204,178 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) { session->session_state=SSH_SESSION_STATE_ERROR; return SSH_PACKET_USED; } + +#ifdef WITH_SERVER + +#define MODULI_FILE "/etc/ssh/moduli" +/* 2 "Safe" prime; (p-1)/2 is also prime. */ +#define SAFE_PRIME 2 +/* 0x04 Probabilistic Miller-Rabin primality tests. */ +#define PRIM_TEST_REQUIRED 0x04 + +/** @internal + * @brief determines if the proposed modulus size is more appropriate than the + * current one. + * @returns 1 if it's more appropriate. Returns 0 if same or less appropriate + */ +static int dhgroup_better_size(int pmin, int pn, int pmax, + int current_size, int proposed_size){ + if (current_size == proposed_size) + return 0; + if (current_size == pn){ + /* can't do better */ + return 0; + } + if (current_size == 0 && proposed_size >= pmin && proposed_size <= pmax) + return 1; + if (proposed_size < pmin || proposed_size > pmax){ + /* out of bounds */ + return 0; + } + if (current_size == 0){ + /* not in the allowed window */ + return 0; + } + if (proposed_size >= pn && proposed_size < current_size) + return 1; + if (proposed_size <= pn && proposed_size > current_size) + return 1; + if (proposed_size >= pn && current_size < pn) + return 1; + /* we're in the allowed window but a better match already exists */ + return 0; +} + +/** @internal + * @brief returns 1 with 1/n probability + * @returns 1 on with P(1/n), 0 with P(n-1/n). + */ +static int invn_chance(int n){ + uint32_t nounce; + ssh_get_random(&nounce, sizeof(nounce), 0); + return (nounce % n) == 0; +} + +/** @internal + * @brief retrieves a DH group from an open moduli file. + */ +static void ssh_retrieve_dhgroup_file(FILE *moduli, int pmin, int pn, int pmax, + int *best_size, char **best_generator, char **best_modulus) { + char *timestamp = NULL, *generator = NULL, *modulus = NULL; + int type, tests, tries, size, proposed_size; + int firstbyte, rc; + int line=0; + int best_nlines = 0; + + while (1){ + line++; + firstbyte= getc(moduli); + if (firstbyte == '#'){ + do { + firstbyte = getc(moduli); + } while(firstbyte != '\n' && firstbyte != EOF); + continue; + } + if (firstbyte == EOF){ + break; + } + ungetc(firstbyte, moduli); + rc = fscanf(moduli, "%ms %d %d %d %d %ms %ms\n", ×tamp, &type, &tests, + &tries, &size, &generator, &modulus); + if (rc != 7){ + SAFE_FREE(timestamp); + SAFE_FREE(generator); + SAFE_FREE(modulus); + if (rc == EOF){ + break; + } + SSH_LOG(SSH_LOG_INFO, "invalid moduli entry line %d", line); + do { + firstbyte = getc(moduli); + } while(firstbyte != '\n' && firstbyte != EOF); + continue; + } + + /* we only want safe primes that were tested */ + if (type != SAFE_PRIME || !(tests & PRIM_TEST_REQUIRED)){ + goto next; + } + + proposed_size = size+1; + if (proposed_size != *best_size && + dhgroup_better_size(pmin, pn, pmax, *best_size, proposed_size)){ + best_nlines = 0; + *best_size = proposed_size; + } + if (proposed_size == *best_size){ + best_nlines++; + } + /* Use reservoir sampling algorithm */ + if (proposed_size == *best_size && invn_chance(best_nlines)){ + SAFE_FREE(*best_generator); + SAFE_FREE(*best_modulus); + *best_generator = generator; + *best_modulus = modulus; + generator = NULL; + modulus = NULL; + } + next: + SAFE_FREE(timestamp); + SAFE_FREE(generator); + SAFE_FREE(modulus); + } + if (*best_size != 0){ + SSH_LOG(SSH_LOG_INFO, "Selected a %d bits modulus out of %d candidates in %d lines", + *best_size, best_nlines-1, line); + } else { + SSH_LOG(SSH_LOG_WARNING, "No moduli found for [%d:%d:%d]", pmin,pn,pmax); + } +} + +/** @internal + * @brief retrieves a DH group from the moduli file based on bits len parameters + * @param[in] pmin minimum group size in bits + * @param[in] pn preferred group size + * @param[in] pmax maximum group size + * @param[out] size size of the chosen modulus + * @param[out] p modulus + * @param[out] g generator + * @return SSH_OK on success, SSH_ERROR otherwise. + */ +static int ssh_retrieve_dhgroup(int pmin, int pn, int pmax, int *size, bignum *p, bignum *g){ + FILE *moduli; + char *generator = NULL; + char *modulus = NULL; + int rc; + + moduli = fopen(MODULI_FILE, "r"); + if(moduli == NULL){ + SSH_LOG(SSH_LOG_WARNING, "Unable to open moduli file: %s",strerror(errno)); + return SSH_ERROR; + } + *size = 0; + ssh_retrieve_dhgroup_file(moduli, pmin, pn, pmax, size, &generator, &modulus); + if (*size == 0){ + return SSH_ERROR; + } + rc = bignum_hex2bn(generator, g); + if (rc == 0){ + goto error; + } + rc = bignum_hex2bn(modulus, p); + if (rc == 0){ + goto error; + } + SAFE_FREE(generator); + SAFE_FREE(modulus); + return SSH_OK; + +error: + bignum_safe_free(*g); + bignum_safe_free(*p); + SAFE_FREE(generator); + SAFE_FREE(modulus); + return SSH_ERROR; +} + +#endif /* WITH_SERVER */ |