X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=gcrypt.c;h=dbe4900862fef83a552d9c60d9ac21d4c8253d5e;hp=7d6b2cae611f4946b5671ece39d0000d39a8f7b2;hb=09dbc597fa9661f8dfd96684430531fe6575d26c;hpb=9249294000b235a0a80ee5e8c70f43c186205888 diff --git a/gcrypt.c b/gcrypt.c index 7d6b2cae..dbe49008 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -1,8 +1,4 @@ -/* - * Copyright (C) 2011 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2011 Andre Noll , see file COPYING. */ /** \file gcrypt.c Libgrcypt-based encryption/decryption routines. */ @@ -56,23 +52,41 @@ void get_random_bytes_or_die(unsigned char *buf, int num) } /* - * This is called at the beginning of every program that uses libgcrypt. We - * don't have to initialize any random seed here, but we must initialize the - * gcrypt library. This task is performed by gcry_check_version() which can - * also check that the gcrypt library version is at least the minimal required - * version. + * This is called at the beginning of every program that uses libgcrypt. The + * call to gcry_check_version() initializes the gcrypt library and checks that + * we have at least the minimal required version. */ -void init_random_seed_or_die(void) +void crypt_init(void) { - const char *ver, *req_ver; + const char *req_ver = "1.5.0"; + int seed; - ver = gcry_check_version(NULL); - req_ver = "1.5.0"; if (!gcry_check_version(req_ver)) { PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n", - req_ver, ver); + req_ver, gcry_check_version(NULL)); exit(EXIT_FAILURE); } + + /* + * Allocate a pool of secure memory. This also drops privileges where + * needed. + */ + gcry_control(GCRYCTL_INIT_SECMEM, 65536, 0); + + /* Tell Libgcrypt that initialization has completed. */ + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + + get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed)); + srandom(seed); +} + +void crypt_shutdown(void) +{ + /* + * WK does not see a way to apply a patch for the sake of Valgrind, so + * as of 2018 libgrypt has no deinitialization routine to free the + * resources on exit. + */ } /** S-expression for the public part of an RSA key. */ @@ -92,66 +106,6 @@ static const char *gcrypt_strerror(gcry_error_t gret) return gcry_strerror(gcry_err_code(gret)); } -static int decode_key(const char *key_file, const char *header_str, - const char *footer_str, unsigned char **result) -{ - int ret, ret2, i, j; - void *map; - size_t map_size, key_size, blob_size; - unsigned char *blob = NULL; - char *begin, *footer, *key; - - ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); - if (ret < 0) - goto out; - ret = -E_KEY_MARKER; - if (strncmp(map, header_str, strlen(header_str))) - goto unmap; - footer = strstr(map, footer_str); - ret = -E_KEY_MARKER; - if (!footer) - goto unmap; - begin = map + strlen(header_str); - /* skip whitespace at the beginning */ - for (; begin < footer; begin++) { - if (para_isspace(*begin)) - continue; - break; - } - ret = -E_KEY_MARKER; - if (begin >= footer) - goto unmap; - - key_size = footer - begin; - key = para_malloc(key_size + 1); - for (i = 0, j = 0; begin + i < footer; i++) { - if (para_isspace(begin[i])) - continue; - key[j++] = begin[i]; - } - key[j] = '\0'; - 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; -unmap: - ret2 = para_munmap(map, map_size); - if (ret >= 0 && ret2 < 0) - ret = ret2; - if (ret < 0) { - free(blob); - blob = NULL; - } -out: - *result = blob; - return ret; -} - /** ASN Types and their code. */ enum asn1_types { /** The next object is an integer. */ @@ -191,71 +145,13 @@ static inline int get_long_form_num_length_bytes(unsigned char c) return c & 0x7f; } -static int find_pubkey_bignum_offset(const unsigned char *data, int len) -{ - const unsigned char *p = data, *end = data + len; - - /* the whole thing starts with one sequence */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - if (p >= end) - return -E_ASN1_PARSE; - /* another sequence containing the object id, skip it */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (!is_short_form(*p)) - return -E_ASN1_PARSE; - p += 1 + get_short_form_length(*p); - if (p >= end) - return -E_ASN1_PARSE; - /* all numbers are wrapped in a bit string object that follows */ - if (*p != ASN1_TYPE_BIT_STRING) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - p++; /* skip number of unused bits in the bit string */ - if (p >= end) - return -E_ASN1_PARSE; - - /* next, we have a sequence of two integers (n and e) */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - if (p >= end) - return -E_ASN1_PARSE; - if (*p != ASN1_TYPE_INTEGER) - return -E_ASN1_PARSE; - return p - data; -} - /* * 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; @@ -281,7 +177,7 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, (unsigned)bn_size); gret = gcry_mpi_scan(bn, GCRYMPI_FMT_STD, cp, bn_size, NULL); if (gret) { - PARA_ERROR_LOG("%s while scanning n\n", + PARA_ERROR_LOG("gcry_mpi_scan: %s\n", gcry_strerror(gcry_err_code(gret))); return-E_MPI_SCAN; } @@ -293,8 +189,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); @@ -302,7 +198,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; @@ -334,211 +278,173 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len) return p - data; } -/** 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 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; - ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER, - &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; - /* - * 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); - } - gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, - n, e, d, p, q, u); - - if (gret) { - PARA_ERROR_LOG("offset %zu: %s\n", erroff, - gcry_strerror(gcry_err_code(gret))); - ret = -E_SEXP_BUILD; - goto release_u; - } - key = para_malloc(sizeof(*key)); - key->sexp = sexp; - *result = key; - ret = n_size * 8; - PARA_INFO_LOG("succesfully read %d bit private key\n", ret); -release_u: - gcry_mpi_release(u); -release_q: - gcry_mpi_release(q); + goto release_p; + return bits; release_p: - gcry_mpi_release(p); + gcry_mpi_release(p->p); +release_u: + gcry_mpi_release(p->u); release_d: - gcry_mpi_release(d); + gcry_mpi_release(p->d); release_e: - gcry_mpi_release(e); + gcry_mpi_release(p->e); release_n: - gcry_mpi_release(n); -free_blob: - free(blob); + gcry_mpi_release(p->n); return ret; } -/** Public keys start with this header. */ -#define PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----" -/** Public keys end with this footer. */ -#define PUBLIC_KEY_FOOTER "-----END PUBLIC KEY-----" - -static int get_asn_public_key(const char *key_file, struct asymmetric_key **result) +static int get_private_key(const char *key_file, struct asymmetric_key **result) { - gcry_mpi_t n = NULL, e = NULL; - unsigned char *blob, *cp, *end; - int blob_size, ret, n_size; + struct rsa_params params; + unsigned char *blob, *end; + unsigned bits; + int ret, key_type; gcry_error_t gret; - size_t erroff; + size_t erroff, blob_size; gcry_sexp_t sexp; struct asymmetric_key *key; - ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER, - &blob); + *result = NULL; + ret = decode_private_key(key_file, &blob, &blob_size); if (ret < 0) return ret; - blob_size = ret; + key_type = ret; end = blob + blob_size; - ret = find_pubkey_bignum_offset(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_DEBUG_LOG("decoding public RSA params at offset %d\n", ret); - cp = blob + ret; - - ret = read_bignum(cp, end, &n, &n_size); + 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; - cp += ret; - - ret = read_bignum(cp, end, &e, NULL); - if (ret < 0) - goto release_n; + 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(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, params.n, + params.e, params.d, params.p, params.q, params.u); - gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, gcry_strerror(gcry_err_code(gret))); ret = -E_SEXP_BUILD; - goto release_e; + goto free_params; } key = para_malloc(sizeof(*key)); key->sexp = sexp; - key->num_bytes = n_size; *result = key; - ret = n_size; - PARA_INFO_LOG("successfully read %d bit asn public key\n", n_size * 8); + ret = bits; + PARA_INFO_LOG("succesfully read %d bit private key\n", ret); +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); -release_e: - gcry_mpi_release(e); -release_n: - gcry_mpi_release(n); free_blob: free(blob); return ret; } -static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result) +int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) { + unsigned char *blob, *p, *end; int ret; gcry_error_t gret; - unsigned char *blob = NULL, *p, *end; - size_t nr_scanned, erroff, decoded_size; - gcry_mpi_t e = NULL, n = NULL; + size_t erroff, decoded_size; + gcry_mpi_t e, n; + gcry_sexp_t sexp; + struct asymmetric_key *key; + unsigned bits; - PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size); - ret = uudecode((char *)data, size, (char **)&blob, &decoded_size); - if (ret < 0) - goto free_blob; - end = blob + decoded_size; - dump_buffer("decoded key", blob, decoded_size); - ret = check_ssh_key_header(blob, decoded_size); + ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) - goto free_blob; + return ret; p = blob + ret; - ret = -E_SSH_PARSE; - if (p >= end) - goto free_blob; + 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); -// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_e); -// PARA_CRIT_LOG("e: %s\n", buf); - 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); -// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_n); -// PARA_CRIT_LOG("n: %s\n", buf); - gret = gcry_sexp_build(result, &erroff, RSA_PUBKEY_SEXP, n, e); + gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, gcry_strerror(gcry_err_code(gret))); ret = -E_SEXP_BUILD; goto release_n; } - ret = nr_scanned / 32 * 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: @@ -548,48 +454,7 @@ free_blob: return ret; } -int get_asymmetric_key(const char *key_file, int private, - struct asymmetric_key **result) -{ - int ret, ret2; - void *map; - size_t map_size; - unsigned char *start, *end; - gcry_sexp_t sexp; - struct asymmetric_key *key; - - if (private) - return get_private_key(key_file, result); - ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); - if (ret < 0) - return ret; - ret = is_ssh_rsa_key(map, map_size); - if (!ret) { - ret = para_munmap(map, map_size); - if (ret < 0) - return ret; - return get_asn_public_key(key_file, result); - } - start = map + ret; - end = map + map_size; - ret = -E_SSH_PARSE; - if (start >= end) - goto unmap; - ret = get_ssh_public_key(start, end - start, &sexp); - if (ret < 0) - goto unmap; - key = para_malloc(sizeof(*key)); - key->num_bytes = ret; - key->sexp = sexp; - *result = key; -unmap: - ret2 = para_munmap(map, map_size); - if (ret >= 0 && ret2 < 0) - ret = ret2; - return ret; -} - -void free_asymmetric_key(struct asymmetric_key *key) +void apc_free_pubkey(struct asymmetric_key *key) { if (!key) return; @@ -607,7 +472,7 @@ static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes) return 1; } -int priv_decrypt(const char *key_file, unsigned char *outbuf, +int apc_priv_decrypt(const char *key_file, unsigned char *outbuf, unsigned char *inbuf, int inlen) { gcry_error_t gret; @@ -617,7 +482,7 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf, gcry_sexp_t in, out, priv_key; size_t nbytes; - ret = check_key_file(key_file, true); + ret = check_private_key_file(key_file); if (ret < 0) return ret; PARA_INFO_LOG("decrypting %d byte input\n", inlen); @@ -669,11 +534,12 @@ in_mpi_release: key_release: gcry_sexp_release(priv_key); free_key: - free_asymmetric_key(priv); + gcry_sexp_release(priv->sexp); + free(priv); return ret; } -int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, +int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, unsigned len, unsigned char *outbuf) { gcry_error_t gret; @@ -739,33 +605,20 @@ struct stream_cipher { gcry_cipher_hd_t handle; }; -struct stream_cipher *sc_new(const unsigned char *data, int len, - bool use_aes) +struct stream_cipher *sc_new(const unsigned char *data, int len) { gcry_error_t gret; struct stream_cipher *sc = para_malloc(sizeof(*sc)); - if (use_aes) { - assert(len >= 2 * AES_CRT128_BLOCK_SIZE); - gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128, - GCRY_CIPHER_MODE_CTR, 0); - assert(gret == 0); - gret = gcry_cipher_setkey(sc->handle, data, - AES_CRT128_BLOCK_SIZE); - assert(gret == 0); - gret = gcry_cipher_setctr(sc->handle, - data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE); - assert(gret == 0); - return sc; - } - gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR, - GCRY_CIPHER_MODE_STREAM, 0); - if (gret) { - PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); - free(sc); - return NULL; - } - gret = gcry_cipher_setkey(sc->handle, data, (size_t)len); + assert(len >= 2 * AES_CRT128_BLOCK_SIZE); + gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_CTR, 0); + assert(gret == 0); + gret = gcry_cipher_setkey(sc->handle, data, + AES_CRT128_BLOCK_SIZE); + assert(gret == 0); + gret = gcry_cipher_setctr(sc->handle, + data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE); assert(gret == 0); return sc; }