diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | cipher/pubkey.c | 504 | ||||
-rw-r--r-- | src/cipher.h | 19 | ||||
-rw-r--r-- | tests/basic.c | 152 |
4 files changed, 606 insertions, 71 deletions
@@ -36,8 +36,6 @@ collectros need to run that bunch of Unix utilities we don't waste their precious results. -* Add OAEP - * gcryptrnd.c Requires a test for pth [done] as well as some other tests. diff --git a/cipher/pubkey.c b/cipher/pubkey.c index 0fd87f96..a434b824 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -783,6 +783,205 @@ pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data, return rc; } +static gcry_err_code_t +mgf1 (unsigned char *output, size_t outlen, unsigned char *seed, size_t seedlen, + int algo) +{ + size_t dlen; + int idx; + gcry_md_hd_t hd; + gcry_error_t err; + unsigned char *p; + + err = gcry_md_test_algo (algo); + if (err) + return gpg_err_code (err); + + memset (output, 0, outlen); + dlen = gcry_md_get_algo_dlen (algo); + for (idx = 0, p = output; idx < (outlen + dlen - 1) / dlen; idx++, p += dlen) + { + unsigned char c[4], *digest; + + c[0] = (idx >> 24) & 0xFF; + c[1] = (idx >> 16) & 0xFF; + c[2] = (idx >> 8) & 0xFF; + c[3] = idx & 0xFF; + + err = gcry_md_open (&hd, algo, 0); + if (err) + return gpg_err_code (err); + + gcry_md_write (hd, seed, seedlen); + gcry_md_write (hd, c, 4); + digest = gcry_md_read (hd, 0); + if (outlen - (p - output) >= dlen) + memcpy (p, digest, dlen); + else + memcpy (p, digest, outlen - (p - output)); + gcry_md_close (hd); + } + return GPG_ERR_NO_ERROR; +} + +static gcry_err_code_t +oaep_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + const unsigned char *value, size_t valuelen, + const unsigned char *label, size_t labellen) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + unsigned char *dmask, *smask, *p; + size_t dlen; + gcry_md_hd_t hd; + size_t n; + + dlen = gcry_md_get_algo_dlen (algo); + if (valuelen > nframe - 2 * dlen - 1 || !nframe) + /* Can't encode a VALUELEN value in a NFRAME bytes frame. */ + return GPG_ERR_TOO_SHORT; /* the key is too short */ + if ( !(frame = gcry_malloc_secure (nframe))) + return gpg_err_code_from_errno (errno); + + /* FRAME = 00 || SEED || DB */ + memset (frame, 0, nframe); + n = 0; + frame[n++] = 0; + gcry_randomize (&frame[n], dlen, GCRY_STRONG_RANDOM); + + n += dlen; + gcry_md_open (&hd, algo, 0); + gcry_md_write (hd, label, labellen); + memcpy (&frame[n], gcry_md_read (hd, 0), dlen); + gcry_md_close (hd); + n = nframe - valuelen - 1; + frame[n++] = 1; + memcpy (&frame[n], value, valuelen); + + if ( !(dmask = gcry_malloc_secure (nframe - dlen - 1))) + { + rc = gpg_err_code_from_errno (errno); + gcry_free (frame); + return rc; + } + mgf1 (dmask, nframe - dlen - 1, &frame[1], dlen, algo); + for (n = 1 + dlen, p = dmask; n < nframe; n++) + frame[n] ^= *p++; + gcry_free (dmask); + n += valuelen; + + if ( !(smask = gcry_malloc_secure (dlen))) + { + rc = gpg_err_code_from_errno (errno); + gcry_free (frame); + return rc; + } + mgf1 (smask, dlen, &frame[1 + dlen], nframe - dlen - 1, algo); + for (n = 1, p = smask; n < 1 + dlen; n++) + frame[n] ^= *p++; + gcry_free (smask); + n = nframe; + + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("OAEP encoded data", *r_result); + gcry_free (frame); + + return rc; +} + +static gcry_err_code_t +oaep_decode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + gcry_mpi_t value, const unsigned char *label, size_t labellen) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL, *dmask, *smask, *p; + size_t nframe = (nbits+7) / 8; + size_t dlen; + gcry_md_hd_t hd; + size_t n; + + if ( !(frame = gcry_malloc_secure (nframe))) + return gpg_err_code_from_errno (errno); + + err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &n, value); + if (err) + return gcry_err_code (err); + if (n < nframe) + { + memmove (frame + (nframe - n), frame, n); + memset (frame, 0, (nframe - n)); + } + + /* FRAME = 00 || MASKED_SEED || MASKED_DB */ + if (frame[0]) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; + } + + dlen = gcry_md_get_algo_dlen (algo); + if (nframe < 1 + 2 * dlen + 1) + { + gcry_free (frame); + return GPG_ERR_TOO_SHORT; + } + if ( !(smask = gcry_malloc_secure (dlen))) + { + rc = gpg_err_code_from_errno (errno); + gcry_free (frame); + return rc; + } + mgf1 (smask, dlen, &frame[1 + dlen], nframe - dlen - 1, algo); + for (n = 1, p = smask; n < 1 + dlen; n++) + frame[n] ^= *p++; + gcry_free (smask); + + if ( !(dmask = gcry_malloc_secure (nframe - dlen - 1))) + { + rc = gpg_err_code_from_errno (errno); + gcry_free (frame); + return rc; + } + mgf1 (dmask, nframe - dlen - 1, &frame[1], dlen, algo); + for (n = 1 + dlen, p = dmask; n < nframe; n++) + frame[n] ^= *p++; + gcry_free (dmask); + + gcry_md_open (&hd, algo, 0); + gcry_md_write (hd, label, labellen); + memcpy (&frame[1], gcry_md_read (hd, 0), dlen); + gcry_md_close (hd); + + if (memcmp (&frame[1], &frame[1 + dlen], dlen)) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; + } + + for (n = 1 + dlen * 2; n < nframe && !frame[n]; n++) + ; + if (n < nframe && frame[n] != 1) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; + } + + n++; + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, &frame[n], nframe - n, NULL); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("value extracted from OAEP encoded data", *r_result); + gcry_free (frame); + + return rc; +} /* Internal function. */ static gcry_err_code_t @@ -1161,22 +1360,76 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, return err; } +static inline int +get_hash_algo (const char *s, size_t n) +{ + static const struct { const char *name; int algo; } hashnames[] = { + { "sha1", GCRY_MD_SHA1 }, + { "md5", GCRY_MD_MD5 }, + { "sha256", GCRY_MD_SHA256 }, + { "ripemd160", GCRY_MD_RMD160 }, + { "rmd160", GCRY_MD_RMD160 }, + { "sha384", GCRY_MD_SHA384 }, + { "sha512", GCRY_MD_SHA512 }, + { "sha224", GCRY_MD_SHA224 }, + { "md2", GCRY_MD_MD2 }, + { "md4", GCRY_MD_MD4 }, + { "tiger", GCRY_MD_TIGER }, + { "haval", GCRY_MD_HAVAL }, + { NULL, 0 } + }; + int algo; + int i; + + for (i=0; hashnames[i].name; i++) + { + if ( strlen (hashnames[i].name) == n + && !memcmp (hashnames[i].name, s, n)) + break; + } + if (hashnames[i].name) + algo = hashnames[i].algo; + else + { + /* In case of not listed or dynamically allocated hash + algorithm we fall back to this somewhat slower + method. Further, it also allows to use OIDs as + algorithm names. */ + char *tmpname; + + tmpname = gcry_malloc (n+1); + if (!tmpname) + algo = 0; /* Out of core - silently give up. */ + else + { + memcpy (tmpname, s, n); + tmpname[n] = 0; + algo = gcry_md_map_name (tmpname); + gcry_free (tmpname); + } + } + return algo; +} + /**************** * Take sexp and return an array of MPI as used for our internal decrypt * function. * s_data = (enc-val - * [(flags [pkcs1])] + * [(flags [raw, pkcs1, oaep, no-blinding, unpad])] + * [(hash-algo <algo>)] + * [(label <label>)] * (<algo> * (<param_name1> <mpi>) * ... * (<param_namen> <mpi>) * )) + * HASH-ALGO and LABEL are specific to OAEP. * RET_MODERN is set to true when at least an empty flags list has been found. */ static gcry_err_code_t sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo, - int *ret_modern, int *ret_want_pkcs1, int *flags) + int *ret_modern, int *flags, struct pk_encoding_ctx *ctx) { gcry_err_code_t err = 0; gcry_sexp_t list = NULL, l2 = NULL; @@ -1187,10 +1440,17 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo, int parsed_flags = 0; const char *elems; gcry_mpi_t *array = NULL; + struct pk_encoding_ctx dummy_ctx; - *ret_want_pkcs1 = 0; *ret_modern = 0; + if (!ctx) + ctx = &dummy_ctx; + ctx->encoding = PUBKEY_ENC_RAW; + ctx->hash_algo = GCRY_MD_SHA1; + ctx->label = NULL; + ctx->labellen = 0; + /* Check that the first element is valid. */ list = gcry_sexp_find_token (sexp, "enc-val" , 0); if (!list) @@ -1229,19 +1489,76 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo, else if (n == 3 && !memcmp (s, "raw", 3)) ; /* This is just a dummy as it is the default. */ else if (n == 5 && !memcmp (s, "pkcs1", 5)) - *ret_want_pkcs1 = 1; + ctx->encoding = PUBKEY_ENC_PKCS1; + else if (n == 4 && !memcmp (s, "oaep", 4)) + ctx->encoding = PUBKEY_ENC_OAEP; else if (n == 11 && ! memcmp (s, "no-blinding", 11)) parsed_flags |= PUBKEY_FLAG_NO_BLINDING; + else if (n == 5 && !memcmp (s, "unpad", 5)) + parsed_flags |= PUBKEY_FLAG_UNPAD; else { err = GPG_ERR_INV_FLAG; goto leave; } } - - /* Get the next which has the actual data. */ gcry_sexp_release (l2); - l2 = gcry_sexp_nth (list, 2); + + /* Get the OAEP parameters HASH-ALGO and LABEL, if any. */ + if (ctx->encoding == PUBKEY_ENC_OAEP) + { + /* Get HASH-ALGO. */ + l2 = gcry_sexp_find_token (list, "hash-algo", 0); + if (l2) + { + s = gcry_sexp_nth_data (l2, 1, &n); + if (!s) + err = GPG_ERR_NO_OBJ; + else + { + ctx->hash_algo = get_hash_algo (s, n); + if (!ctx->hash_algo) + err = GPG_ERR_DIGEST_ALGO; + } + gcry_sexp_release (l2); + if (err) + goto leave; + } + + /* Get LABEL. */ + l2 = gcry_sexp_find_token (list, "label", 0); + if (l2) + { + s = gcry_sexp_nth_data (l2, 1, &n); + if (!s) + err = GPG_ERR_NO_OBJ; + else if (n > 0) + { + ctx->label = gcry_malloc (n); + if (!ctx->label) + err = gpg_err_code_from_errno (errno); + else + { + memcpy (ctx->label, s, n); + ctx->labellen = n; + } + } + gcry_sexp_release (l2); + if (err) + goto leave; + } + } + + /* Get the next which has the actual data - skip HASH-ALGO and LABEL. */ + for (i = 2; (l2 = gcry_sexp_nth (list, i)) != NULL; i++) + { + s = gcry_sexp_nth_data (l2, 0, &n); + if (!(n == 9 && !memcmp (s, "hash-algo", 9)) + && !(n == 5 && !memcmp (s, "label", 5))) + break; + gcry_sexp_release (l2); + } + if (!l2) { err = GPG_ERR_NO_OBJ; /* No cdr for the data object. */ @@ -1294,6 +1611,7 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo, _gcry_module_release (module); ath_mutex_unlock (&pubkeys_registered_lock); gcry_free (array); + gcry_free (ctx->label); } else { @@ -1314,32 +1632,45 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo, (<mpi>) or (data - [(flags [pkcs1])] + [(flags [raw, pkcs1, oaep, no-blinding])] [(hash <algo> <value>)] [(value <text>)] + [(hash-algo <algo>)] + [(label <label>)] ) Either the VALUE or the HASH element must be present for use with signatures. VALUE is used for encryption. + HASH-ALGO and LABEL are specific to OAEP. + NBITS is the length of the key in bits. */ static gcry_err_code_t sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, - int for_encryption, int *flags) + int for_encryption, int *flags, struct pk_encoding_ctx *ctx) { gcry_err_code_t rc = 0; gcry_sexp_t ldata, lhash, lvalue; int i; size_t n; const char *s; - int is_raw = 0, is_pkcs1 = 0, unknown_flag=0; + int unknown_flag=0; int parsed_flags = 0, dummy_flags; + struct pk_encoding_ctx dummy_ctx; if (! flags) flags = &dummy_flags; + if (! ctx) + ctx = &dummy_ctx; + + ctx->encoding = PUBKEY_ENC_UNKNOWN; + ctx->hash_algo = GCRY_MD_SHA1; + ctx->label = NULL; + ctx->labellen = 0; + *ret_mpi = NULL; ldata = gcry_sexp_find_token (input, "data", 0); if (!ldata) @@ -1359,9 +1690,11 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, if (!s) ; /* not a data element*/ else if ( n == 3 && !memcmp (s, "raw", 3)) - is_raw = 1; + ctx->encoding = PUBKEY_ENC_RAW; else if ( n == 5 && !memcmp (s, "pkcs1", 5)) - is_pkcs1 = 1; + ctx->encoding = PUBKEY_ENC_PKCS1; + else if ( n == 4 && !memcmp (s, "oaep", 4)) + ctx->encoding = PUBKEY_ENC_OAEP; else if (n == 11 && ! memcmp (s, "no-blinding", 11)) parsed_flags |= PUBKEY_FLAG_NO_BLINDING; else @@ -1371,8 +1704,8 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, } } - if (!is_pkcs1 && !is_raw) - is_raw = 1; /* default to raw */ + if (ctx->encoding == PUBKEY_ENC_UNKNOWN) + ctx->encoding = PUBKEY_ENC_RAW; /* default to raw */ /* Get HASH or MPI */ lhash = gcry_sexp_find_token (ldata, "hash", 0); @@ -1382,15 +1715,13 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, rc = GPG_ERR_INV_OBJ; /* none or both given */ else if (unknown_flag) rc = GPG_ERR_INV_FLAG; - else if (is_raw && is_pkcs1 && !for_encryption) - rc = GPG_ERR_CONFLICT; - else if (is_raw && lvalue) + else if (ctx->encoding == PUBKEY_ENC_RAW && lvalue) { *ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, 0); if (!*ret_mpi) rc = GPG_ERR_INV_OBJ; } - else if (is_pkcs1 && lvalue && for_encryption) + else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue && for_encryption) { /* Create pkcs#1 block type 2 padding. */ unsigned char *frame = NULL; @@ -1457,7 +1788,7 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, gcry_free(frame); } - else if (is_pkcs1 && lhash && !for_encryption) + else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash && !for_encryption) { /* Create pkcs#1 block type 1 padding. */ if (gcry_sexp_length (lhash) != 3) @@ -1466,21 +1797,6 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, rc = GPG_ERR_INV_OBJ; else { - static struct { const char *name; int algo; } hashnames[] = - { { "sha1", GCRY_MD_SHA1 }, - { "md5", GCRY_MD_MD5 }, - { "sha256", GCRY_MD_SHA256 }, - { "ripemd160", GCRY_MD_RMD160 }, - { "rmd160", GCRY_MD_RMD160 }, - { "sha384", GCRY_MD_SHA384 }, - { "sha512", GCRY_MD_SHA512 }, - { "sha224", GCRY_MD_SHA224 }, - { "md2", GCRY_MD_MD2 }, - { "md4", GCRY_MD_MD4 }, - { "tiger", GCRY_MD_TIGER }, - { "haval", GCRY_MD_HAVAL }, - { NULL, 0 } - }; int algo; byte asn[100]; byte *frame = NULL; @@ -1489,34 +1805,7 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, size_t valuelen; size_t asnlen, dlen; - for (i=0; hashnames[i].name; i++) - { - if ( strlen (hashnames[i].name) == n - && !memcmp (hashnames[i].name, s, n)) - break; - } - if (hashnames[i].name) - algo = hashnames[i].algo; - else - { - /* In case of not listed or dynamically allocated hash - algorithm we fall back to this somewhat slower - method. Further, it also allows to use OIDs as - algorithm names. */ - char *tmpname; - - tmpname = gcry_malloc (n+1); - if (!tmpname) - algo = 0; /* Out of core - silently give up. */ - else - { - memcpy (tmpname, s, n); - tmpname[n] = 0; - algo = gcry_md_map_name (tmpname); - gcry_free (tmpname); - } - } - + algo = get_hash_algo (s, n); asnlen = DIM(asn); dlen = gcry_md_get_algo_dlen (algo); @@ -1567,15 +1856,74 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi, gcry_free (frame); } } + else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue && for_encryption) + { + const void * value; + size_t valuelen; + + if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) + rc = GPG_ERR_INV_OBJ; + else + { + gcry_sexp_t list; + + /* Get HASH-ALGO. */ + list = gcry_sexp_find_token (ldata, "hash-algo", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else + { + ctx->hash_algo = get_hash_algo (s, n); + if (!ctx->hash_algo) + rc = GPG_ERR_DIGEST_ALGO; + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + /* Get LABEL. */ + list = gcry_sexp_find_token (ldata, "label", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else if (n > 0) + { + ctx->label = gcry_malloc (n); + if (!ctx->label) + rc = gpg_err_code_from_errno (errno); + else + { + memcpy (ctx->label, s, n); + ctx->labellen = n; + } + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + rc = oaep_encode (ret_mpi, nbits, ctx->hash_algo, value, valuelen, + ctx->label, ctx->labellen); + } + } else rc = GPG_ERR_CONFLICT; + leave: gcry_sexp_release (ldata); gcry_sexp_release (lhash); gcry_sexp_release (lvalue); if (!rc) *flags = parsed_flags; + else + gcry_free (ctx->label); return rc; } @@ -1609,6 +1957,7 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) gcry_mpi_t *pkey = NULL, data = NULL, *ciph = NULL; const char *algo_name, *algo_elems; int flags; + struct pk_encoding_ctx ctx; gcry_err_code_t rc; gcry_pk_spec_t *pubkey = NULL; gcry_module_t module = NULL; @@ -1617,6 +1966,7 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) REGISTER_DEFAULT_PUBKEYS; + memset (&ctx, 0, sizeof(struct pk_encoding_ctx)); /* Get the key. */ rc = sexp_to_key (s_pkey, 0, NULL, &pkey, &module); if (rc) @@ -1638,7 +1988,7 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) /* Get the stuff we want to encrypt. */ rc = sexp_data_to_mpi (s_data, gcry_pk_get_nbits (s_pkey), &data, 1, - &flags); + &flags, &ctx); if (rc) goto leave; @@ -1721,6 +2071,8 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) ath_mutex_unlock (&pubkeys_registered_lock); } + gcry_free (ctx.label); + return gcry_error (rc); } @@ -1751,12 +2103,14 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) gcry_error_t gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) { - gcry_mpi_t *skey = NULL, *data = NULL, plain = NULL; - int modern, want_pkcs1, flags; + gcry_mpi_t *skey = NULL, *data = NULL, plain = NULL, unpad = NULL; + int modern, flags; + struct pk_encoding_ctx ctx; gcry_err_code_t rc; gcry_module_t module_enc = NULL, module_key = NULL; *r_plain = NULL; + ctx.label = NULL; REGISTER_DEFAULT_PUBKEYS; @@ -1764,7 +2118,7 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) if (rc) goto leave; - rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &want_pkcs1, &flags); + rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &flags, &ctx); if (rc) goto leave; @@ -1778,6 +2132,18 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) if (rc) goto leave; + /* Do un-padding. */ + /* FIXME: Currently only OAEP is supported. */ + if ((flags & PUBKEY_FLAG_UNPAD) && ctx.encoding == PUBKEY_ENC_OAEP) + { + rc = oaep_decode (&unpad, gcry_pk_get_nbits (s_skey), ctx.hash_algo, + plain, ctx.label, ctx.labellen); + mpi_free (plain); + if (rc) + goto leave; + plain = unpad; + } + if (gcry_sexp_build (r_plain, NULL, modern? "(value %m)" : "%m", plain)) BUG (); @@ -1807,6 +2173,8 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) ath_mutex_unlock (&pubkeys_registered_lock); } + gcry_free (ctx.label); + return gcry_error (rc); } @@ -1869,7 +2237,7 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey) /* Get the stuff we want to sign. Note that pk_get_nbits does also work on a private key. */ rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_skey), - &hash, 0, NULL); + &hash, 0, NULL, NULL); if (rc) goto leave; @@ -1980,7 +2348,7 @@ gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey) goto leave; } - rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_pkey), &hash, 0, 0); + rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_pkey), &hash, 0, 0, NULL); if (rc) goto leave; diff --git a/src/cipher.h b/src/cipher.h index a5688005..689995b8 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -1,5 +1,5 @@ /* cipher.h - * Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 2002, 2003, 2009 Free Software Foundation, Inc. * * This file is part of Libgcrypt. * @@ -27,6 +27,23 @@ #include "../random/random.h" #define PUBKEY_FLAG_NO_BLINDING (1 << 0) +#define PUBKEY_FLAG_UNPAD (1 << 1) + +enum pk_encoding + { + PUBKEY_ENC_RAW, + PUBKEY_ENC_PKCS1, + PUBKEY_ENC_OAEP, + PUBKEY_ENC_UNKNOWN + }; + +struct pk_encoding_ctx +{ + enum pk_encoding encoding; + int hash_algo; + unsigned char *label; + size_t labellen; +}; #define CIPHER_INFO_NO_WEAK_KEY 1 diff --git a/tests/basic.c b/tests/basic.c index 2216476b..cf4fb8c3 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -2295,6 +2295,9 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey) { "(data\n (flags pkcs1)\n" " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n", 0 }, + { "(data\n (flags oaep)\n" + " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n", + GPG_ERR_CONFLICT }, /* This test is to see whether hash algorithms not hard wired in pubkey.c are detected: */ { "(data\n (flags pkcs1)\n" @@ -2355,6 +2358,151 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey) } static void +check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey) +{ + gcry_error_t rc; + gcry_sexp_t plain, ciph, data; + int dataidx; + static struct + { + const char *data; + const char *hint; + int unpadded; + int expected_rc; + } datas[] = + { + { "(data\n (flags pkcs1)\n" + " (value #11223344556677889900AA#))\n", + NULL, + 0, + 0 }, + { "(data\n (flags oaep)\n" + " (value #11223344556677889900AA#))\n", + "(flags oaep unpad)", + 1, + 0 }, + { "(data\n (flags oaep)\n (hash-algo sha1)\n" + " (value #11223344556677889900AA#))\n", + "(flags oaep unpad)(hash-algo sha1)", + 1, + 0 }, + { "(data\n (flags oaep)\n (hash-algo sha1)\n (label \"test\")\n" + " (value #11223344556677889900AA#))\n", + "(flags oaep unpad)(hash-algo sha1)(label \"test\")", + 1, + 0 }, + { "(data\n (flags )\n" " (value #11223344556677889900AA#))\n", + NULL, + 1, + 0 }, + { "(data\n (flags )\n" " (value #0090223344556677889900AA#))\n", + NULL, + 1, + 0 }, + { "(data\n (flags raw)\n" " (value #11223344556677889900AA#))\n", + NULL, + 1, + 0 }, + { "(data\n (flags pkcs1)\n" + " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n", + NULL, + 0, + GPG_ERR_CONFLICT }, + { "(data\n (flags raw foo)\n" + " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n", + NULL, + 0, + GPG_ERR_INV_FLAG }, + { NULL } + }; + + (void)n; + + for (dataidx = 0; datas[dataidx].data; dataidx++) + { + if (verbose) + fprintf (stderr, " encryption/decryption test %d\n", dataidx); + + rc = gcry_sexp_sscan (&data, NULL, datas[dataidx].data, + strlen (datas[dataidx].data)); + if (rc) + die ("converting data failed: %s\n", gpg_strerror (rc)); + + rc = gcry_pk_encrypt (&ciph, data, pkey); + if (gcry_err_code (rc) != datas[dataidx].expected_rc) + fail ("gcry_pk_encrypt failed: %s\n", gpg_strerror (rc)); + + if (!rc) + { + /* Insert decoding hint to CIPH. */ + if (datas[dataidx].hint) + { + size_t hint_len, len; + char *hint, *buf; + gcry_sexp_t list; + + /* Convert decoding hint into canonical sexp. */ + hint_len = gcry_sexp_new (&list, datas[dataidx].hint, + strlen (datas[dataidx].hint), 1); + hint_len = gcry_sexp_sprint (list, GCRYSEXP_FMT_CANON, NULL, 0); + hint = gcry_malloc (hint_len); + if (!hint) + die ("can't allocate memory\n"); + hint_len = gcry_sexp_sprint (list, GCRYSEXP_FMT_CANON, hint, + hint_len); + gcry_sexp_release (list); + + /* Convert CIPH into canonical sexp. */ + len = gcry_sexp_sprint (ciph, GCRYSEXP_FMT_CANON, NULL, 0); + buf = gcry_malloc (len + hint_len); + if (!buf) + die ("can't allocate memory\n"); + len = gcry_sexp_sprint (ciph, GCRYSEXP_FMT_CANON, buf, len); + /* assert (!strcmp (buf, "(7:enc-val", 10)); */ + + /* Copy decoding hint into CIPH. */ + memmove (buf + 10 + hint_len, buf + 10, len - 10); + memcpy (buf + 10, hint, hint_len); + gcry_free (hint); + gcry_sexp_new (&list, buf, len + hint_len, 1); + gcry_free (buf); + gcry_sexp_release (ciph); + ciph = list; + } + rc = gcry_pk_decrypt (&plain, ciph, skey); + if (rc) + fail ("gcry_pk_decrypt failed: %s\n", gpg_strerror (rc)); + else if (datas[dataidx].unpadded) + { + gcry_sexp_t p1, p2; + + p1 = gcry_sexp_find_token (data, "value", 0); + p2 = gcry_sexp_find_token (plain, "value", 0); + if (p1 && p2) + { + const char *s1, *s2; + size_t n1, n2; + + s1 = gcry_sexp_nth_data (p1, 1, &n1); + s2 = gcry_sexp_nth_data (p2, 1, &n2); + if (n1 != n2 || memcmp (s1, s2, n1)) + fail ("gcry_pk_encrypt/gcry_pk_decrypt do not roundtrip\n"); + } + gcry_sexp_release (p1); + gcry_sexp_release (p2); + } + } + + gcry_sexp_release (plain); + plain = NULL; + gcry_sexp_release (ciph); + ciph = NULL; + gcry_sexp_release (data); + data = NULL; + } +} + +static void check_pubkey_grip (int n, const unsigned char *grip, gcry_sexp_t skey, gcry_sexp_t pkey) { @@ -2376,6 +2524,8 @@ do_check_one_pubkey (int n, gcry_sexp_t skey, gcry_sexp_t pkey, { if (flags & FLAG_SIGN) check_pubkey_sign (n, skey, pkey); + if (flags & FLAG_CRYPT) + check_pubkey_crypt (n, skey, pkey); if (grip && (flags & FLAG_GRIP)) check_pubkey_grip (n, grip, skey, pkey); } @@ -2439,6 +2589,8 @@ check_one_pubkey_new (int n) get_keys_new (&pkey, &skey); do_check_one_pubkey (n, skey, pkey, NULL, FLAG_SIGN | FLAG_CRYPT); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); } /* Run all tests for the public key functions. */ |