summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--cipher/pubkey.c504
-rw-r--r--src/cipher.h19
-rw-r--r--tests/basic.c152
4 files changed, 606 insertions, 71 deletions
diff --git a/TODO b/TODO
index 596912a6..ffadc06d 100644
--- a/TODO
+++ b/TODO
@@ -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. */