X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fgcrypt%2Frsagen.c;h=acf96acf5224aabc7ce6e4d707f90b2642e74d00;hb=77cd819058de43a5fcea54300dde50e03088c318;hp=030f2bcb1111ba04636994a39e9a478ae4d328e5;hpb=3b6d366005b6fc23c705b3156e365316f6ab776c;p=tinc diff --git a/src/gcrypt/rsagen.c b/src/gcrypt/rsagen.c index 030f2bcb..acf96acf 100644 --- a/src/gcrypt/rsagen.c +++ b/src/gcrypt/rsagen.c @@ -20,220 +20,299 @@ #include "system.h" #include +#include + +#include "../rsagen.h" +#include "xalloc.h" +#include "rsa.h" +#include "pem.h" + +// ASN.1 tags. +typedef enum { + TAG_INTEGER = 2, + TAG_SEQUENCE = 16, +} asn1_tag_t; + +static size_t der_tag_len(size_t n) { + if(n < 128) { + return 2; + } -#include "rsagen.h" + if(n < 256) { + return 3; + } -#if 0 -// Base64 encoding table + if(n < 65536) { + return 4; + } -static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + abort(); +} -// PEM encoding +static uint8_t *der_store_tag(uint8_t *p, asn1_tag_t tag, size_t n) { + if(tag == TAG_SEQUENCE) { + tag |= 0x20; + } -static bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) { - bool decode = false; - char line[1024]; - uint32_t word = 0; - int shift = 0; - size_t i, j = 0; + *p++ = tag; + + if(n < 128) { + *p++ = n; + } else if(n < 256) { + *p++ = 0x81; + *p++ = n; + } else if(n < 65536) { + *p++ = 0x82; + *p++ = n >> 8; + *p++ = n & 0xff; + } else { + abort(); + } - fprintf(fp, "-----BEGIN %s-----\n", header); + return p; +} - for(i = 0; i < size; i += 3) { - if(i <= size - 3) { - word = buf[i] << 16 | buf[i + 1] << 8 | buf[i + 2]; - } else { - word = buf[i] << 16; +static size_t der_fill(uint8_t *derbuf, bool is_private, const gcry_mpi_t mpi[], size_t num_mpi) { + size_t needed = 0; + size_t lengths[16] = {0}; - if(i == size - 2) { - word |= buf[i + 1] << 8; - } - } + assert(num_mpi > 0 && num_mpi < sizeof(lengths) / sizeof(*lengths)); - line[j++] = b64e[(word >> 18) ]; - line[j++] = b64e[(word >> 12) & 0x3f]; - line[j++] = b64e[(word >> 6) & 0x3f]; - line[j++] = b64e[(word) & 0x3f]; + if(is_private) { + // Add space for the version number. + needed += der_tag_len(1) + 1; + } - if(j >= 64) { - line[j++] = '\n'; - line[j] = 0; - fputs(line, fp); - j = 0; - } + for(size_t i = 0; i < num_mpi; ++i) { + gcry_mpi_print(GCRYMPI_FMT_STD, NULL, 0, &lengths[i], mpi[i]); + needed += der_tag_len(lengths[i]) + lengths[i]; } - if(size % 3 > 0) { - if(size % 3 > 1) { - line[j++] = '='; - } + const size_t derlen = der_tag_len(needed) + needed; + + uint8_t *der = derbuf; + der = der_store_tag(der, TAG_SEQUENCE, needed); - line[j++] = '='; + if(is_private) { + // Private key requires storing version number. + der = der_store_tag(der, TAG_INTEGER, 1); + *der++ = 0; } - if(j) { - line[j++] = '\n'; - line[j] = 0; - fputs(line, fp); + for(size_t i = 0; i < num_mpi; ++i) { + const size_t len = lengths[i]; + der = der_store_tag(der, TAG_INTEGER, len); + gcry_mpi_print(GCRYMPI_FMT_STD, der, len, NULL, mpi[i]); + der += len; } - fprintf(fp, "-----END %s-----\n", header); + assert(der - derbuf == derlen); + return derlen; +} + +bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) { + uint8_t derbuf[8096]; + + gcry_mpi_t params[] = { + rsa->n, + rsa->e, + }; - return true; + size_t derlen = der_fill(derbuf, false, params, sizeof(params) / sizeof(*params)); + + return pem_encode(fp, "RSA PUBLIC KEY", derbuf, derlen); } +// Calculate p/q primes from n/e/d. +static void get_p_q(gcry_mpi_t *p, + gcry_mpi_t *q, + const gcry_mpi_t n, + const gcry_mpi_t e, + const gcry_mpi_t d) { + const size_t nbits = gcry_mpi_get_nbits(n); + + gcry_mpi_t k = gcry_mpi_new(nbits); + gcry_mpi_mul(k, e, d); + gcry_mpi_sub_ui(k, k, 1); -// BER encoding functions + size_t t = 0; -static bool ber_write_id(uint8_t **p, size_t *buflen, int id) { - if(*buflen <= 0) { - return false; + while(!gcry_mpi_test_bit(k, t)) { + ++t; } - if(id >= 0x1f) { - while(id) { - if(*buflen <= 0) { - return false; - } + gcry_mpi_t g = gcry_mpi_new(nbits); + gcry_mpi_t gk = gcry_mpi_new(0); + gcry_mpi_t sq = gcry_mpi_new(0); + gcry_mpi_t rem = gcry_mpi_new(0); + gcry_mpi_t gcd = gcry_mpi_new(0); - (*buflen)--; - **p = id & 0x7f; - id >>= 7; + while(true) { + gcry_mpi_t kt = gcry_mpi_copy(k); + gcry_mpi_randomize(g, nbits, GCRY_STRONG_RANDOM); - if(id) { - **p |= 0x80; - } + size_t i; - (*p)++; - } - } else { - (*buflen)--; - *(*p)++ = id; - } + for(i = 0; i < t; ++i) { + gcry_mpi_rshift(kt, kt, 1); + gcry_mpi_powm(gk, g, kt, n); - return true; -} + if(gcry_mpi_cmp_ui(gk, 1) != 0) { + gcry_mpi_mul(sq, gk, gk); + gcry_mpi_mod(rem, sq, n); -static bool ber_write_len(uint8_t **p, size_t *buflen, size_t len) { - do { - if(*buflen <= 0) { - return false; + if(gcry_mpi_cmp_ui(rem, 1) == 0) { + break; + } + } } - (*buflen)--; - **p = len & 0x7f; - len >>= 7; + gcry_mpi_release(kt); + + if(i < t) { + gcry_mpi_sub_ui(gk, gk, 1); + gcry_mpi_gcd(gcd, gk, n); - if(len) { - **p |= 0x80; + if(gcry_mpi_cmp_ui(gcd, 1) != 0) { + break; + } } + } - (*p)++; - } while(len); + gcry_mpi_release(k); + gcry_mpi_release(g); + gcry_mpi_release(gk); + gcry_mpi_release(sq); + gcry_mpi_release(rem); - return true; + *p = gcd; + *q = gcry_mpi_new(0); + + gcry_mpi_div(*q, NULL, n, *p, 0); } -static bool ber_write_sequence(uint8_t **p, size_t *buflen, uint8_t *seqbuf, size_t seqlen) { - if(!ber_write_id(p, buflen, 0x10) || !ber_write_len(p, buflen, seqlen) || *buflen < seqlen) { - return false; +bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) { + gcry_mpi_t params[] = { + rsa->n, + rsa->e, + rsa->d, + NULL, // p + NULL, // q + gcry_mpi_new(0), // d mod (p-1) + gcry_mpi_new(0), // d mod (q-1) + gcry_mpi_new(0), // u = p^-1 mod q + }; + + // Indexes into params. + const size_t d = 2; + const size_t p = 3; + const size_t q = 4; + const size_t dp = 5; + const size_t dq = 6; + const size_t u = 7; + + // Calculate p and q. + get_p_q(¶ms[p], ¶ms[q], rsa->n, rsa->e, rsa->d); + + // Swap p and q if q > p. + if(gcry_mpi_cmp(params[q], params[p]) > 0) { + gcry_mpi_swap(params[p], params[q]); } - memcpy(*p, seqbuf, seqlen); - *p += seqlen; - *buflen -= seqlen; + // Calculate u. + gcry_mpi_invm(params[u], params[p], params[q]); - return true; -} + // Calculate d mod (p - 1). + gcry_mpi_sub_ui(params[dp], params[p], 1); + gcry_mpi_mod(params[dp], params[d], params[dp]); -static bool ber_write_mpi(uint8_t **p, size_t *buflen, gcry_mpi_t mpi) { - uint8_t tmpbuf[1024]; - size_t tmplen = sizeof(tmpbuf); - gcry_error_t err; + // Calculate d mod (q - 1). + gcry_mpi_sub_ui(params[dq], params[q], 1); + gcry_mpi_mod(params[dq], params[d], params[dq]); - err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &tmpbuf, &tmplen, mpi); + uint8_t derbuf[8096]; + const size_t nparams = sizeof(params) / sizeof(*params); + size_t derlen = der_fill(derbuf, true, params, nparams); - if(err) { - return false; - } + gcry_mpi_release(params[p]); + gcry_mpi_release(params[q]); + gcry_mpi_release(params[dp]); + gcry_mpi_release(params[dq]); + gcry_mpi_release(params[u]); - if(!ber_write_id(p, buflen, 0x02) || !ber_write_len(p, buflen, tmplen) || *buflen < tmplen) { - return false; - } + return pem_encode(fp, "RSA PRIVATE KEY", derbuf, derlen); +} - memcpy(*p, tmpbuf, tmplen); - *p += tmplen; - *buflen -= tmplen; +static gcry_mpi_t find_mpi(const gcry_sexp_t rsa, const char *token) { + gcry_sexp_t sexp = gcry_sexp_find_token(rsa, token, 1); + + if(!sexp) { + fprintf(stderr, "Token %s not found in RSA S-expression.\n", token); + return NULL; + } - return true; + gcry_mpi_t mpi = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + return mpi; } -// Write PEM RSA keys +rsa_t *rsa_generate(size_t bits, unsigned long exponent) { + gcry_sexp_t s_params; + gcry_error_t err = gcry_sexp_build(&s_params, NULL, + "(genkey" + " (rsa" + " (nbits %u)" + " (rsa-use-e %u)))", + bits, + exponent); -bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) { - uint8_t derbuf1[8096]; - uint8_t derbuf2[8096]; - uint8_t *derp1 = derbuf1; - uint8_t *derp2 = derbuf2; - size_t derlen1 = sizeof(derbuf1); - size_t derlen2 = sizeof(derbuf2); + if(err) { + fprintf(stderr, "Error building keygen S-expression: %s.\n", gcry_strerror(err)); + return NULL; + } - if(!ber_write_mpi(&derp1, &derlen1, &rsa->n) - || !ber_write_mpi(&derp1, &derlen1, &rsa->e) - || !ber_write_sequence(&derp2, &derlen2, derbuf1, derlen1)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA public key"); - return false; + gcry_sexp_t s_key; + err = gcry_pk_genkey(&s_key, s_params); + gcry_sexp_release(s_params); + + if(err) { + fprintf(stderr, "Error generating RSA key pair: %s.\n", gcry_strerror(err)); + return NULL; } - if(!pem_encode(fp, "RSA PUBLIC KEY", derbuf2, derlen2)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA public key: %s", strerror(errno)); - return false; + // `gcry_sexp_extract_param` can replace everything below + // with a single line, but it's not available on CentOS 7. + gcry_sexp_t s_priv = gcry_sexp_find_token(s_key, "private-key", 0); + + if(!s_priv) { + fprintf(stderr, "Private key not found in gcrypt result.\n"); + gcry_sexp_release(s_key); + return NULL; } - return true; -} + gcry_sexp_t s_rsa = gcry_sexp_find_token(s_priv, "rsa", 0); -bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) { - uint8_t derbuf1[8096]; - uint8_t derbuf2[8096]; - uint8_t *derp1 = derbuf1; - uint8_t *derp2 = derbuf2; - size_t derlen1 = sizeof(derbuf1); - size_t derlen2 = sizeof(derbuf2); - - if(!ber_write_mpi(&derp1, &derlen1, &bits) - || ber_write_mpi(&derp1, &derlen1, &rsa->n) // modulus - || ber_write_mpi(&derp1, &derlen1, &rsa->e) // public exponent - || ber_write_mpi(&derp1, &derlen1, &rsa->d) // private exponent - || ber_write_mpi(&derp1, &derlen1, &p) - || ber_write_mpi(&derp1, &derlen1, &q) - || ber_write_mpi(&derp1, &derlen1, &exp1) - || ber_write_mpi(&derp1, &derlen1, &exp2) - || ber_write_mpi(&derp1, &derlen1, &coeff)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA private key"); - } - - return false; -} + if(!s_rsa) { + fprintf(stderr, "RSA not found in gcrypt result.\n"); + gcry_sexp_release(s_priv); + gcry_sexp_release(s_key); + return NULL; + } -if(!pem_encode(fp, "RSA PRIVATE KEY", derbuf2, derlen2)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA private key: %s", strerror(errno)); - return false; -} + rsa_t *rsa = xzalloc(sizeof(*rsa)); -return true; -} -#endif + rsa->n = find_mpi(s_rsa, "n"); + rsa->e = find_mpi(s_rsa, "e"); + rsa->d = find_mpi(s_rsa, "d"); -bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) { - return false; -} + gcry_sexp_release(s_rsa); + gcry_sexp_release(s_priv); + gcry_sexp_release(s_key); -bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) { - return false; -} + if(rsa->n && rsa->e && rsa->d) { + return rsa; + } -bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) { - fprintf(stderr, "Generating RSA keys with libgcrypt not implemented yet\n"); - return false; + rsa_free(rsa); + return NULL; }