From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/lib/libgcrypt/cipher/pubkey.c | 4212 +++++++++++++++++++++++++++++++ 1 file changed, 4212 insertions(+) create mode 100644 grub-core/lib/libgcrypt/cipher/pubkey.c (limited to 'grub-core/lib/libgcrypt/cipher/pubkey.c') diff --git a/grub-core/lib/libgcrypt/cipher/pubkey.c b/grub-core/lib/libgcrypt/cipher/pubkey.c new file mode 100644 index 0000000..ca087ad --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/pubkey.c @@ -0,0 +1,4212 @@ +/* pubkey.c - pubkey dispatcher + * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005, + * 2007, 2008, 2011 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser general Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +#include "g10lib.h" +#include "mpi.h" +#include "cipher.h" +#include "ath.h" + + +static gcry_err_code_t pubkey_decrypt (int algo, gcry_mpi_t *result, + gcry_mpi_t *data, gcry_mpi_t *skey, + int flags); +static gcry_err_code_t pubkey_sign (int algo, gcry_mpi_t *resarr, + gcry_mpi_t hash, gcry_mpi_t *skey); +static gcry_err_code_t pubkey_verify (int algo, gcry_mpi_t hash, + gcry_mpi_t *data, gcry_mpi_t *pkey, + int (*cmp) (void *, gcry_mpi_t), + void *opaque); + + +/* A dummy extraspec so that we do not need to tests the extraspec + field from the module specification against NULL and instead + directly test the respective fields of extraspecs. */ +static pk_extra_spec_t dummy_extra_spec; + + +/* This is the list of the default public-key ciphers included in + libgcrypt. FIPS_ALLOWED indicated whether the algorithm is used in + FIPS mode. */ +static struct pubkey_table_entry +{ + gcry_pk_spec_t *pubkey; + pk_extra_spec_t *extraspec; + unsigned int algorithm; + int fips_allowed; +} pubkey_table[] = + { +#if USE_RSA + { &_gcry_pubkey_spec_rsa, + &_gcry_pubkey_extraspec_rsa, GCRY_PK_RSA, 1}, +#endif +#if USE_ELGAMAL + { &_gcry_pubkey_spec_elg, + &_gcry_pubkey_extraspec_elg, GCRY_PK_ELG }, + { &_gcry_pubkey_spec_elg, + &_gcry_pubkey_extraspec_elg, GCRY_PK_ELG_E }, +#endif +#if USE_DSA + { &_gcry_pubkey_spec_dsa, + &_gcry_pubkey_extraspec_dsa, GCRY_PK_DSA, 1 }, +#endif +#if USE_ECC + { &_gcry_pubkey_spec_ecdsa, + &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDSA, 0 }, + { &_gcry_pubkey_spec_ecdh, + &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDH, 0 }, +#endif + { NULL, 0 }, + }; + +/* List of registered ciphers. */ +static gcry_module_t pubkeys_registered; + +/* This is the lock protecting PUBKEYS_REGISTERED. */ +static ath_mutex_t pubkeys_registered_lock = ATH_MUTEX_INITIALIZER;; + +/* Flag to check whether the default pubkeys have already been + registered. */ +static int default_pubkeys_registered; + +/* Convenient macro for registering the default digests. */ +#define REGISTER_DEFAULT_PUBKEYS \ + do \ + { \ + ath_mutex_lock (&pubkeys_registered_lock); \ + if (! default_pubkeys_registered) \ + { \ + pk_register_default (); \ + default_pubkeys_registered = 1; \ + } \ + ath_mutex_unlock (&pubkeys_registered_lock); \ + } \ + while (0) + +/* These dummy functions are used in case a cipher implementation + refuses to provide it's own functions. */ + +static gcry_err_code_t +dummy_generate (int algorithm, unsigned int nbits, unsigned long dummy, + gcry_mpi_t *skey, gcry_mpi_t **retfactors) +{ + (void)algorithm; + (void)nbits; + (void)dummy; + (void)skey; + (void)retfactors; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static gcry_err_code_t +dummy_check_secret_key (int algorithm, gcry_mpi_t *skey) +{ + (void)algorithm; + (void)skey; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static gcry_err_code_t +dummy_encrypt (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data, + gcry_mpi_t *pkey, int flags) +{ + (void)algorithm; + (void)resarr; + (void)data; + (void)pkey; + (void)flags; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static gcry_err_code_t +dummy_decrypt (int algorithm, gcry_mpi_t *result, gcry_mpi_t *data, + gcry_mpi_t *skey, int flags) +{ + (void)algorithm; + (void)result; + (void)data; + (void)skey; + (void)flags; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static gcry_err_code_t +dummy_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data, + gcry_mpi_t *skey) +{ + (void)algorithm; + (void)resarr; + (void)data; + (void)skey; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static gcry_err_code_t +dummy_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data, + gcry_mpi_t *pkey, + int (*cmp) (void *, gcry_mpi_t), void *opaquev) +{ + (void)algorithm; + (void)hash; + (void)data; + (void)pkey; + (void)cmp; + (void)opaquev; + fips_signal_error ("using dummy public key function"); + return GPG_ERR_NOT_IMPLEMENTED; +} + +static unsigned +dummy_get_nbits (int algorithm, gcry_mpi_t *pkey) +{ + (void)algorithm; + (void)pkey; + fips_signal_error ("using dummy public key function"); + return 0; +} + +/* Internal function. Register all the pubkeys included in + PUBKEY_TABLE. Returns zero on success or an error code. */ +static void +pk_register_default (void) +{ + gcry_err_code_t err = 0; + int i; + + for (i = 0; (! err) && pubkey_table[i].pubkey; i++) + { +#define pubkey_use_dummy(func) \ + if (! pubkey_table[i].pubkey->func) \ + pubkey_table[i].pubkey->func = dummy_##func; + + pubkey_use_dummy (generate); + pubkey_use_dummy (check_secret_key); + pubkey_use_dummy (encrypt); + pubkey_use_dummy (decrypt); + pubkey_use_dummy (sign); + pubkey_use_dummy (verify); + pubkey_use_dummy (get_nbits); +#undef pubkey_use_dummy + + err = _gcry_module_add (&pubkeys_registered, + pubkey_table[i].algorithm, + (void *) pubkey_table[i].pubkey, + (void *) pubkey_table[i].extraspec, + NULL); + } + + if (err) + BUG (); +} + +/* Internal callback function. Used via _gcry_module_lookup. */ +static int +gcry_pk_lookup_func_name (void *spec, void *data) +{ + gcry_pk_spec_t *pubkey = (gcry_pk_spec_t *) spec; + char *name = (char *) data; + const char **aliases = pubkey->aliases; + int ret = stricmp (name, pubkey->name); + + while (ret && *aliases) + ret = stricmp (name, *aliases++); + + return ! ret; +} + +/* Internal function. Lookup a pubkey entry by it's name. */ +static gcry_module_t +gcry_pk_lookup_name (const char *name) +{ + gcry_module_t pubkey; + + pubkey = _gcry_module_lookup (pubkeys_registered, (void *) name, + gcry_pk_lookup_func_name); + + return pubkey; +} + +/* Register a new pubkey module whose specification can be found in + PUBKEY. On success, a new algorithm ID is stored in ALGORITHM_ID + and a pointer representhing this module is stored in MODULE. */ +gcry_error_t +_gcry_pk_register (gcry_pk_spec_t *pubkey, + pk_extra_spec_t *extraspec, + unsigned int *algorithm_id, + gcry_module_t *module) +{ + gcry_err_code_t err = GPG_ERR_NO_ERROR; + gcry_module_t mod; + + /* We do not support module loading in fips mode. */ + if (fips_mode ()) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + ath_mutex_lock (&pubkeys_registered_lock); + err = _gcry_module_add (&pubkeys_registered, 0, + (void *) pubkey, + (void *)(extraspec? extraspec : &dummy_extra_spec), + &mod); + ath_mutex_unlock (&pubkeys_registered_lock); + + if (! err) + { + *module = mod; + *algorithm_id = mod->mod_id; + } + + return err; +} + +/* Unregister the pubkey identified by ID, which must have been + registered with gcry_pk_register. */ +void +gcry_pk_unregister (gcry_module_t module) +{ + ath_mutex_lock (&pubkeys_registered_lock); + _gcry_module_release (module); + ath_mutex_unlock (&pubkeys_registered_lock); +} + +static void +release_mpi_array (gcry_mpi_t *array) +{ + for (; *array; array++) + { + mpi_free(*array); + *array = NULL; + } +} + +/**************** + * Map a string to the pubkey algo + */ +int +gcry_pk_map_name (const char *string) +{ + gcry_module_t pubkey; + int algorithm = 0; + + if (!string) + return 0; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = gcry_pk_lookup_name (string); + if (pubkey) + { + algorithm = pubkey->mod_id; + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return algorithm; +} + + +/* Map the public key algorithm whose ID is contained in ALGORITHM to + a string representation of the algorithm name. For unknown + algorithm IDs this functions returns "?". */ +const char * +gcry_pk_algo_name (int algorithm) +{ + gcry_module_t pubkey; + const char *name; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + name = ((gcry_pk_spec_t *) pubkey->spec)->name; + _gcry_module_release (pubkey); + } + else + name = "?"; + ath_mutex_unlock (&pubkeys_registered_lock); + + return name; +} + + +/* A special version of gcry_pk_algo name to return the first aliased + name of the algorithm. This is required to adhere to the spki + specs where the algorithm names are lowercase. */ +const char * +_gcry_pk_aliased_algo_name (int algorithm) +{ + const char *name = NULL; + gcry_module_t module; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + gcry_pk_spec_t *pubkey = (gcry_pk_spec_t *) module->spec; + + name = pubkey->aliases? *pubkey->aliases : NULL; + if (!name || !*name) + name = pubkey->name; + _gcry_module_release (module); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return name; +} + + +static void +disable_pubkey_algo (int algorithm) +{ + gcry_module_t pubkey; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + if (! (pubkey-> flags & FLAG_MODULE_DISABLED)) + pubkey->flags |= FLAG_MODULE_DISABLED; + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); +} + + +/**************** + * A USE of 0 means: don't care. + */ +static gcry_err_code_t +check_pubkey_algo (int algorithm, unsigned use) +{ + gcry_err_code_t err = GPG_ERR_NO_ERROR; + gcry_pk_spec_t *pubkey; + gcry_module_t module; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + pubkey = (gcry_pk_spec_t *) module->spec; + + if (((use & GCRY_PK_USAGE_SIGN) + && (! (pubkey->use & GCRY_PK_USAGE_SIGN))) + || ((use & GCRY_PK_USAGE_ENCR) + && (! (pubkey->use & GCRY_PK_USAGE_ENCR)))) + err = GPG_ERR_WRONG_PUBKEY_ALGO; + else if (module->flags & FLAG_MODULE_DISABLED) + err = GPG_ERR_PUBKEY_ALGO; + _gcry_module_release (module); + } + else + err = GPG_ERR_PUBKEY_ALGO; + ath_mutex_unlock (&pubkeys_registered_lock); + + return err; +} + + +/**************** + * Return the number of public key material numbers + */ +static int +pubkey_get_npkey (int algorithm) +{ + gcry_module_t pubkey; + int npkey = 0; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + npkey = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_pkey); + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return npkey; +} + +/**************** + * Return the number of secret key material numbers + */ +static int +pubkey_get_nskey (int algorithm) +{ + gcry_module_t pubkey; + int nskey = 0; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + nskey = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_skey); + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return nskey; +} + +/**************** + * Return the number of signature material numbers + */ +static int +pubkey_get_nsig (int algorithm) +{ + gcry_module_t pubkey; + int nsig = 0; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + nsig = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_sig); + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return nsig; +} + +/**************** + * Return the number of encryption material numbers + */ +static int +pubkey_get_nenc (int algorithm) +{ + gcry_module_t pubkey; + int nenc = 0; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + nenc = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_enc); + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return nenc; +} + + +/* Generate a new public key with algorithm ALGORITHM of size NBITS + and return it at SKEY. USE_E depends on the ALGORITHM. GENPARMS + is passed to the algorithm module if it features an extended + generation function. RETFACTOR is used by some algorithms to + return certain additional information which are in general not + required. + + The function returns the error code number or 0 on success. */ +static gcry_err_code_t +pubkey_generate (int algorithm, + unsigned int nbits, + unsigned long use_e, + gcry_sexp_t genparms, + gcry_mpi_t *skey, gcry_mpi_t **retfactors, + gcry_sexp_t *r_extrainfo) +{ + gcry_err_code_t ec = GPG_ERR_PUBKEY_ALGO; + gcry_module_t pubkey; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + pk_extra_spec_t *extraspec = pubkey->extraspec; + + if (extraspec && extraspec->ext_generate) + { + /* Use the extended generate function. */ + ec = extraspec->ext_generate + (algorithm, nbits, use_e, genparms, skey, retfactors, r_extrainfo); + } + else + { + /* Use the standard generate function. */ + ec = ((gcry_pk_spec_t *) pubkey->spec)->generate + (algorithm, nbits, use_e, skey, retfactors); + } + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return ec; +} + + +static gcry_err_code_t +pubkey_check_secret_key (int algorithm, gcry_mpi_t *skey) +{ + gcry_err_code_t err = GPG_ERR_PUBKEY_ALGO; + gcry_module_t pubkey; + + REGISTER_DEFAULT_PUBKEYS; + + ath_mutex_lock (&pubkeys_registered_lock); + pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (pubkey) + { + err = ((gcry_pk_spec_t *) pubkey->spec)->check_secret_key + (algorithm, skey); + _gcry_module_release (pubkey); + } + ath_mutex_unlock (&pubkeys_registered_lock); + + return err; +} + + +/**************** + * This is the interface to the public key encryption. Encrypt DATA + * with PKEY and put it into RESARR which should be an array of MPIs + * of size PUBKEY_MAX_NENC (or less if the algorithm allows this - + * check with pubkey_get_nenc() ) + */ +static gcry_err_code_t +pubkey_encrypt (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data, + gcry_mpi_t *pkey, int flags) +{ + gcry_pk_spec_t *pubkey; + gcry_module_t module; + gcry_err_code_t rc; + int i; + + /* Note: In fips mode DBG_CIPHER will enver evaluate to true but as + an extra failsafe protection we explicitly test for fips mode + here. */ + if (DBG_CIPHER && !fips_mode ()) + { + log_debug ("pubkey_encrypt: algo=%d\n", algorithm); + for(i = 0; i < pubkey_get_npkey (algorithm); i++) + log_mpidump (" pkey:", pkey[i]); + log_mpidump (" data:", data); + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + pubkey = (gcry_pk_spec_t *) module->spec; + rc = pubkey->encrypt (algorithm, resarr, data, pkey, flags); + _gcry_module_release (module); + goto ready; + } + rc = GPG_ERR_PUBKEY_ALGO; + + ready: + ath_mutex_unlock (&pubkeys_registered_lock); + + if (!rc && DBG_CIPHER && !fips_mode ()) + { + for(i = 0; i < pubkey_get_nenc (algorithm); i++) + log_mpidump(" encr:", resarr[i] ); + } + return rc; +} + + +/**************** + * This is the interface to the public key decryption. + * ALGO gives the algorithm to use and this implicitly determines + * the size of the arrays. + * result is a pointer to a mpi variable which will receive a + * newly allocated mpi or NULL in case of an error. + */ +static gcry_err_code_t +pubkey_decrypt (int algorithm, gcry_mpi_t *result, gcry_mpi_t *data, + gcry_mpi_t *skey, int flags) +{ + gcry_pk_spec_t *pubkey; + gcry_module_t module; + gcry_err_code_t rc; + int i; + + *result = NULL; /* so the caller can always do a mpi_free */ + if (DBG_CIPHER && !fips_mode ()) + { + log_debug ("pubkey_decrypt: algo=%d\n", algorithm); + for(i = 0; i < pubkey_get_nskey (algorithm); i++) + log_mpidump (" skey:", skey[i]); + for(i = 0; i < pubkey_get_nenc (algorithm); i++) + log_mpidump (" data:", data[i]); + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + pubkey = (gcry_pk_spec_t *) module->spec; + rc = pubkey->decrypt (algorithm, result, data, skey, flags); + _gcry_module_release (module); + goto ready; + } + + rc = GPG_ERR_PUBKEY_ALGO; + + ready: + ath_mutex_unlock (&pubkeys_registered_lock); + + if (!rc && DBG_CIPHER && !fips_mode ()) + log_mpidump (" plain:", *result); + + return rc; +} + + +/**************** + * This is the interface to the public key signing. + * Sign data with skey and put the result into resarr which + * should be an array of MPIs of size PUBKEY_MAX_NSIG (or less if the + * algorithm allows this - check with pubkey_get_nsig() ) + */ +static gcry_err_code_t +pubkey_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data, + gcry_mpi_t *skey) +{ + gcry_pk_spec_t *pubkey; + gcry_module_t module; + gcry_err_code_t rc; + int i; + + if (DBG_CIPHER && !fips_mode ()) + { + log_debug ("pubkey_sign: algo=%d\n", algorithm); + for(i = 0; i < pubkey_get_nskey (algorithm); i++) + log_mpidump (" skey:", skey[i]); + log_mpidump(" data:", data ); + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + pubkey = (gcry_pk_spec_t *) module->spec; + rc = pubkey->sign (algorithm, resarr, data, skey); + _gcry_module_release (module); + goto ready; + } + + rc = GPG_ERR_PUBKEY_ALGO; + + ready: + ath_mutex_unlock (&pubkeys_registered_lock); + + if (!rc && DBG_CIPHER && !fips_mode ()) + for (i = 0; i < pubkey_get_nsig (algorithm); i++) + log_mpidump (" sig:", resarr[i]); + + return rc; +} + +/**************** + * Verify a public key signature. + * Return 0 if the signature is good + */ +static gcry_err_code_t +pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data, + gcry_mpi_t *pkey, + int (*cmp)(void *, gcry_mpi_t), void *opaquev) +{ + gcry_pk_spec_t *pubkey; + gcry_module_t module; + gcry_err_code_t rc; + int i; + + if (DBG_CIPHER && !fips_mode ()) + { + log_debug ("pubkey_verify: algo=%d\n", algorithm); + for (i = 0; i < pubkey_get_npkey (algorithm); i++) + log_mpidump (" pkey", pkey[i]); + for (i = 0; i < pubkey_get_nsig (algorithm); i++) + log_mpidump (" sig", data[i]); + log_mpidump (" hash", hash); + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = _gcry_module_lookup_id (pubkeys_registered, algorithm); + if (module) + { + pubkey = (gcry_pk_spec_t *) module->spec; + rc = pubkey->verify (algorithm, hash, data, pkey, cmp, opaquev); + _gcry_module_release (module); + goto ready; + } + + rc = GPG_ERR_PUBKEY_ALGO; + + ready: + ath_mutex_unlock (&pubkeys_registered_lock); + return rc; +} + + +/* Turn VALUE into an octet string and store it in an allocated buffer + at R_FRAME or - if R_RAME is NULL - copy it into the caller + provided buffer SPACE; either SPACE or R_FRAME may be used. If + SPACE if not NULL, the caller must provide a buffer of at least + NBYTES. If the resulting octet string is shorter than NBYTES pad + it to the left with zeroes. If VALUE does not fit into NBYTES + return an error code. */ +static gpg_err_code_t +octet_string_from_mpi (unsigned char **r_frame, void *space, + gcry_mpi_t value, size_t nbytes) +{ + gpg_err_code_t rc; + size_t nframe, noff, n; + unsigned char *frame; + + if (!r_frame == !space) + return GPG_ERR_INV_ARG; /* Only one may be used. */ + + if (r_frame) + *r_frame = NULL; + + rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG, + NULL, 0, &nframe, value)); + if (rc) + return rc; + if (nframe > nbytes) + return GPG_ERR_TOO_LARGE; /* Value too long to fit into NBYTES. */ + + noff = (nframe < nbytes)? nbytes - nframe : 0; + n = nframe + noff; + if (space) + frame = space; + else + { + frame = mpi_is_secure (value)? gcry_malloc_secure (n) : gcry_malloc (n); + if (!frame) + { + rc = gpg_err_code_from_syserror (); + return rc; + } + } + if (noff) + memset (frame, 0, noff); + nframe += noff; + rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG, + frame+noff, nframe-noff, NULL, value)); + if (rc) + { + gcry_free (frame); + return rc; + } + + if (r_frame) + *r_frame = frame; + return 0; +} + + +/* Encode {VALUE,VALUELEN} for an NBITS keys using the pkcs#1 block + type 2 padding. On sucess the result is stored as a new MPI at + R_RESULT. On error the value at R_RESULT is undefined. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the seed instead of using a random string for it. This feature is + only useful for regression tests. Note that this value may not + contain zero bytes. + + We encode the value in this way: + + 0 2 RND(n bytes) 0 VALUE + + 0 is a marker we unfortunately can't encode because we return an + MPI which strips all leading zeroes. + 2 is the block type. + RND are non-zero random bytes. + + (Note that OpenPGP includes the cipher algorithm and a checksum in + VALUE; the caller needs to prepare the value accordingly.) + */ +static gcry_err_code_t +pkcs1_encode_for_encryption (gcry_mpi_t *r_result, unsigned int nbits, + const unsigned char *value, size_t valuelen, + const unsigned char *random_override, + size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + int i; + size_t n; + unsigned char *p; + + if (valuelen + 7 > nframe || !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_syserror (); + + n = 0; + frame[n++] = 0; + frame[n++] = 2; /* block type */ + i = nframe - 3 - valuelen; + gcry_assert (i > 0); + + if (random_override) + { + int j; + + if (random_override_len != i) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + /* Check that random does not include a zero byte. */ + for (j=0; j < random_override_len; j++) + if (!random_override[j]) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + memcpy (frame + n, random_override, random_override_len); + n += random_override_len; + } + else + { + p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); + /* Replace zero bytes by new values. */ + for (;;) + { + int j, k; + unsigned char *pp; + + /* Count the zero bytes. */ + for (j=k=0; j < i; j++) + { + if (!p[j]) + k++; + } + if (!k) + break; /* Okay: no (more) zero bytes. */ + + k += k/128 + 3; /* Better get some more. */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for (j=0; j < i && k; ) + { + if (!p[j]) + p[j] = pp[--k]; + if (p[j]) + j++; + } + gcry_free (pp); + } + memcpy (frame+n, p, i); + n += i; + gcry_free (p); + } + + frame[n++] = 0; + memcpy (frame+n, value, valuelen); + n += valuelen; + gcry_assert (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 ("PKCS#1 block type 2 encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding. + NBITS is the size of the secret key. On success the result is + stored as a newly allocated buffer at R_RESULT and its valid length at + R_RESULTLEN. On error NULL is stored at R_RESULT. */ +static gcry_err_code_t +pkcs1_decode_for_encryption (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, gcry_mpi_t value) +{ + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + size_t n; + + *r_result = NULL; + + if ( !(frame = gcry_malloc_secure (nframe))) + return gpg_err_code_from_syserror (); + + err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &n, value); + if (err) + { + gcry_free (frame); + return gcry_err_code (err); + } + + nframe = n; /* Set NFRAME to the actual length. */ + + /* FRAME = 0x00 || 0x02 || PS || 0x00 || M + + pkcs#1 requires that the first byte is zero. Our MPIs usually + strip leading zero bytes; thus we are not able to detect them. + However due to the way gcry_mpi_print is implemented we may see + leading zero bytes nevertheless. We handle this by making the + first zero byte optional. */ + if (nframe < 4) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* Too short. */ + } + n = 0; + if (!frame[0]) + n++; + if (frame[n++] != 0x02) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* Wrong block type. */ + } + + /* Skip the non-zero random bytes and the terminating zero byte. */ + for (; n < nframe && frame[n] != 0x00; n++) + ; + if (n+1 >= nframe) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* No zero byte. */ + } + n++; /* Skip the zero byte. */ + + /* To avoid an extra allocation we reuse the frame buffer. The only + caller of this function will anyway free the result soon. */ + memmove (frame, frame + n, nframe - n); + *r_result = frame; + *r_resultlen = nframe - n; + + if (DBG_CIPHER) + log_printhex ("value extracted from PKCS#1 block type 2 encoded data:", + *r_result, *r_resultlen); + + return 0; +} + + +/* Encode {VALUE,VALUELEN} for an NBITS keys and hash algorith ALGO + using the pkcs#1 block type 1 padding. On success the result is + stored as a new MPI at R_RESULT. On error the value at R_RESULT is + undefined. + + We encode the value in this way: + + 0 1 PAD(n bytes) 0 ASN(asnlen bytes) VALUE(valuelen bytes) + + 0 is a marker we unfortunately can't encode because we return an + MPI which strips all leading zeroes. + 1 is the block type. + PAD consists of 0xff bytes. + 0 marks the end of the padding. + ASN is the DER encoding of the hash algorithm; along with the VALUE + it yields a valid DER encoding. + + (Note that PGP prior to version 2.3 encoded the message digest as: + 0 1 MD(16 bytes) 0 PAD(n bytes) 1 + The MD is always 16 bytes here because it's always MD5. GnuPG + does not not support pre-v2.3 signatures, but I'm including this + comment so the information is easily found if needed.) +*/ +static gcry_err_code_t +pkcs1_encode_for_signature (gcry_mpi_t *r_result, unsigned int nbits, + const unsigned char *value, size_t valuelen, + int algo) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + byte asn[100]; + byte *frame = NULL; + size_t nframe = (nbits+7) / 8; + int i; + size_t n; + size_t asnlen, dlen; + + asnlen = DIM(asn); + dlen = gcry_md_get_algo_dlen (algo); + + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + /* We don't have yet all of the above algorithms. */ + return GPG_ERR_NOT_IMPLEMENTED; + } + + if ( valuelen != dlen ) + { + /* Hash value does not match the length of digest for + the given algorithm. */ + return GPG_ERR_CONFLICT; + } + + if ( !dlen || dlen + asnlen + 4 > nframe) + { + /* Can't encode an DLEN byte digest MD into an NFRAME byte + frame. */ + return GPG_ERR_TOO_SHORT; + } + + if ( !(frame = gcry_malloc (nframe)) ) + return gpg_err_code_from_syserror (); + + /* Assemble the pkcs#1 block type 1. */ + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - valuelen - asnlen - 3 ; + gcry_assert (i > 1); + memset (frame+n, 0xff, i ); + n += i; + frame[n++] = 0; + memcpy (frame+n, asn, asnlen); + n += asnlen; + memcpy (frame+n, value, valuelen ); + n += valuelen; + gcry_assert (n == nframe); + + /* Convert it into an MPI. */ + 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 ("PKCS#1 block type 1 encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* Mask generation function for OAEP. See RFC-3447 B.2.1. */ +static gcry_err_code_t +mgf1 (unsigned char *output, size_t outlen, unsigned char *seed, size_t seedlen, + int algo) +{ + size_t dlen, nbytes, n; + int idx; + gcry_md_hd_t hd; + gcry_error_t err; + + err = gcry_md_open (&hd, algo, 0); + if (err) + return gpg_err_code (err); + + dlen = gcry_md_get_algo_dlen (algo); + + /* We skip step 1 which would be assert(OUTLEN <= 2^32). The loop + in step 3 is merged with step 4 by concatenating no more octets + than what would fit into OUTPUT. The ceiling for the counter IDX + is implemented indirectly. */ + nbytes = 0; /* Step 2. */ + idx = 0; + while ( nbytes < outlen ) + { + unsigned char c[4], *digest; + + if (idx) + gcry_md_reset (hd); + + c[0] = (idx >> 24) & 0xFF; + c[1] = (idx >> 16) & 0xFF; + c[2] = (idx >> 8) & 0xFF; + c[3] = idx & 0xFF; + idx++; + + gcry_md_write (hd, seed, seedlen); + gcry_md_write (hd, c, 4); + digest = gcry_md_read (hd, 0); + + n = (outlen - nbytes < dlen)? (outlen - nbytes) : dlen; + memcpy (output+nbytes, digest, n); + nbytes += n; + } + + gcry_md_close (hd); + return GPG_ERR_NO_ERROR; +} + + +/* RFC-3447 (pkcs#1 v2.1) OAEP encoding. NBITS is the length of the + key measured in bits. ALGO is the hash function; it must be a + valid and usable algorithm. {VALUE,VALUELEN} is the message to + encrypt. {LABEL,LABELLEN} is the optional label to be associated + with the message, if LABEL is NULL the default is to use the empty + string as label. On success the encoded ciphertext is returned at + R_RESULT. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the seed instead of using a random string for it. This feature is + only useful for regression tests. + + Here is figure 1 from the RFC depicting the process: + + +----------+---------+-------+ + DB = | lHash | PS | M | + +----------+---------+-------+ + | + +----------+ V + | seed |--> MGF ---> xor + +----------+ | + | | + +--+ V | + |00| xor <----- MGF <-----| + +--+ | | + | | | + V V V + +--+----------+----------------------------+ + EM = |00|maskedSeed| maskedDB | + +--+----------+----------------------------+ + */ +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, + const void *random_override, size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + unsigned char *p; + size_t hlen; + size_t n; + + *r_result = NULL; + + /* Set defaults for LABEL. */ + if (!label || !labellen) + { + label = (const unsigned char*)""; + labellen = 0; + } + + hlen = gcry_md_get_algo_dlen (algo); + + /* We skip step 1a which would be to check that LABELLEN is not + greater than 2^61-1. See rfc-3447 7.1.1. */ + + /* Step 1b. Note that the obsolete rfc-2437 uses the check: + valuelen > nframe - 2 * hlen - 1 . */ + if (valuelen > nframe - 2 * hlen - 2 || !nframe) + { + /* Can't encode a VALUELEN value in a NFRAME bytes frame. */ + return GPG_ERR_TOO_SHORT; /* The key is too short. */ + } + + /* Allocate the frame. */ + frame = gcry_calloc_secure (1, nframe); + if (!frame) + return gpg_err_code_from_syserror (); + + /* Step 2a: Compute the hash of the label. We store it in the frame + where later the maskedDB will commence. */ + gcry_md_hash_buffer (algo, frame + 1 + hlen, label, labellen); + + /* Step 2b: Set octet string to zero. */ + /* This has already been done while allocating FRAME. */ + + /* Step 2c: Create DB by concatenating lHash, PS, 0x01 and M. */ + n = nframe - valuelen - 1; + frame[n] = 0x01; + memcpy (frame + n + 1, value, valuelen); + + /* Step 3d: Generate seed. We store it where the maskedSeed will go + later. */ + if (random_override) + { + if (random_override_len != hlen) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + memcpy (frame + 1, random_override, hlen); + } + else + gcry_randomize (frame + 1, hlen, GCRY_STRONG_RANDOM); + + /* Step 2e and 2f: Create maskedDB. */ + { + unsigned char *dmask; + + dmask = gcry_malloc_secure (nframe - hlen - 1); + if (!dmask) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + return rc; + } + rc = mgf1 (dmask, nframe - hlen - 1, frame+1, hlen, algo); + if (rc) + { + gcry_free (dmask); + gcry_free (frame); + return rc; + } + for (n = 1 + hlen, p = dmask; n < nframe; n++) + frame[n] ^= *p++; + gcry_free (dmask); + } + + /* Step 2g and 2h: Create maskedSeed. */ + { + unsigned char *smask; + + smask = gcry_malloc_secure (hlen); + if (!smask) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + return rc; + } + rc = mgf1 (smask, hlen, frame + 1 + hlen, nframe - hlen - 1, algo); + if (rc) + { + gcry_free (smask); + gcry_free (frame); + return rc; + } + for (n = 1, p = smask; n < 1 + hlen; n++) + frame[n] ^= *p++; + gcry_free (smask); + } + + /* Step 2i: Concatenate 0x00, maskedSeed and maskedDB. */ + /* This has already been done by using in-place operations. */ + + /* Convert the stuff into an MPI as expected by the caller. */ + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, nframe, NULL); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("OAEP encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* RFC-3447 (pkcs#1 v2.1) OAEP decoding. NBITS is the length of the + key measured in bits. ALGO is the hash function; it must be a + valid and usable algorithm. VALUE is the raw decrypted message + {LABEL,LABELLEN} is the optional label to be associated with the + message, if LABEL is NULL the default is to use the empty string as + label. On success the plaintext is returned as a newly allocated + buffer at R_RESULT; its valid length is stored at R_RESULTLEN. On + error NULL is stored at R_RESULT. */ +static gcry_err_code_t +oaep_decode (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, int algo, + gcry_mpi_t value, const unsigned char *label, size_t labellen) +{ + gcry_err_code_t rc; + unsigned char *frame = NULL; /* Encoded messages (EM). */ + unsigned char *masked_seed; /* Points into FRAME. */ + unsigned char *masked_db; /* Points into FRAME. */ + unsigned char *seed = NULL; /* Allocated space for the seed and DB. */ + unsigned char *db; /* Points into SEED. */ + unsigned char *lhash = NULL; /* Hash of the label. */ + size_t nframe; /* Length of the ciphertext (EM). */ + size_t hlen; /* Length of the hash digest. */ + size_t db_len; /* Length of DB and masked_db. */ + size_t nkey = (nbits+7)/8; /* Length of the key in bytes. */ + int failed = 0; /* Error indicator. */ + size_t n; + + *r_result = NULL; + + /* This code is implemented as described by rfc-3447 7.1.2. */ + + /* Set defaults for LABEL. */ + if (!label || !labellen) + { + label = (const unsigned char*)""; + labellen = 0; + } + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + + /* Hash the label right away. */ + lhash = gcry_malloc (hlen); + if (!lhash) + return gpg_err_code_from_syserror (); + gcry_md_hash_buffer (algo, lhash, label, labellen); + + /* Turn the MPI into an octet string. If the octet string is + shorter than the key we pad it to the left with zeroes. This may + happen due to the leading zero in OAEP frames and due to the + following random octets (seed^mask) which may have leading zero + bytes. This all is needed to cope with our leading zeroes + suppressing MPI implementation. The code implictly implements + Step 1b (bail out if NFRAME != N). */ + rc = octet_string_from_mpi (&frame, NULL, value, nkey); + if (rc) + { + gcry_free (lhash); + return GPG_ERR_ENCODING_PROBLEM; + } + nframe = nkey; + + /* Step 1c: Check that the key is long enough. */ + if ( nframe < 2 * hlen + 2 ) + { + gcry_free (frame); + gcry_free (lhash); + return GPG_ERR_ENCODING_PROBLEM; + } + + /* Step 2 has already been done by the caller and the + gcry_mpi_aprint above. */ + + /* Allocate space for SEED and DB. */ + seed = gcry_malloc_secure (nframe - 1); + if (!seed) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + gcry_free (lhash); + return rc; + } + db = seed + hlen; + + /* To avoid choosen ciphertext attacks from now on we make sure to + run all code even in the error case; this avoids possible timing + attacks as described by Manger. */ + + /* Step 3a: Hash the label. */ + /* This has already been done. */ + + /* Step 3b: Separate the encoded message. */ + masked_seed = frame + 1; + masked_db = frame + 1 + hlen; + db_len = nframe - 1 - hlen; + + /* Step 3c and 3d: seed = maskedSeed ^ mgf(maskedDB, hlen). */ + if (mgf1 (seed, hlen, masked_db, db_len, algo)) + failed = 1; + for (n = 0; n < hlen; n++) + seed[n] ^= masked_seed[n]; + + /* Step 3e and 3f: db = maskedDB ^ mgf(seed, db_len). */ + if (mgf1 (db, db_len, seed, hlen, algo)) + failed = 1; + for (n = 0; n < db_len; n++) + db[n] ^= masked_db[n]; + + /* Step 3g: Check lhash, an possible empty padding string terminated + by 0x01 and the first byte of EM being 0. */ + if (memcmp (lhash, db, hlen)) + failed = 1; + for (n = hlen; n < db_len; n++) + if (db[n] == 0x01) + break; + if (n == db_len) + failed = 1; + if (frame[0]) + failed = 1; + + gcry_free (lhash); + gcry_free (frame); + if (failed) + { + gcry_free (seed); + return GPG_ERR_ENCODING_PROBLEM; + } + + /* Step 4: Output M. */ + /* To avoid an extra allocation we reuse the seed buffer. The only + caller of this function will anyway free the result soon. */ + n++; + memmove (seed, db + n, db_len - n); + *r_result = seed; + *r_resultlen = db_len - n; + seed = NULL; + + if (DBG_CIPHER) + log_printhex ("value extracted from OAEP encoded data:", + *r_result, *r_resultlen); + + return 0; +} + + +/* RFC-3447 (pkcs#1 v2.1) PSS encoding. Encode {VALUE,VALUELEN} for + an NBITS key. Note that VALUE is already the mHash from the + picture below. ALGO is a valid hash algorithm and SALTLEN is the + length of salt to be used. On success the result is stored as a + new MPI at R_RESULT. On error the value at R_RESULT is undefined. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the salt instead of using a random string for the salt. This + feature is only useful for regression tests. + + Here is figure 2 from the RFC (errata 595 applied) depicting the + process: + + +-----------+ + | M | + +-----------+ + | + V + Hash + | + V + +--------+----------+----------+ + M' = |Padding1| mHash | salt | + +--------+----------+----------+ + | + +--------+----------+ V + DB = |Padding2| salt | Hash + +--------+----------+ | + | | + V | +----+ + xor <--- MGF <---| |0xbc| + | | +----+ + | | | + V V V + +-------------------+----------+----+ + EM = | maskedDB | H |0xbc| + +-------------------+----------+----+ + + */ +static gcry_err_code_t +pss_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + const unsigned char *value, size_t valuelen, int saltlen, + const void *random_override, size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + size_t hlen; /* Length of the hash digest. */ + unsigned char *em = NULL; /* Encoded message. */ + size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */ + unsigned char *h; /* Points into EM. */ + unsigned char *buf = NULL; /* Help buffer. */ + size_t buflen; /* Length of BUF. */ + unsigned char *mhash; /* Points into BUF. */ + unsigned char *salt; /* Points into BUF. */ + unsigned char *dbmask; /* Points into BUF. */ + unsigned char *p; + size_t n; + + /* This code is implemented as described by rfc-3447 9.1.1. */ + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + + /* Allocate a help buffer and setup some pointers. */ + buflen = 8 + hlen + saltlen + (emlen - hlen - 1); + buf = gcry_malloc (buflen); + if (!buf) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + mhash = buf + 8; + salt = mhash + hlen; + dbmask= salt + saltlen; + + /* Step 2: That would be: mHash = Hash(M) but our input is already + mHash thus we do only a consistency check and copy to MHASH. */ + if (valuelen != hlen) + { + rc = GPG_ERR_INV_LENGTH; + goto leave; + } + memcpy (mhash, value, hlen); + + /* Step 3: Check length constraints. */ + if (emlen < hlen + saltlen + 2) + { + rc = GPG_ERR_TOO_SHORT; + goto leave; + } + + /* Allocate space for EM. */ + em = gcry_malloc (emlen); + if (!em) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + h = em + emlen - 1 - hlen; + + /* Step 4: Create a salt. */ + if (saltlen) + { + if (random_override) + { + if (random_override_len != saltlen) + { + rc = GPG_ERR_INV_ARG; + goto leave; + } + memcpy (salt, random_override, saltlen); + } + else + gcry_randomize (salt, saltlen, GCRY_STRONG_RANDOM); + } + + /* Step 5 and 6: M' = Hash(Padding1 || mHash || salt). */ + memset (buf, 0, 8); /* Padding. */ + gcry_md_hash_buffer (algo, h, buf, 8 + hlen + saltlen); + + /* Step 7 and 8: DB = PS || 0x01 || salt. */ + /* Note that we use EM to store DB and later Xor in-place. */ + p = em + emlen - 1 - hlen - saltlen - 1; + memset (em, 0, p - em); + *p++ = 0x01; + memcpy (p, salt, saltlen); + + /* Step 9: dbmask = MGF(H, emlen - hlen - 1). */ + mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo); + + /* Step 10: maskedDB = DB ^ dbMask */ + for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++) + em[n] ^= *p; + + /* Step 11: Set the leftmost bits to zero. */ + em[0] &= 0xFF >> (8 * emlen - nbits); + + /* Step 12: EM = maskedDB || H || 0xbc. */ + em[emlen-1] = 0xbc; + + /* Convert EM into an MPI. */ + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, em, emlen, NULL); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("PSS encoded data", *r_result); + + leave: + if (em) + { + wipememory (em, emlen); + gcry_free (em); + } + if (buf) + { + wipememory (buf, buflen); + gcry_free (buf); + } + return rc; +} + + +/* Verify a signature assuming PSS padding. VALUE is the hash of the + message (mHash) encoded as an MPI; its length must match the digest + length of ALGO. ENCODED is the output of the RSA public key + function (EM). NBITS is the size of the public key. ALGO is the + hash algorithm and SALTLEN is the length of the used salt. The + function returns 0 on success or on error code. */ +static gcry_err_code_t +pss_verify (gcry_mpi_t value, gcry_mpi_t encoded, unsigned int nbits, int algo, + size_t saltlen) +{ + gcry_err_code_t rc = 0; + size_t hlen; /* Length of the hash digest. */ + unsigned char *em = NULL; /* Encoded message. */ + size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */ + unsigned char *salt; /* Points into EM. */ + unsigned char *h; /* Points into EM. */ + unsigned char *buf = NULL; /* Help buffer. */ + size_t buflen; /* Length of BUF. */ + unsigned char *dbmask; /* Points into BUF. */ + unsigned char *mhash; /* Points into BUF. */ + unsigned char *p; + size_t n; + + /* This code is implemented as described by rfc-3447 9.1.2. */ + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + + /* Allocate a help buffer and setup some pointers. + This buffer is used for two purposes: + +------------------------------+-------+ + 1. | dbmask | mHash | + +------------------------------+-------+ + emlen - hlen - 1 hlen + + +----------+-------+---------+-+-------+ + 2. | padding1 | mHash | salt | | mHash | + +----------+-------+---------+-+-------+ + 8 hlen saltlen hlen + */ + buflen = 8 + hlen + saltlen; + if (buflen < emlen - hlen - 1) + buflen = emlen - hlen - 1; + buflen += hlen; + buf = gcry_malloc (buflen); + if (!buf) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + dbmask = buf; + mhash = buf + buflen - hlen; + + /* Step 2: That would be: mHash = Hash(M) but our input is already + mHash thus we only need to convert VALUE into MHASH. */ + rc = octet_string_from_mpi (NULL, mhash, value, hlen); + if (rc) + goto leave; + + /* Convert the signature into an octet string. */ + rc = octet_string_from_mpi (&em, NULL, encoded, emlen); + if (rc) + goto leave; + + /* Step 3: Check length of EM. Because we internally use MPI + functions we can't do this properly; EMLEN is always the length + of the key because octet_string_from_mpi needs to left pad the + result with zero to cope with the fact that our MPIs suppress all + leading zeroes. Thus what we test here are merely the digest and + salt lengths to the key. */ + if (emlen < hlen + saltlen + 2) + { + rc = GPG_ERR_TOO_SHORT; /* For the hash and saltlen. */ + goto leave; + } + + /* Step 4: Check last octet. */ + if (em[emlen - 1] != 0xbc) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 5: Split EM. */ + h = em + emlen - 1 - hlen; + + /* Step 6: Check the leftmost bits. */ + if ((em[0] & ~(0xFF >> (8 * emlen - nbits)))) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 7: dbmask = MGF(H, emlen - hlen - 1). */ + mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo); + + /* Step 8: maskedDB = DB ^ dbMask. */ + for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++) + em[n] ^= *p; + + /* Step 9: Set leftmost bits in DB to zero. */ + em[0] &= 0xFF >> (8 * emlen - nbits); + + /* Step 10: Check the padding of DB. */ + for (n = 0; n < emlen - hlen - saltlen - 2 && !em[n]; n++) + ; + if (n != emlen - hlen - saltlen - 2 || em[n++] != 1) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 11: Extract salt from DB. */ + salt = em + n; + + /* Step 12: M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */ + memset (buf, 0, 8); + memcpy (buf+8, mhash, hlen); + memcpy (buf+8+hlen, salt, saltlen); + + /* Step 13: H' = Hash(M'). */ + gcry_md_hash_buffer (algo, buf, buf, 8 + hlen + saltlen); + + /* Step 14: Check H == H'. */ + rc = memcmp (h, buf, hlen) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR; + + leave: + if (em) + { + wipememory (em, emlen); + gcry_free (em); + } + if (buf) + { + wipememory (buf, buflen); + gcry_free (buf); + } + return rc; +} + + +/* Callback for the pubkey algorithm code to verify PSS signatures. + OPAQUE is the data provided by the actual caller. The meaning of + TMP depends on the actual algorithm (but there is only RSA); now + for RSA it is the output of running the public key function on the + input. */ +static int +pss_verify_cmp (void *opaque, gcry_mpi_t tmp) +{ + struct pk_encoding_ctx *ctx = opaque; + gcry_mpi_t hash = ctx->verify_arg; + + return pss_verify (hash, tmp, ctx->nbits - 1, ctx->hash_algo, ctx->saltlen); +} + + +/* Internal function. */ +static gcry_err_code_t +sexp_elements_extract (gcry_sexp_t key_sexp, const char *element_names, + gcry_mpi_t *elements, const char *algo_name) +{ + gcry_err_code_t err = 0; + int i, idx; + const char *name; + gcry_sexp_t list; + + for (name = element_names, idx = 0; *name && !err; name++, idx++) + { + list = gcry_sexp_find_token (key_sexp, name, 1); + if (!list) + elements[idx] = NULL; + else + { + elements[idx] = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (list); + if (!elements[idx]) + err = GPG_ERR_INV_OBJ; + } + } + + if (!err) + { + /* Check that all elements are available. */ + for (name = element_names, idx = 0; *name; name++, idx++) + if (!elements[idx]) + break; + if (*name) + { + err = GPG_ERR_NO_OBJ; + /* Some are missing. Before bailing out we test for + optional parameters. */ + if (algo_name && !strcmp (algo_name, "RSA") + && !strcmp (element_names, "nedpqu") ) + { + /* This is RSA. Test whether we got N, E and D and that + the optional P, Q and U are all missing. */ + if (elements[0] && elements[1] && elements[2] + && !elements[3] && !elements[4] && !elements[5]) + err = 0; + } + } + } + + + if (err) + { + for (i = 0; i < idx; i++) + if (elements[i]) + gcry_free (elements[i]); + } + return err; +} + + +/* Internal function used for ecc. Note, that this function makes use + of its intimate knowledge about the ECC parameters from ecc.c. */ +static gcry_err_code_t +sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, + gcry_mpi_t *elements, pk_extra_spec_t *extraspec) + +{ + gcry_err_code_t err = 0; + int idx; + const char *name; + gcry_sexp_t list; + + /* Clear the array for easier error cleanup. */ + for (name = element_names, idx = 0; *name; name++, idx++) + elements[idx] = NULL; + gcry_assert (idx >= 5); /* We know that ECC has at least 5 elements + (params only) or 6 (full public key). */ + if (idx == 5) + elements[5] = NULL; /* Extra clear for the params only case. */ + + + /* Init the array with the available curve parameters. */ + for (name = element_names, idx = 0; *name && !err; name++, idx++) + { + list = gcry_sexp_find_token (key_sexp, name, 1); + if (!list) + elements[idx] = NULL; + else + { + elements[idx] = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (list); + if (!elements[idx]) + { + err = GPG_ERR_INV_OBJ; + goto leave; + } + } + } + + /* Check whether a curve parameter has been given and then fill any + missing elements. */ + list = gcry_sexp_find_token (key_sexp, "curve", 5); + if (list) + { + if (extraspec->get_param) + { + char *curve; + gcry_mpi_t params[6]; + + for (idx = 0; idx < DIM(params); idx++) + params[idx] = NULL; + + curve = _gcry_sexp_nth_string (list, 1); + gcry_sexp_release (list); + if (!curve) + { + /* No curve name given (or out of core). */ + err = GPG_ERR_INV_OBJ; + goto leave; + } + err = extraspec->get_param (curve, params); + gcry_free (curve); + if (err) + goto leave; + + for (idx = 0; idx < DIM(params); idx++) + { + if (!elements[idx]) + elements[idx] = params[idx]; + else + mpi_free (params[idx]); + } + } + else + { + gcry_sexp_release (list); + err = GPG_ERR_INV_OBJ; /* "curve" given but ECC not supported. */ + goto leave; + } + } + + /* Check that all parameters are known. */ + for (name = element_names, idx = 0; *name; name++, idx++) + if (!elements[idx]) + { + err = GPG_ERR_NO_OBJ; + goto leave; + } + + leave: + if (err) + { + for (name = element_names, idx = 0; *name; name++, idx++) + if (elements[idx]) + gcry_free (elements[idx]); + } + return err; +} + + + +/**************** + * Convert a S-Exp with either a private or a public key to our + * internal format. Currently we do only support the following + * algorithms: + * dsa + * rsa + * openpgp-dsa + * openpgp-rsa + * openpgp-elg + * openpgp-elg-sig + * ecdsa + * ecdh + * Provide a SE with the first element be either "private-key" or + * or "public-key". It is followed by a list with its first element + * be one of the above algorithm identifiers and the remaning + * elements are pairs with parameter-id and value. + * NOTE: we look through the list to find a list beginning with + * "private-key" or "public-key" - the first one found is used. + * + * If OVERRIDE_ELEMS is not NULL those elems override the parameter + * specification taken from the module. This ise used by + * gcry_pk_get_curve. + * + * Returns: A pointer to an allocated array of MPIs if the return value is + * zero; the caller has to release this array. + * + * Example of a DSA public key: + * (private-key + * (dsa + * (p ) + * (g ) + * (y ) + * (x ) + * ) + * ) + * The are expected to be in GCRYMPI_FMT_USG + */ +static gcry_err_code_t +sexp_to_key (gcry_sexp_t sexp, int want_private, const char *override_elems, + gcry_mpi_t **retarray, gcry_module_t *retalgo) +{ + gcry_err_code_t err = 0; + gcry_sexp_t list, l2; + char *name; + const char *elems; + gcry_mpi_t *array; + gcry_module_t module; + gcry_pk_spec_t *pubkey; + pk_extra_spec_t *extraspec; + int is_ecc; + + /* Check that the first element is valid. */ + list = gcry_sexp_find_token (sexp, + want_private? "private-key":"public-key", 0); + if (!list) + return GPG_ERR_INV_OBJ; /* Does not contain a key object. */ + + l2 = gcry_sexp_cadr( list ); + gcry_sexp_release ( list ); + list = l2; + name = _gcry_sexp_nth_string (list, 0); + if (!name) + { + gcry_sexp_release ( list ); + return GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = gcry_pk_lookup_name (name); + ath_mutex_unlock (&pubkeys_registered_lock); + + /* Fixme: We should make sure that an ECC key is always named "ecc" + and not "ecdsa". "ecdsa" should be used for the signature + itself. We need a function to test whether an algorithm given + with a key is compatible with an application of the key (signing, + encryption). For RSA this is easy, but ECC is the first + algorithm which has many flavours. */ + is_ecc = ( !strcmp (name, "ecdsa") + || !strcmp (name, "ecdh") + || !strcmp (name, "ecc") ); + gcry_free (name); + + if (!module) + { + gcry_sexp_release (list); + return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */ + } + else + { + pubkey = (gcry_pk_spec_t *) module->spec; + extraspec = module->extraspec; + } + + if (override_elems) + elems = override_elems; + else if (want_private) + elems = pubkey->elements_skey; + else + elems = pubkey->elements_pkey; + array = gcry_calloc (strlen (elems) + 1, sizeof (*array)); + if (!array) + err = gpg_err_code_from_syserror (); + if (!err) + { + if (is_ecc) + err = sexp_elements_extract_ecc (list, elems, array, extraspec); + else + err = sexp_elements_extract (list, elems, array, pubkey->name); + } + + gcry_sexp_release (list); + + if (err) + { + gcry_free (array); + + ath_mutex_lock (&pubkeys_registered_lock); + _gcry_module_release (module); + ath_mutex_unlock (&pubkeys_registered_lock); + } + else + { + *retarray = array; + *retalgo = module; + } + + return err; +} + + +static gcry_err_code_t +sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, + gcry_module_t *retalgo) +{ + gcry_err_code_t err = 0; + gcry_sexp_t list, l2; + char *name; + const char *elems; + gcry_mpi_t *array; + gcry_module_t module; + gcry_pk_spec_t *pubkey; + + /* Check that the first element is valid. */ + list = gcry_sexp_find_token( sexp, "sig-val" , 0 ); + if (!list) + return GPG_ERR_INV_OBJ; /* Does not contain a signature value object. */ + + l2 = gcry_sexp_nth (list, 1); + if (!l2) + { + gcry_sexp_release (list); + return GPG_ERR_NO_OBJ; /* No cadr for the sig object. */ + } + name = _gcry_sexp_nth_string (l2, 0); + if (!name) + { + gcry_sexp_release (list); + gcry_sexp_release (l2); + return GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + } + else if (!strcmp (name, "flags")) + { + /* Skip flags, since they are not used but here just for the + sake of consistent S-expressions. */ + gcry_free (name); + gcry_sexp_release (l2); + l2 = gcry_sexp_nth (list, 2); + if (!l2) + { + gcry_sexp_release (list); + return GPG_ERR_INV_OBJ; + } + name = _gcry_sexp_nth_string (l2, 0); + } + + ath_mutex_lock (&pubkeys_registered_lock); + module = gcry_pk_lookup_name (name); + ath_mutex_unlock (&pubkeys_registered_lock); + gcry_free (name); + name = NULL; + + if (!module) + { + gcry_sexp_release (l2); + gcry_sexp_release (list); + return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */ + } + else + pubkey = (gcry_pk_spec_t *) module->spec; + + elems = pubkey->elements_sig; + array = gcry_calloc (strlen (elems) + 1 , sizeof *array ); + if (!array) + err = gpg_err_code_from_syserror (); + + if (!err) + err = sexp_elements_extract (list, elems, array, NULL); + + gcry_sexp_release (l2); + gcry_sexp_release (list); + + if (err) + { + ath_mutex_lock (&pubkeys_registered_lock); + _gcry_module_release (module); + ath_mutex_unlock (&pubkeys_registered_lock); + + gcry_free (array); + } + else + { + *retarray = array; + *retalgo = module; + } + + 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 [raw, pkcs1, oaep, no-blinding])] + * [(hash-algo )] + * [(label