X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=gcrypt.c;h=f825f6c6a3de6388959d7b900f31ef9d48e6397b;hp=1312aaedc275206ba88ce4cd44cd89c01d2ba9ba;hb=4a4d8f266a79275d7b2c902dc69b5ec8d46406b2;hpb=1ecb886f1aa76e72bd11d45782210b5ca19bce42 diff --git a/gcrypt.c b/gcrypt.c index 1312aaed..f825f6c6 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -19,6 +19,9 @@ //#define GCRYPT_DEBUG 1 +static bool libgcrypt_has_oaep; +static const char *rsa_decrypt_sexp; + #ifdef GCRYPT_DEBUG static void dump_buffer(const char *msg, unsigned char *buf, int len) { @@ -56,14 +59,32 @@ 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 the call to gcry_control - * is necessary to avoid warnings of the form "missing initialization - please - * fix the application". + * 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 function also tells us whether we have to use our own OAEP + * padding code. */ void init_random_seed_or_die(void) { - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + const char *ver, *req_ver; + + ver = gcry_check_version(NULL); + req_ver = "1.4.0"; + if (!gcry_check_version(req_ver)) { + PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n", + req_ver, ver); + exit(EXIT_FAILURE); + } + req_ver = "1.5.0"; + if (gcry_check_version(req_ver)) { + libgcrypt_has_oaep = true; + rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))"; + } else { + libgcrypt_has_oaep = false; + rsa_decrypt_sexp = "(enc-val(rsa(a %m)))"; + } } /** S-expression for the public part of an RSA key. */ @@ -143,7 +164,7 @@ static void pad_oaep(unsigned char *in, size_t in_len, unsigned char *out, /* rfc 3447, section 7.1.2 */ static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len) -{ int ret; +{ unsigned char *masked_seed = in + 1; unsigned char *db = in + 1 + HASH_SIZE; unsigned char seed[HASH_SIZE], seed_mask[HASH_SIZE]; @@ -168,7 +189,7 @@ static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out, p++; *out_len = in + in_len - p; memcpy(out, p, *out_len); - return ret; + return 1; } struct asymmetric_key { @@ -566,7 +587,7 @@ static int get_asn_public_key(const char *key_file, struct asymmetric_key **resu key = para_malloc(sizeof(*key)); key->sexp = sexp; *result = key; - ret = n_size * 8; + ret = n_size; PARA_INFO_LOG("successfully read %u bit asn public key\n", n_size * 8); release_e: @@ -693,16 +714,76 @@ void free_asymmetric_key(struct asymmetric_key *key) free(key); } +static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf, + size_t *nbytes) +{ + int ret; + gcry_error_t gret; + unsigned char oaep_buf[512]; + gcry_mpi_t out_mpi; + + if (libgcrypt_has_oaep) { + const char *p = gcry_sexp_nth_data(sexp, 1, nbytes); + + if (!p) { + PARA_ERROR_LOG("could not get data from list\n"); + return -E_OEAP; + } + memcpy(outbuf, p, *nbytes); + return 1; + } + out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG); + if (!out_mpi) + return -E_SEXP_FIND; + gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf), + nbytes, out_mpi); + if (gret) { + PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret)); + ret = -E_MPI_PRINT; + goto out_mpi_release; + } + /* + * An oaep-encoded buffer always starts with at least one zero byte. + * However, leading zeroes in an mpi are omitted in the output of + * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The + * alternative, GCRYMPI_FMT_STD, does not work either because here the + * leading zero(es) might also be omitted, depending on the value of + * the second byte. + * + * To circumvent this, we shift the oaep buffer to the right. But first + * we check that the buffer actually started with a zero byte, i.e. that + * nbytes < key_size. Otherwise a decoding error occurred. + */ + ret = -E_SEXP_DECRYPT; + if (*nbytes >= key_size) + goto out_mpi_release; + memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes); + memset(oaep_buf, 0, key_size - *nbytes); + + PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n", + key_size); + dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);; + ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes); + if (ret < 0) + goto out_mpi_release; + PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n", + *nbytes); + dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);; + ret = 1; +out_mpi_release: + gcry_mpi_release(out_mpi); + return ret; +} + int priv_decrypt(const char *key_file, unsigned char *outbuf, unsigned char *inbuf, int inlen) { gcry_error_t gret; int ret, key_size; struct asymmetric_key *priv; - gcry_mpi_t in_mpi = NULL, out_mpi = NULL; + gcry_mpi_t in_mpi = NULL; gcry_sexp_t in, out, priv_key; size_t nbytes; - unsigned char oaep_buf[512]; PARA_INFO_LOG("decrypting %d byte input\n", inlen); /* key_file -> asymmetric key priv */ @@ -725,9 +806,8 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf, ret = -E_MPI_SCAN; goto key_release; } - /* in_mpi -> in sexp */ - gret = gcry_sexp_build(&in, NULL, "(enc-val(rsa(a %m)))", in_mpi); + gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi); if (gret) { PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); ret = -E_SEXP_BUILD; @@ -741,47 +821,11 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf, ret = -E_SEXP_DECRYPT; goto in_release; } - out_mpi = gcry_sexp_nth_mpi(out, 0, GCRYMPI_FMT_USG); - if (!out_mpi) { - ret = -E_SEXP_FIND; + ret = decode_rsa(out, key_size, outbuf, &nbytes); + if (ret < 0) goto out_release; - } - gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf), - &nbytes, out_mpi); - if (gret) { - PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret)); - ret = -E_MPI_PRINT; - goto out_mpi_release; - } - /* - * An oaep-encoded buffer always starts with at least one zero byte. - * However, leading zeroes in an mpi are omitted in the output of - * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The - * alternative, GCRYMPI_FMT_STD, does not work either because here the - * leading zero(es) might also be omitted, depending on the value of - * the second byte. - * - * To circumvent this, we shift the oaep buffer to the right. But first - * we check that the buffer actually started with a zero byte, i.e. that - * nbytes < key_size. Otherwise a decoding error occurred. - */ - ret = -E_SEXP_DECRYPT; - if (nbytes >= key_size) - goto out_mpi_release; - memmove(oaep_buf + key_size - nbytes, oaep_buf, nbytes); - memset(oaep_buf, 0, key_size - nbytes); - - PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n", - key_size); - dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);; - unpad_oaep(oaep_buf, key_size, outbuf, &nbytes); - PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n", - nbytes); - dump_buffer("unpadded decrypted buffer", outbuf, nbytes);; + PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes); ret = nbytes; - PARA_INFO_LOG("successfully decrypted %u byte message\n", ret); -out_mpi_release: - gcry_mpi_release(out_mpi); out_release: gcry_sexp_release(out); in_release: @@ -799,12 +843,10 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, unsigned len, unsigned char *outbuf) { gcry_error_t gret; - const size_t pad_size = 256; gcry_sexp_t pub_key, in, out, out_a; gcry_mpi_t out_mpi = NULL; size_t nbytes; int ret; - unsigned char padded_input[256]; PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes); @@ -812,13 +854,18 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0); if (!pub_key) return -E_SEXP_FIND; - - /* inbuf -> padded inbuf */ - pad_oaep(inbuf, len, padded_input, pad_size); - - /* padded inbuf -> in sexp */ - gret = gcry_sexp_build(&in, NULL, "(data(flags raw)(value %b))", - pad_size, padded_input); + if (libgcrypt_has_oaep) { + gret = gcry_sexp_build(&in, NULL, + "(data(flags oaep)(value %b))", len, inbuf); + } else { + unsigned char padded_input[256]; + const size_t pad_size = 256; + /* inbuf -> padded inbuf */ + pad_oaep(inbuf, len, padded_input, pad_size); + /* padded inbuf -> in sexp */ + gret = gcry_sexp_build(&in, NULL, + "(data(flags raw)(value %b))", pad_size, padded_input); + } if (gret) { PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); ret = -E_SEXP_BUILD; @@ -837,7 +884,7 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, if (!out_a) goto out_release; /* convert sexp out_a -> out_mpi */ - out_mpi = gcry_sexp_nth_mpi(out_a, 1, GCRYMPI_FMT_STD); + out_mpi = gcry_sexp_nth_mpi(out_a, 1, GCRYMPI_FMT_USG); if (!out_mpi) { ret = -E_SEXP_FIND; goto out_a_release;