X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=gcrypt.c;h=69c2c34f0b4e87fd57b0c22f2a6d7725eaaaa043;hp=50ddff46df79820c9af24c7a4f96c02ef05f9675;hb=0b8b2469b5d2b81ff928bddd10d4ac3b819b75f8;hpb=eb833d819057adb3916aa2f2704ca2df7fd6a48f diff --git a/gcrypt.c b/gcrypt.c index 50ddff46..69c2c34f 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -12,6 +12,7 @@ #include "crypt_backend.h" #include "fd.h" #include "base64.h" +#include "portable_io.h" //#define GCRYPT_DEBUG 1 @@ -106,16 +107,25 @@ static const char *gcrypt_strerror(gcry_error_t gret) return gcry_strerror(gcry_err_code(gret)); } -/** Private keys start with this header. */ -#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" -/** Private keys end with this footer. */ -#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----" - -static int decode_key(const char *key_file, unsigned char **result) +/** Private PEM keys (legacy format) start with this header. */ +#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" +/** Private OPENSSH keys (RFC4716) start with this header. */ +#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----" +/** Private PEM keys (legacy format) end with this footer. */ +#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----" +/** Private OPENSSH keys (RFC4716) end with this footer. */ +#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----" +/** Legacy PEM keys (openssh-7.7 and earlier, paraslash.0.6.2 and earlier) */ +#define PKT_PEM (0) +/** OPENSSH keys (since openssh-7.8, paraslash.0.6.3) */ +#define PKT_OPENSSH (1) + +static int decode_private_key(const char *key_file, unsigned char **result, + size_t *blob_size) { - int ret, ret2, i, j; + int ret, ret2, i, j, key_type; void *map; - size_t map_size, key_size, blob_size; + size_t map_size, key_size; unsigned char *blob = NULL; char *begin, *footer, *key; @@ -123,13 +133,22 @@ static int decode_key(const char *key_file, unsigned char **result) if (ret < 0) goto out; ret = -E_KEY_MARKER; - if (strncmp(map, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER))) + if (strncmp(map, PRIVATE_PEM_KEY_HEADER, + strlen(PRIVATE_PEM_KEY_HEADER)) == 0) { + key_type = PKT_PEM; + begin = map + strlen(PRIVATE_PEM_KEY_HEADER); + footer = strstr(map, PRIVATE_PEM_KEY_FOOTER); + PARA_INFO_LOG("detected legacy PEM key %s\n", key_file); + } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER, + strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) { + key_type = PKT_OPENSSH; + begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER); + footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER); + PARA_INFO_LOG("detected openssh key %s\n", key_file); + } else goto unmap; - footer = strstr(map, PRIVATE_KEY_FOOTER); - ret = -E_KEY_MARKER; if (!footer) goto unmap; - begin = map + strlen(PRIVATE_KEY_HEADER); /* skip whitespace at the beginning */ for (; begin < footer; begin++) { if (para_isspace(*begin)) @@ -148,15 +167,11 @@ static int decode_key(const char *key_file, unsigned char **result) key[j++] = begin[i]; } key[j] = '\0'; - ret = base64_decode(key, j, (char **)&blob, &blob_size); + ret = base64_decode(key, j, (char **)&blob, blob_size); free(key); if (ret < 0) - goto free_unmap; - ret = blob_size; - goto unmap; -free_unmap: - free(blob); - blob = NULL; + goto unmap; + ret = key_type; unmap: ret2 = para_munmap(map, map_size); if (ret >= 0 && ret2 < 0) @@ -211,11 +226,11 @@ static inline int get_long_form_num_length_bytes(unsigned char c) /* * Returns: Number of bytes scanned. This may differ from the value returned via - * bn_bytes because the latter does not include the ASN.1 prefix and a leading - * zero is not considered as an additional byte for bn_bytes. + * bitsp because the latter does not include the ASN.1 prefix and a leading + * zero is not considered as an additional byte for the number of bits. */ -static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, - int *bn_bytes) +static int read_pem_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, + unsigned *bitsp) { int i, bn_size; gcry_error_t gret; @@ -253,8 +268,8 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, cp++; bn_size--; } - if (bn_bytes) - *bn_bytes = bn_size; + if (bitsp) + *bitsp = bn_size * 8; cp += bn_size; // unsigned char *buf; // gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn); @@ -262,7 +277,55 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, return cp - start; } -static int find_privkey_bignum_offset(const unsigned char *data, int len) +struct rsa_params { + gcry_mpi_t n, e, d, p, q, u; +}; + +static int read_pem_rsa_params(unsigned char *start, unsigned char *end, + struct rsa_params *p) +{ + unsigned char *cp = start; + unsigned bits; + int ret; + + ret = read_pem_bignum(cp, end, &p->n, &bits); + if (ret < 0) + return ret; + cp += ret; + ret = read_pem_bignum(cp, end, &p->e, NULL); + if (ret < 0) + goto release_n; + cp += ret; + ret = read_pem_bignum(cp, end, &p->d, NULL); + if (ret < 0) + goto release_e; + cp += ret; + ret = read_pem_bignum(cp, end, &p->p, NULL); + if (ret < 0) + goto release_d; + cp += ret; + ret = read_pem_bignum(cp, end, &p->q, NULL); + if (ret < 0) + goto release_p; + cp += ret; + ret = read_pem_bignum(cp, end, &p->u, NULL); + if (ret < 0) + goto release_q; + return bits; +release_q: + gcry_mpi_release(p->q); +release_p: + gcry_mpi_release(p->p); +release_d: + gcry_mpi_release(p->d); +release_e: + gcry_mpi_release(p->e); +release_n: + gcry_mpi_release(p->n); + return ret; +} + +static int find_pem_bignum_offset(const unsigned char *data, int len) { const unsigned char *p = data, *end = data + len; @@ -294,90 +357,131 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len) return p - data; } -static int get_private_key(const char *key_file, struct asymmetric_key **result) +static int read_openssh_bignum(unsigned char *start, unsigned char *end, + gcry_mpi_t *bn, unsigned *bitsp) { - gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, - u = NULL; - unsigned char *blob, *cp, *end; - int blob_size, ret, n_size; gcry_error_t gret; - size_t erroff; - gcry_sexp_t sexp; - struct asymmetric_key *key; + size_t nscanned; + unsigned bits; - *result = NULL; - ret = decode_key(key_file, &blob); - if (ret < 0) - return ret; - blob_size = ret; - end = blob + blob_size; - ret = find_privkey_bignum_offset(blob, blob_size); - if (ret < 0) - goto free_blob; - PARA_INFO_LOG("reading RSA params at offset %d\n", ret); - cp = blob + ret; + gret = gcry_mpi_scan(bn, GCRYMPI_FMT_SSH, start, end - start, &nscanned); + if (gret) { + PARA_ERROR_LOG("gcry_mpi_scan: %s\n", + gcry_strerror(gcry_err_code(gret))); + return -E_MPI_SCAN; + } + bits = (nscanned - 4 - (start[4] == '\0')) * 8; + if (bitsp) + *bitsp = bits; + PARA_DEBUG_LOG("scanned %u-bit bignum\n", bits); + return nscanned; +} + +static int read_openssh_rsa_params(unsigned char *start, unsigned char *end, + struct rsa_params *p) +{ + unsigned char *cp = start; + unsigned bits; + int ret; - ret = read_bignum(cp, end, &n, &n_size); + ret = read_openssh_bignum(cp, end, &p->n, &bits); if (ret < 0) - goto free_blob; + return ret; cp += ret; - - ret = read_bignum(cp, end, &e, NULL); + ret = read_openssh_bignum(cp, end, &p->e, NULL); if (ret < 0) goto release_n; cp += ret; - - ret = read_bignum(cp, end, &d, NULL); + ret = read_openssh_bignum(cp, end, &p->d, NULL); if (ret < 0) goto release_e; cp += ret; - - ret = read_bignum(cp, end, &p, NULL); + ret = read_openssh_bignum(cp, end, &p->u, NULL); if (ret < 0) goto release_d; cp += ret; - - ret = read_bignum(cp, end, &q, NULL); + ret = read_openssh_bignum(cp, end, &p->p, NULL); if (ret < 0) - goto release_p; + goto release_u; cp += ret; - ret = read_bignum(cp, end, &u, NULL); + ret = read_openssh_bignum(cp, end, &p->q, NULL); if (ret < 0) - goto release_q; + goto release_p; + return bits; +release_p: + gcry_mpi_release(p->p); +release_u: + gcry_mpi_release(p->u); +release_d: + gcry_mpi_release(p->d); +release_e: + gcry_mpi_release(p->e); +release_n: + gcry_mpi_release(p->n); + return ret; +} + +static int get_private_key(const char *key_file, struct asymmetric_key **result) +{ + struct rsa_params params; + unsigned char *blob, *end; + unsigned bits; + int ret, key_type; + gcry_error_t gret; + size_t erroff, blob_size; + gcry_sexp_t sexp; + struct asymmetric_key *key; + + *result = NULL; + ret = decode_private_key(key_file, &blob, &blob_size); + if (ret < 0) + return ret; + key_type = ret; + end = blob + blob_size; + if (key_type == PKT_PEM) + ret = find_pem_bignum_offset(blob, blob_size); + else + ret = find_openssh_bignum_offset(blob, blob_size); + if (ret < 0) + goto free_blob; + PARA_INFO_LOG("reading RSA params at offset %d\n", ret); + if (key_type == PKT_PEM) + ret = read_pem_rsa_params(blob + ret, end, ¶ms); + else + ret = read_openssh_rsa_params(blob + ret, end, ¶ms); + if (ret < 0) + goto free_blob; + bits = ret; /* * OpenSSL uses slightly different parameters than gcrypt. To use these * parameters we need to swap the values of p and q and recompute u. */ - if (gcry_mpi_cmp(p, q) > 0) { - gcry_mpi_swap(p, q); - gcry_mpi_invm(u, p, q); + if (gcry_mpi_cmp(params.p, params.q) > 0) { + gcry_mpi_swap(params.p, params.q); + gcry_mpi_invm(params.u, params.p, params.q); } - gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, - n, e, d, p, q, u); + gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, params.n, + params.e, params.d, params.p, params.q, params.u); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, gcry_strerror(gcry_err_code(gret))); ret = -E_SEXP_BUILD; - goto release_u; + goto free_params; } key = para_malloc(sizeof(*key)); key->sexp = sexp; *result = key; - ret = n_size * 8; + ret = bits; PARA_INFO_LOG("succesfully read %d bit private key\n", ret); -release_u: - gcry_mpi_release(u); -release_q: - gcry_mpi_release(q); -release_p: - gcry_mpi_release(p); -release_d: - gcry_mpi_release(d); -release_e: - gcry_mpi_release(e); -release_n: - gcry_mpi_release(n); +free_params: + gcry_mpi_release(params.n); + gcry_mpi_release(params.e); + gcry_mpi_release(params.d); + gcry_mpi_release(params.u); + gcry_mpi_release(params.p); + gcry_mpi_release(params.q); + free_blob: free(blob); return ret; @@ -388,34 +492,25 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) unsigned char *blob, *p, *end; int ret; gcry_error_t gret; - size_t nr_scanned, erroff, decoded_size; + size_t erroff, decoded_size; gcry_mpi_t e, n; gcry_sexp_t sexp; struct asymmetric_key *key; + unsigned bits; - ret = decode_ssh_key(key_file, &blob, &decoded_size); + ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) return ret; p = blob + ret; end = blob + decoded_size; PARA_DEBUG_LOG("scanning modulus and public exponent\n"); - gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); - if (gret) { - ret = -E_MPI_SCAN; - PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + ret = read_openssh_bignum(p, end, &e, NULL); + if (ret < 0) goto free_blob; - } - PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned); - p += nr_scanned; - if (p >= end) - goto release_e; - gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); - if (gret) { - ret = -E_MPI_SCAN; - PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + p += ret; + ret = read_openssh_bignum(p, end, &n, &bits); + if (ret < 0) goto release_e; - } - PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned); gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, @@ -423,12 +518,12 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) ret = -E_SEXP_BUILD; goto release_n; } - ret = ROUND_DOWN(nr_scanned, 32); - PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8); + PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits); key = para_malloc(sizeof(*key)); key->num_bytes = ret; key->sexp = sexp; *result = key; + ret = bits; release_n: gcry_mpi_release(n); release_e: