summaryrefslogtreecommitdiff
path: root/libgcrypt-1.4.6/cipher/pubkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgcrypt-1.4.6/cipher/pubkey.c')
-rw-r--r--libgcrypt-1.4.6/cipher/pubkey.c5498
1 files changed, 2749 insertions, 2749 deletions
diff --git a/libgcrypt-1.4.6/cipher/pubkey.c b/libgcrypt-1.4.6/cipher/pubkey.c
index 1aae8ea..08abcbf 100644
--- a/libgcrypt-1.4.6/cipher/pubkey.c
+++ b/libgcrypt-1.4.6/cipher/pubkey.c
@@ -1,2749 +1,2749 @@
-/* pubkey.c - pubkey dispatcher
- * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005,
- * 2007, 2008 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 <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#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 },
-#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;
-}
-
-
-/* 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 >= 6); /* We know that ECC has at least 6 elements. */
-
- /* 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
- * 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.
- *
- * 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 <mpi>)
- * (g <mpi>)
- * (y <mpi>)
- * (x <mpi>)
- * )
- * )
- * The <mpi> are expected to be in GCRYMPI_FMT_USG
- */
-static gcry_err_code_t
-sexp_to_key (gcry_sexp_t sexp, int want_private, 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, "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;
- }
-
- elems = want_private ? pubkey->elements_skey : pubkey->elements_pkey;
- array = gcry_calloc (strlen (elems) + 1, sizeof (*array));
- if (!array)
- err = gpg_err_code_from_errno (errno);
- 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_errno (errno);
-
- 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;
-}
-
-
-/****************
- * Take sexp and return an array of MPI as used for our internal decrypt
- * function.
- * s_data = (enc-val
- * [(flags [pkcs1])]
- * (<algo>
- * (<param_name1> <mpi>)
- * ...
- * (<param_namen> <mpi>)
- * ))
- * 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)
-{
- gcry_err_code_t err = 0;
- gcry_sexp_t list = NULL, l2 = NULL;
- gcry_pk_spec_t *pubkey = NULL;
- gcry_module_t module = NULL;
- char *name = NULL;
- size_t n;
- int parsed_flags = 0;
- const char *elems;
- gcry_mpi_t *array = NULL;
-
- *ret_want_pkcs1 = 0;
- *ret_modern = 0;
-
- /* Check that the first element is valid. */
- list = gcry_sexp_find_token (sexp, "enc-val" , 0);
- if (!list)
- {
- err = GPG_ERR_INV_OBJ; /* Does not contain an encrypted value object. */
- goto leave;
- }
-
- l2 = gcry_sexp_nth (list, 1);
- if (!l2)
- {
- err = GPG_ERR_NO_OBJ; /* No cdr for the data object. */
- goto leave;
- }
-
- /* Extract identifier of sublist. */
- name = _gcry_sexp_nth_string (l2, 0);
- if (!name)
- {
- err = GPG_ERR_INV_OBJ; /* Invalid structure of object. */
- goto leave;
- }
-
- if (!strcmp (name, "flags"))
- {
- /* There is a flags element - process it. */
- const char *s;
- int i;
-
- *ret_modern = 1;
- for (i = gcry_sexp_length (l2) - 1; i > 0; i--)
- {
- s = gcry_sexp_nth_data (l2, i, &n);
- if (! s)
- ; /* Not a data element - ignore. */
- 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;
- else if (n == 11 && ! memcmp (s, "no-blinding", 11))
- parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
- 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);
- if (!l2)
- {
- err = GPG_ERR_NO_OBJ; /* No cdr for the data object. */
- goto leave;
- }
-
- /* Extract sublist identifier. */
- gcry_free (name);
- name = _gcry_sexp_nth_string (l2, 0);
- if (!name)
- {
- err = GPG_ERR_INV_OBJ; /* Invalid structure of object. */
- goto leave;
- }
-
- gcry_sexp_release (list);
- list = l2;
- l2 = NULL;
- }
-
- ath_mutex_lock (&pubkeys_registered_lock);
- module = gcry_pk_lookup_name (name);
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- if (!module)
- {
- err = GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
- goto leave;
- }
- pubkey = (gcry_pk_spec_t *) module->spec;
-
- elems = pubkey->elements_enc;
- array = gcry_calloc (strlen (elems) + 1, sizeof (*array));
- if (!array)
- {
- err = gpg_err_code_from_errno (errno);
- goto leave;
- }
-
- err = sexp_elements_extract (list, elems, array, NULL);
-
- leave:
- gcry_sexp_release (list);
- gcry_sexp_release (l2);
- gcry_free (name);
-
- 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;
- *flags = parsed_flags;
- }
-
- return err;
-}
-
-/* Take the hash value and convert into an MPI, suitable for
- passing to the low level functions. We currently support the
- old style way of passing just a MPI and the modern interface which
- allows to pass flags so that we can choose between raw and pkcs1
- padding - may be more padding options later.
-
- (<mpi>)
- or
- (data
- [(flags [pkcs1])]
- [(hash <algo> <value>)]
- [(value <text>)]
- )
-
- Either the VALUE or the HASH element must be present for use
- with signatures. VALUE is used for encryption.
-
- 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)
-{
- 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 parsed_flags = 0, dummy_flags;
-
- if (! flags)
- flags = &dummy_flags;
-
- *ret_mpi = NULL;
- ldata = gcry_sexp_find_token (input, "data", 0);
- if (!ldata)
- { /* assume old style */
- *ret_mpi = gcry_sexp_nth_mpi (input, 0, 0);
- return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ;
- }
-
- /* see whether there is a flags object */
- {
- gcry_sexp_t lflags = gcry_sexp_find_token (ldata, "flags", 0);
- if (lflags)
- { /* parse the flags list. */
- for (i=gcry_sexp_length (lflags)-1; i > 0; i--)
- {
- s = gcry_sexp_nth_data (lflags, i, &n);
- if (!s)
- ; /* not a data element*/
- else if ( n == 3 && !memcmp (s, "raw", 3))
- is_raw = 1;
- else if ( n == 5 && !memcmp (s, "pkcs1", 5))
- is_pkcs1 = 1;
- else if (n == 11 && ! memcmp (s, "no-blinding", 11))
- parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
- else
- unknown_flag = 1;
- }
- gcry_sexp_release (lflags);
- }
- }
-
- if (!is_pkcs1 && !is_raw)
- is_raw = 1; /* default to raw */
-
- /* Get HASH or MPI */
- lhash = gcry_sexp_find_token (ldata, "hash", 0);
- lvalue = lhash? NULL : gcry_sexp_find_token (ldata, "value", 0);
-
- if (!(!lhash ^ !lvalue))
- 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)
- {
- *ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, 0);
- if (!*ret_mpi)
- rc = GPG_ERR_INV_OBJ;
- }
- else if (is_pkcs1 && lvalue && for_encryption)
- {
- /* Create pkcs#1 block type 2 padding. */
- unsigned char *frame = NULL;
- size_t nframe = (nbits+7) / 8;
- const void * value;
- size_t valuelen;
- unsigned char *p;
-
- if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen )
- rc = GPG_ERR_INV_OBJ;
- else if (valuelen + 7 > nframe || !nframe)
- {
- /* Can't encode a VALUELEN value in a NFRAME bytes frame. */
- rc = GPG_ERR_TOO_SHORT; /* the key is too short */
- }
- else if ( !(frame = gcry_malloc_secure (nframe)))
- rc = gpg_err_code_from_errno (errno);
- else
- {
- n = 0;
- frame[n++] = 0;
- frame[n++] = 2; /* block type */
- i = nframe - 3 - valuelen;
- gcry_assert (i > 0);
- 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);
-
- /* FIXME, error checking? */
- gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, frame, n, &nframe);
- }
-
- gcry_free(frame);
- }
- else if (is_pkcs1 && lhash && !for_encryption)
- {
- /* Create pkcs#1 block type 1 padding. */
- if (gcry_sexp_length (lhash) != 3)
- rc = GPG_ERR_INV_OBJ;
- else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n )
- 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;
- size_t nframe = (nbits+7) / 8;
- const void * value;
- 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);
- }
- }
-
- asnlen = DIM(asn);
- dlen = gcry_md_get_algo_dlen (algo);
-
- if (!algo)
- rc = GPG_ERR_DIGEST_ALGO;
- else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
- || !valuelen )
- rc = GPG_ERR_INV_OBJ;
- else if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
- {
- /* We don't have yet all of the above algorithms. */
- rc = GPG_ERR_NOT_IMPLEMENTED;
- }
- else if ( valuelen != dlen )
- {
- /* Hash value does not match the length of digest for
- the given algorithm. */
- rc = GPG_ERR_CONFLICT;
- }
- else if( !dlen || dlen + asnlen + 4 > nframe)
- {
- /* Can't encode an DLEN byte digest MD into a NFRAME
- byte frame. */
- rc = GPG_ERR_TOO_SHORT;
- }
- else if ( !(frame = gcry_malloc (nframe)) )
- rc = gpg_err_code_from_errno (errno);
- else
- { /* 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. FIXME: error checking? */
- gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, frame, n, &nframe);
- }
-
- gcry_free (frame);
- }
- }
- else
- rc = GPG_ERR_CONFLICT;
-
- gcry_sexp_release (ldata);
- gcry_sexp_release (lhash);
- gcry_sexp_release (lvalue);
-
- if (!rc)
- *flags = parsed_flags;
-
- return rc;
-}
-
-
-/*
- Do a PK encrypt operation
-
- Caller has to provide a public key as the SEXP pkey and data as a
- SEXP with just one MPI in it. Alternatively S_DATA might be a
- complex S-Expression, similar to the one used for signature
- verification. This provides a flag which allows to handle PKCS#1
- block type 2 padding. The function returns a a sexp which may be
- passed to to pk_decrypt.
-
- Returns: 0 or an errorcode.
-
- s_data = See comment for sexp_data_to_mpi
- s_pkey = <key-as-defined-in-sexp_to_key>
- r_ciph = (enc-val
- (<algo>
- (<param_name1> <mpi>)
- ...
- (<param_namen> <mpi>)
- ))
-
-*/
-gcry_error_t
-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;
- gcry_err_code_t rc;
- gcry_pk_spec_t *pubkey = NULL;
- gcry_module_t module = NULL;
-
- *r_ciph = NULL;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- /* Get the key. */
- rc = sexp_to_key (s_pkey, 0, &pkey, &module);
- if (rc)
- goto leave;
-
- gcry_assert (module);
- pubkey = (gcry_pk_spec_t *) module->spec;
-
- /* If aliases for the algorithm name exists, take the first one
- instead of the regular name to adhere to SPKI conventions. We
- assume that the first alias name is the lowercase version of the
- regular one. This change is required for compatibility with
- 1.1.12 generated S-expressions. */
- algo_name = pubkey->aliases? *pubkey->aliases : NULL;
- if (!algo_name || !*algo_name)
- algo_name = pubkey->name;
-
- algo_elems = pubkey->elements_enc;
-
- /* Get the stuff we want to encrypt. */
- rc = sexp_data_to_mpi (s_data, gcry_pk_get_nbits (s_pkey), &data, 1,
- &flags);
- if (rc)
- goto leave;
-
- /* Now we can encrypt DATA to CIPH. */
- ciph = gcry_calloc (strlen (algo_elems) + 1, sizeof (*ciph));
- if (!ciph)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- rc = pubkey_encrypt (module->mod_id, ciph, data, pkey, flags);
- mpi_free (data);
- data = NULL;
- if (rc)
- goto leave;
-
- /* We did it. Now build the return list */
- {
- char *string, *p;
- int i;
- size_t nelem = strlen (algo_elems);
- size_t needed = 19 + strlen (algo_name) + (nelem * 5);
- void **arg_list;
-
- /* Build the string. */
- string = p = gcry_malloc (needed);
- if (!string)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- p = stpcpy ( p, "(enc-val(" );
- p = stpcpy ( p, algo_name );
- for (i=0; algo_elems[i]; i++ )
- {
- *p++ = '(';
- *p++ = algo_elems[i];
- p = stpcpy ( p, "%m)" );
- }
- strcpy ( p, "))" );
-
- /* And now the ugly part: We don't have a function to pass an
- * array to a format string, so we have to do it this way :-(. */
- /* FIXME: There is now such a format specifier, so we can
- change the code to be more clear. */
- arg_list = malloc (nelem * sizeof *arg_list);
- if (!arg_list)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
-
- for (i = 0; i < nelem; i++)
- arg_list[i] = ciph + i;
-
- rc = gcry_sexp_build_array (r_ciph, NULL, string, arg_list);
- free (arg_list);
- if (rc)
- BUG ();
- gcry_free (string);
- }
-
- leave:
- if (pkey)
- {
- release_mpi_array (pkey);
- gcry_free (pkey);
- }
-
- if (ciph)
- {
- release_mpi_array (ciph);
- gcry_free (ciph);
- }
-
- if (module)
- {
- ath_mutex_lock (&pubkeys_registered_lock);
- _gcry_module_release (module);
- ath_mutex_unlock (&pubkeys_registered_lock);
- }
-
- return gcry_error (rc);
-}
-
-/*
- Do a PK decrypt operation
-
- Caller has to provide a secret key as the SEXP skey and data in a
- format as created by gcry_pk_encrypt. For historic reasons the
- function returns simply an MPI as an S-expression part; this is
- deprecated and the new method should be used which returns a real
- S-expressionl this is selected by adding at least an empty flags
- list to S_DATA.
-
- Returns: 0 or an errorcode.
-
- s_data = (enc-val
- [(flags)]
- (<algo>
- (<param_name1> <mpi>)
- ...
- (<param_namen> <mpi>)
- ))
- s_skey = <key-as-defined-in-sexp_to_key>
- r_plain= Either an incomplete S-expression without the parentheses
- or if the flags list is used (even if empty) a real S-expression:
- (value PLAIN).
- */
-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_err_code_t rc;
- gcry_module_t module_enc = NULL, module_key = NULL;
- gcry_pk_spec_t *pubkey = NULL;
-
- *r_plain = NULL;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- rc = sexp_to_key (s_skey, 1, &skey, &module_key);
- if (rc)
- goto leave;
-
- rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &want_pkcs1, &flags);
- if (rc)
- goto leave;
-
- if (module_key->mod_id != module_enc->mod_id)
- {
- rc = GPG_ERR_CONFLICT; /* Key algo does not match data algo. */
- goto leave;
- }
-
- pubkey = (gcry_pk_spec_t *) module_key->spec;
-
- rc = pubkey_decrypt (module_key->mod_id, &plain, data, skey, flags);
- if (rc)
- goto leave;
-
- if (gcry_sexp_build (r_plain, NULL, modern? "(value %m)" : "%m", plain))
- BUG ();
-
- leave:
- if (skey)
- {
- release_mpi_array (skey);
- gcry_free (skey);
- }
-
- if (plain)
- mpi_free (plain);
-
- if (data)
- {
- release_mpi_array (data);
- gcry_free (data);
- }
-
- if (module_key || module_enc)
- {
- ath_mutex_lock (&pubkeys_registered_lock);
- if (module_key)
- _gcry_module_release (module_key);
- if (module_enc)
- _gcry_module_release (module_enc);
- ath_mutex_unlock (&pubkeys_registered_lock);
- }
-
- return gcry_error (rc);
-}
-
-
-
-/*
- Create a signature.
-
- Caller has to provide a secret key as the SEXP skey and data
- expressed as a SEXP list hash with only one element which should
- instantly be available as a MPI. Alternatively the structure given
- below may be used for S_HASH, it provides the abiliy to pass flags
- to the operation; the only flag defined by now is "pkcs1" which
- does PKCS#1 block type 1 style padding.
-
- Returns: 0 or an errorcode.
- In case of 0 the function returns a new SEXP with the
- signature value; the structure of this signature depends on the
- other arguments but is always suitable to be passed to
- gcry_pk_verify
-
- s_hash = See comment for sexp_data_to_mpi
-
- s_skey = <key-as-defined-in-sexp_to_key>
- r_sig = (sig-val
- (<algo>
- (<param_name1> <mpi>)
- ...
- (<param_namen> <mpi>))
- [(hash algo)])
-
- Note that (hash algo) in R_SIG is not used.
-*/
-gcry_error_t
-gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
-{
- gcry_mpi_t *skey = NULL, hash = NULL, *result = NULL;
- gcry_pk_spec_t *pubkey = NULL;
- gcry_module_t module = NULL;
- const char *algo_name, *algo_elems;
- int i;
- gcry_err_code_t rc;
-
- *r_sig = NULL;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- rc = sexp_to_key (s_skey, 1, &skey, &module);
- if (rc)
- goto leave;
-
- gcry_assert (module);
- pubkey = (gcry_pk_spec_t *) module->spec;
- algo_name = pubkey->aliases? *pubkey->aliases : NULL;
- if (!algo_name || !*algo_name)
- algo_name = pubkey->name;
-
- algo_elems = pubkey->elements_sig;
-
- /* 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);
- if (rc)
- goto leave;
-
- result = gcry_calloc (strlen (algo_elems) + 1, sizeof (*result));
- if (!result)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- rc = pubkey_sign (module->mod_id, result, hash, skey);
- if (rc)
- goto leave;
-
- {
- char *string, *p;
- size_t nelem, needed = strlen (algo_name) + 20;
- void **arg_list;
-
- nelem = strlen (algo_elems);
-
- /* Count elements, so that we can allocate enough space. */
- needed += 10 * nelem;
-
- /* Build the string. */
- string = p = gcry_malloc (needed);
- if (!string)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- p = stpcpy (p, "(sig-val(");
- p = stpcpy (p, algo_name);
- for (i = 0; algo_elems[i]; i++)
- {
- *p++ = '(';
- *p++ = algo_elems[i];
- p = stpcpy (p, "%m)");
- }
- strcpy (p, "))");
-
- arg_list = malloc (nelem * sizeof *arg_list);
- if (!arg_list)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
-
- for (i = 0; i < nelem; i++)
- arg_list[i] = result + i;
-
- rc = gcry_sexp_build_array (r_sig, NULL, string, arg_list);
- free (arg_list);
- if (rc)
- BUG ();
- gcry_free (string);
- }
-
- leave:
- if (skey)
- {
- release_mpi_array (skey);
- gcry_free (skey);
- }
-
- if (hash)
- mpi_free (hash);
-
- if (result)
- {
- release_mpi_array (result);
- gcry_free (result);
- }
-
- return gcry_error (rc);
-}
-
-
-/*
- Verify a signature.
-
- Caller has to supply the public key pkey, the signature sig and his
- hashvalue data. Public key has to be a standard public key given
- as an S-Exp, sig is a S-Exp as returned from gcry_pk_sign and data
- must be an S-Exp like the one in sign too. */
-gcry_error_t
-gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
-{
- gcry_module_t module_key = NULL, module_sig = NULL;
- gcry_mpi_t *pkey = NULL, hash = NULL, *sig = NULL;
- gcry_err_code_t rc;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- rc = sexp_to_key (s_pkey, 0, &pkey, &module_key);
- if (rc)
- goto leave;
-
- rc = sexp_to_sig (s_sig, &sig, &module_sig);
- if (rc)
- goto leave;
-
- /* Fixme: Check that the algorithm of S_SIG is compatible to the one
- of S_PKEY. */
-
- if (module_key->mod_id != module_sig->mod_id)
- {
- rc = GPG_ERR_CONFLICT;
- goto leave;
- }
-
- rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_pkey), &hash, 0, 0);
- if (rc)
- goto leave;
-
- rc = pubkey_verify (module_key->mod_id, hash, sig, pkey, NULL, NULL);
-
- leave:
- if (pkey)
- {
- release_mpi_array (pkey);
- gcry_free (pkey);
- }
- if (sig)
- {
- release_mpi_array (sig);
- gcry_free (sig);
- }
- if (hash)
- mpi_free (hash);
-
- if (module_key || module_sig)
- {
- ath_mutex_lock (&pubkeys_registered_lock);
- if (module_key)
- _gcry_module_release (module_key);
- if (module_sig)
- _gcry_module_release (module_sig);
- ath_mutex_unlock (&pubkeys_registered_lock);
- }
-
- return gcry_error (rc);
-}
-
-
-/*
- Test a key.
-
- This may be used either for a public or a secret key to see whether
- the internal structure is okay.
-
- Returns: 0 or an errorcode.
-
- s_key = <key-as-defined-in-sexp_to_key> */
-gcry_error_t
-gcry_pk_testkey (gcry_sexp_t s_key)
-{
- gcry_module_t module = NULL;
- gcry_mpi_t *key = NULL;
- gcry_err_code_t rc;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- /* Note we currently support only secret key checking. */
- rc = sexp_to_key (s_key, 1, &key, &module);
- if (! rc)
- {
- rc = pubkey_check_secret_key (module->mod_id, key);
- release_mpi_array (key);
- gcry_free (key);
- }
- return gcry_error (rc);
-}
-
-
-/*
- Create a public key pair and return it in r_key.
- How the key is created depends on s_parms:
- (genkey
- (algo
- (parameter_name_1 ....)
- ....
- (parameter_name_n ....)
- ))
- The key is returned in a format depending on the
- algorithm. Both, private and secret keys are returned
- and optionally some additional informatin.
- For elgamal we return this structure:
- (key-data
- (public-key
- (elg
- (p <mpi>)
- (g <mpi>)
- (y <mpi>)
- )
- )
- (private-key
- (elg
- (p <mpi>)
- (g <mpi>)
- (y <mpi>)
- (x <mpi>)
- )
- )
- (misc-key-info
- (pm1-factors n1 n2 ... nn)
- ))
- */
-gcry_error_t
-gcry_pk_genkey (gcry_sexp_t *r_key, gcry_sexp_t s_parms)
-{
- gcry_pk_spec_t *pubkey = NULL;
- gcry_module_t module = NULL;
- gcry_sexp_t list = NULL;
- gcry_sexp_t l2 = NULL;
- gcry_sexp_t l3 = NULL;
- char *name = NULL;
- size_t n;
- gcry_err_code_t rc = GPG_ERR_NO_ERROR;
- int i;
- const char *algo_name = NULL;
- int algo;
- const char *sec_elems = NULL, *pub_elems = NULL;
- gcry_mpi_t skey[12];
- gcry_mpi_t *factors = NULL;
- gcry_sexp_t extrainfo = NULL;
- unsigned int nbits = 0;
- unsigned long use_e = 0;
-
- skey[0] = NULL;
- *r_key = NULL;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- list = gcry_sexp_find_token (s_parms, "genkey", 0);
- if (!list)
- {
- rc = GPG_ERR_INV_OBJ; /* Does not contain genkey data. */
- goto leave;
- }
-
- l2 = gcry_sexp_cadr (list);
- gcry_sexp_release (list);
- list = l2;
- l2 = NULL;
- if (! list)
- {
- rc = GPG_ERR_NO_OBJ; /* No cdr for the genkey. */
- goto leave;
- }
-
- name = _gcry_sexp_nth_string (list, 0);
- if (!name)
- {
- rc = GPG_ERR_INV_OBJ; /* Algo string missing. */
- goto leave;
- }
-
- 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)
- {
- rc = GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
- goto leave;
- }
-
- pubkey = (gcry_pk_spec_t *) module->spec;
- algo = module->mod_id;
- algo_name = pubkey->aliases? *pubkey->aliases : NULL;
- if (!algo_name || !*algo_name)
- algo_name = pubkey->name;
- pub_elems = pubkey->elements_pkey;
- sec_elems = pubkey->elements_skey;
- if (strlen (sec_elems) >= DIM(skey))
- BUG ();
-
- /* Handle the optional rsa-use-e element. Actually this belong into
- the algorithm module but we have this parameter in the public
- module API, so we need to parse it right here. */
- l2 = gcry_sexp_find_token (list, "rsa-use-e", 0);
- if (l2)
- {
- char buf[50];
- const char *s;
-
- s = gcry_sexp_nth_data (l2, 1, &n);
- if ( !s || n >= DIM (buf) - 1 )
- {
- rc = GPG_ERR_INV_OBJ; /* No value or value too large. */
- goto leave;
- }
- memcpy (buf, s, n);
- buf[n] = 0;
- use_e = strtoul (buf, NULL, 0);
- gcry_sexp_release (l2);
- l2 = NULL;
- }
- else
- use_e = 65537; /* Not given, use the value generated by old versions. */
-
-
- /* Get the "nbits" parameter. */
- l2 = gcry_sexp_find_token (list, "nbits", 0);
- if (l2)
- {
- char buf[50];
- const char *s;
-
- s = gcry_sexp_nth_data (l2, 1, &n);
- if (!s || n >= DIM (buf) - 1 )
- {
- rc = GPG_ERR_INV_OBJ; /* NBITS given without a cdr. */
- goto leave;
- }
- memcpy (buf, s, n);
- buf[n] = 0;
- nbits = (unsigned int)strtoul (buf, NULL, 0);
- gcry_sexp_release (l2); l2 = NULL;
- }
- else
- nbits = 0;
-
- /* Pass control to the algorithm module. */
- rc = pubkey_generate (module->mod_id, nbits, use_e, list, skey,
- &factors, &extrainfo);
- gcry_sexp_release (list); list = NULL;
- if (rc)
- goto leave;
-
- /* Key generation succeeded: Build an S-expression. */
- {
- char *string, *p;
- size_t nelem=0, nelem_cp = 0, needed=0;
- gcry_mpi_t mpis[30];
-
- /* Estimate size of format string. */
- nelem = strlen (pub_elems) + strlen (sec_elems);
- if (factors)
- {
- for (i = 0; factors[i]; i++)
- nelem++;
- }
- nelem_cp = nelem;
-
- needed += nelem * 10;
- /* (+5 is for EXTRAINFO ("%S")). */
- needed += 2 * strlen (algo_name) + 300 + 5;
- if (nelem > DIM (mpis))
- BUG ();
-
- /* Build the string. */
- nelem = 0;
- string = p = gcry_malloc (needed);
- if (!string)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- p = stpcpy (p, "(key-data");
- p = stpcpy (p, "(public-key(");
- p = stpcpy (p, algo_name);
- for(i = 0; pub_elems[i]; i++)
- {
- *p++ = '(';
- *p++ = pub_elems[i];
- p = stpcpy (p, "%m)");
- mpis[nelem++] = skey[i];
- }
- p = stpcpy (p, "))");
- p = stpcpy (p, "(private-key(");
- p = stpcpy (p, algo_name);
- for (i = 0; sec_elems[i]; i++)
- {
- *p++ = '(';
- *p++ = sec_elems[i];
- p = stpcpy (p, "%m)");
- mpis[nelem++] = skey[i];
- }
- p = stpcpy (p, "))");
-
- /* Hack to make release_mpi_array() work. */
- skey[i] = NULL;
-
- if (extrainfo)
- {
- /* If we have extrainfo we should not have any factors. */
- p = stpcpy (p, "%S");
- }
- else if (factors && factors[0])
- {
- p = stpcpy (p, "(misc-key-info(pm1-factors");
- for(i = 0; factors[i]; i++)
- {
- p = stpcpy (p, "%m");
- mpis[nelem++] = factors[i];
- }
- p = stpcpy (p, "))");
- }
- strcpy (p, ")");
- gcry_assert (p - string < needed);
-
- while (nelem < DIM (mpis))
- mpis[nelem++] = NULL;
-
- {
- int elem_n = strlen (pub_elems) + strlen (sec_elems);
- void **arg_list;
-
- /* Allocate one extra for EXTRAINFO ("%S"). */
- arg_list = gcry_calloc (nelem_cp+1, sizeof *arg_list);
- if (!arg_list)
- {
- rc = gpg_err_code_from_errno (errno);
- goto leave;
- }
- for (i = 0; i < elem_n; i++)
- arg_list[i] = mpis + i;
- if (extrainfo)
- arg_list[i] = &extrainfo;
- else if (factors && factors[0])
- {
- for (; i < nelem_cp; i++)
- arg_list[i] = factors + i - elem_n;
- }
-
- rc = gcry_sexp_build_array (r_key, NULL, string, arg_list);
- gcry_free (arg_list);
- if (rc)
- BUG ();
- gcry_assert (DIM (mpis) == 30); /* Reminder to make sure that
- the array gets increased if
- new parameters are added. */
- }
- gcry_free (string);
- }
-
- leave:
- gcry_free (name);
- gcry_sexp_release (extrainfo);
- release_mpi_array (skey);
- /* Don't free SKEY itself, it is an stack allocated array. */
-
- if (factors)
- {
- release_mpi_array ( factors );
- gcry_free (factors);
- }
-
- gcry_sexp_release (l3);
- gcry_sexp_release (l2);
- gcry_sexp_release (list);
-
- if (module)
- {
- ath_mutex_lock (&pubkeys_registered_lock);
- _gcry_module_release (module);
- ath_mutex_unlock (&pubkeys_registered_lock);
- }
-
- return gcry_error (rc);
-}
-
-
-/*
- Get the number of nbits from the public key.
-
- Hmmm: Should we have really this function or is it better to have a
- more general function to retrieve different properties of the key? */
-unsigned int
-gcry_pk_get_nbits (gcry_sexp_t key)
-{
- gcry_module_t module = NULL;
- gcry_pk_spec_t *pubkey;
- gcry_mpi_t *keyarr = NULL;
- unsigned int nbits = 0;
- gcry_err_code_t rc;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- rc = sexp_to_key (key, 0, &keyarr, &module);
- if (rc == GPG_ERR_INV_OBJ)
- rc = sexp_to_key (key, 1, &keyarr, &module);
- if (rc)
- return 0; /* Error - 0 is a suitable indication for that. */
-
- pubkey = (gcry_pk_spec_t *) module->spec;
- nbits = (*pubkey->get_nbits) (module->mod_id, keyarr);
-
- ath_mutex_lock (&pubkeys_registered_lock);
- _gcry_module_release (module);
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- release_mpi_array (keyarr);
- gcry_free (keyarr);
-
- return nbits;
-}
-
-
-/* Return the so called KEYGRIP which is the SHA-1 hash of the public
- key parameters expressed in a way depending on the algorithm.
-
- ARRAY must either be 20 bytes long or NULL; in the latter case a
- newly allocated array of that size is returned, otherwise ARRAY or
- NULL is returned to indicate an error which is most likely an
- unknown algorithm. The function accepts public or secret keys. */
-unsigned char *
-gcry_pk_get_keygrip (gcry_sexp_t key, unsigned char *array)
-{
- gcry_sexp_t list = NULL, l2 = NULL;
- gcry_pk_spec_t *pubkey = NULL;
- gcry_module_t module = NULL;
- pk_extra_spec_t *extraspec;
- const char *s;
- char *name = NULL;
- int idx;
- const char *elems;
- gcry_md_hd_t md = NULL;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- /* Check that the first element is valid. */
- list = gcry_sexp_find_token (key, "public-key", 0);
- if (! list)
- list = gcry_sexp_find_token (key, "private-key", 0);
- if (! list)
- list = gcry_sexp_find_token (key, "protected-private-key", 0);
- if (! list)
- list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
- if (! list)
- return NULL; /* No public- or private-key object. */
-
- l2 = gcry_sexp_cadr (list);
- gcry_sexp_release (list);
- list = l2;
- l2 = NULL;
-
- name = _gcry_sexp_nth_string (list, 0);
- if (!name)
- goto fail; /* Invalid structure of object. */
-
- ath_mutex_lock (&pubkeys_registered_lock);
- module = gcry_pk_lookup_name (name);
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- if (!module)
- goto fail; /* Unknown algorithm. */
-
- pubkey = (gcry_pk_spec_t *) module->spec;
- extraspec = module->extraspec;
-
- elems = pubkey->elements_grip;
- if (!elems)
- goto fail; /* No grip parameter. */
-
- if (gcry_md_open (&md, GCRY_MD_SHA1, 0))
- goto fail;
-
- if (extraspec && extraspec->comp_keygrip)
- {
- /* Module specific method to compute a keygrip. */
- if (extraspec->comp_keygrip (md, list))
- goto fail;
- }
- else
- {
- /* Generic method to compute a keygrip. */
- for (idx = 0, s = elems; *s; s++, idx++)
- {
- const char *data;
- size_t datalen;
- char buf[30];
-
- l2 = gcry_sexp_find_token (list, s, 1);
- if (! l2)
- goto fail;
- data = gcry_sexp_nth_data (l2, 1, &datalen);
- if (! data)
- goto fail;
-
- snprintf (buf, sizeof buf, "(1:%c%u:", *s, (unsigned int)datalen);
- gcry_md_write (md, buf, strlen (buf));
- gcry_md_write (md, data, datalen);
- gcry_sexp_release (l2);
- gcry_md_write (md, ")", 1);
- }
- }
-
- if (!array)
- {
- array = gcry_malloc (20);
- if (! array)
- goto fail;
- }
-
- memcpy (array, gcry_md_read (md, GCRY_MD_SHA1), 20);
- gcry_md_close (md);
- gcry_sexp_release (list);
- return array;
-
- fail:
- gcry_free (name);
- gcry_sexp_release (l2);
- gcry_md_close (md);
- gcry_sexp_release (list);
- return NULL;
-}
-
-
-gcry_error_t
-gcry_pk_ctl (int cmd, void *buffer, size_t buflen)
-{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- switch (cmd)
- {
- case GCRYCTL_DISABLE_ALGO:
- /* This one expects a buffer pointing to an integer with the
- algo number. */
- if ((! buffer) || (buflen != sizeof (int)))
- err = GPG_ERR_INV_ARG;
- else
- disable_pubkey_algo (*((int *) buffer));
- break;
-
- default:
- err = GPG_ERR_INV_OP;
- }
-
- return gcry_error (err);
-}
-
-
-/* Return information about the given algorithm
-
- WHAT selects the kind of information returned:
-
- GCRYCTL_TEST_ALGO:
- Returns 0 when the specified algorithm is available for use.
- Buffer must be NULL, nbytes may have the address of a variable
- with the required usage of the algorithm. It may be 0 for don't
- care or a combination of the GCRY_PK_USAGE_xxx flags;
-
- GCRYCTL_GET_ALGO_USAGE:
- Return the usage glafs for the give algo. An invalid alog
- does return 0. Disabled algos are ignored here because we
- only want to know whether the algo is at all capable of
- the usage.
-
- Note: Because this function is in most cases used to return an
- integer value, we can make it easier for the caller to just look at
- the return value. The caller will in all cases consult the value
- and thereby detecting whether a error occurred or not (i.e. while
- checking the block size) */
-gcry_error_t
-gcry_pk_algo_info (int algorithm, int what, void *buffer, size_t *nbytes)
-{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
-
- switch (what)
- {
- case GCRYCTL_TEST_ALGO:
- {
- int use = nbytes ? *nbytes : 0;
- if (buffer)
- err = GPG_ERR_INV_ARG;
- else if (check_pubkey_algo (algorithm, use))
- err = GPG_ERR_PUBKEY_ALGO;
- break;
- }
-
- case GCRYCTL_GET_ALGO_USAGE:
- {
- gcry_module_t pubkey;
- int use = 0;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- ath_mutex_lock (&pubkeys_registered_lock);
- pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
- if (pubkey)
- {
- use = ((gcry_pk_spec_t *) pubkey->spec)->use;
- _gcry_module_release (pubkey);
- }
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- /* FIXME? */
- *nbytes = use;
-
- break;
- }
-
- case GCRYCTL_GET_ALGO_NPKEY:
- {
- /* FIXME? */
- int npkey = pubkey_get_npkey (algorithm);
- *nbytes = npkey;
- break;
- }
- case GCRYCTL_GET_ALGO_NSKEY:
- {
- /* FIXME? */
- int nskey = pubkey_get_nskey (algorithm);
- *nbytes = nskey;
- break;
- }
- case GCRYCTL_GET_ALGO_NSIGN:
- {
- /* FIXME? */
- int nsign = pubkey_get_nsig (algorithm);
- *nbytes = nsign;
- break;
- }
- case GCRYCTL_GET_ALGO_NENCR:
- {
- /* FIXME? */
- int nencr = pubkey_get_nenc (algorithm);
- *nbytes = nencr;
- break;
- }
-
- default:
- err = GPG_ERR_INV_OP;
- }
-
- return gcry_error (err);
-}
-
-
-/* Explicitly initialize this module. */
-gcry_err_code_t
-_gcry_pk_init (void)
-{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- return err;
-}
-
-
-gcry_err_code_t
-_gcry_pk_module_lookup (int algorithm, gcry_module_t *module)
-{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
- gcry_module_t pubkey;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- ath_mutex_lock (&pubkeys_registered_lock);
- pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
- if (pubkey)
- *module = pubkey;
- else
- err = GPG_ERR_PUBKEY_ALGO;
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- return err;
-}
-
-
-void
-_gcry_pk_module_release (gcry_module_t module)
-{
- ath_mutex_lock (&pubkeys_registered_lock);
- _gcry_module_release (module);
- ath_mutex_unlock (&pubkeys_registered_lock);
-}
-
-/* Get a list consisting of the IDs of the loaded pubkey modules. If
- LIST is zero, write the number of loaded pubkey modules to
- LIST_LENGTH and return. If LIST is non-zero, the first
- *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
- according size. In case there are less pubkey modules than
- *LIST_LENGTH, *LIST_LENGTH is updated to the correct number. */
-gcry_error_t
-gcry_pk_list (int *list, int *list_length)
-{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
-
- ath_mutex_lock (&pubkeys_registered_lock);
- err = _gcry_module_list (pubkeys_registered, list, list_length);
- ath_mutex_unlock (&pubkeys_registered_lock);
-
- return err;
-}
-
-
-/* Run the selftests for pubkey algorithm ALGO with optional reporting
- function REPORT. */
-gpg_error_t
-_gcry_pk_selftest (int algo, int extended, selftest_report_func_t report)
-{
- gcry_module_t module = NULL;
- pk_extra_spec_t *extraspec = NULL;
- gcry_err_code_t ec = 0;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- ath_mutex_lock (&pubkeys_registered_lock);
- module = _gcry_module_lookup_id (pubkeys_registered, algo);
- if (module && !(module->flags & FLAG_MODULE_DISABLED))
- extraspec = module->extraspec;
- ath_mutex_unlock (&pubkeys_registered_lock);
- if (extraspec && extraspec->selftest)
- ec = extraspec->selftest (algo, extended, report);
- else
- {
- ec = GPG_ERR_PUBKEY_ALGO;
- if (report)
- report ("pubkey", algo, "module",
- module && !(module->flags & FLAG_MODULE_DISABLED)?
- "no selftest available" :
- module? "algorithm disabled" : "algorithm not found");
- }
-
- if (module)
- {
- ath_mutex_lock (&pubkeys_registered_lock);
- _gcry_module_release (module);
- ath_mutex_unlock (&pubkeys_registered_lock);
- }
- return gpg_error (ec);
-}
-
-
-/* This function is only used by ac.c! */
-gcry_err_code_t
-_gcry_pk_get_elements (int algo, char **enc, char **sig)
-{
- gcry_module_t pubkey;
- gcry_pk_spec_t *spec;
- gcry_err_code_t err;
- char *enc_cp;
- char *sig_cp;
-
- REGISTER_DEFAULT_PUBKEYS;
-
- enc_cp = NULL;
- sig_cp = NULL;
- spec = NULL;
-
- pubkey = _gcry_module_lookup_id (pubkeys_registered, algo);
- if (! pubkey)
- {
- err = GPG_ERR_INTERNAL;
- goto out;
- }
- spec = pubkey->spec;
-
- if (enc)
- {
- enc_cp = _strdup (spec->elements_enc);
- if (! enc_cp)
- {
- err = gpg_err_code_from_errno (errno);
- goto out;
- }
- }
-
- if (sig)
- {
- sig_cp = _strdup (spec->elements_sig);
- if (! sig_cp)
- {
- err = gpg_err_code_from_errno (errno);
- goto out;
- }
- }
-
- if (enc)
- *enc = enc_cp;
- if (sig)
- *sig = sig_cp;
- err = 0;
-
- out:
-
- _gcry_module_release (pubkey);
- if (err)
- {
- free (enc_cp);
- free (sig_cp);
- }
-
- return err;
-}
+/* pubkey.c - pubkey dispatcher
+ * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005,
+ * 2007, 2008 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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 },
+#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 wether 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;
+}
+
+
+/* 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 >= 6); /* We know that ECC has at least 6 elements. */
+
+ /* 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
+ * 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.
+ *
+ * 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 <mpi>)
+ * (g <mpi>)
+ * (y <mpi>)
+ * (x <mpi>)
+ * )
+ * )
+ * The <mpi> are expected to be in GCRYMPI_FMT_USG
+ */
+static gcry_err_code_t
+sexp_to_key (gcry_sexp_t sexp, int want_private, 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, "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;
+ }
+
+ elems = want_private ? pubkey->elements_skey : pubkey->elements_pkey;
+ array = gcry_calloc (strlen (elems) + 1, sizeof (*array));
+ if (!array)
+ err = gpg_err_code_from_errno (errno);
+ 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_errno (errno);
+
+ 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;
+}
+
+
+/****************
+ * Take sexp and return an array of MPI as used for our internal decrypt
+ * function.
+ * s_data = (enc-val
+ * [(flags [pkcs1])]
+ * (<algo>
+ * (<param_name1> <mpi>)
+ * ...
+ * (<param_namen> <mpi>)
+ * ))
+ * 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)
+{
+ gcry_err_code_t err = 0;
+ gcry_sexp_t list = NULL, l2 = NULL;
+ gcry_pk_spec_t *pubkey = NULL;
+ gcry_module_t module = NULL;
+ char *name = NULL;
+ size_t n;
+ int parsed_flags = 0;
+ const char *elems;
+ gcry_mpi_t *array = NULL;
+
+ *ret_want_pkcs1 = 0;
+ *ret_modern = 0;
+
+ /* Check that the first element is valid. */
+ list = gcry_sexp_find_token (sexp, "enc-val" , 0);
+ if (!list)
+ {
+ err = GPG_ERR_INV_OBJ; /* Does not contain an encrypted value object. */
+ goto leave;
+ }
+
+ l2 = gcry_sexp_nth (list, 1);
+ if (!l2)
+ {
+ err = GPG_ERR_NO_OBJ; /* No cdr for the data object. */
+ goto leave;
+ }
+
+ /* Extract identifier of sublist. */
+ name = _gcry_sexp_nth_string (l2, 0);
+ if (!name)
+ {
+ err = GPG_ERR_INV_OBJ; /* Invalid structure of object. */
+ goto leave;
+ }
+
+ if (!strcmp (name, "flags"))
+ {
+ /* There is a flags element - process it. */
+ const char *s;
+ int i;
+
+ *ret_modern = 1;
+ for (i = gcry_sexp_length (l2) - 1; i > 0; i--)
+ {
+ s = gcry_sexp_nth_data (l2, i, &n);
+ if (! s)
+ ; /* Not a data element - ignore. */
+ 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;
+ else if (n == 11 && ! memcmp (s, "no-blinding", 11))
+ parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
+ 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);
+ if (!l2)
+ {
+ err = GPG_ERR_NO_OBJ; /* No cdr for the data object. */
+ goto leave;
+ }
+
+ /* Extract sublist identifier. */
+ gcry_free (name);
+ name = _gcry_sexp_nth_string (l2, 0);
+ if (!name)
+ {
+ err = GPG_ERR_INV_OBJ; /* Invalid structure of object. */
+ goto leave;
+ }
+
+ gcry_sexp_release (list);
+ list = l2;
+ l2 = NULL;
+ }
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ module = gcry_pk_lookup_name (name);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ if (!module)
+ {
+ err = GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
+ goto leave;
+ }
+ pubkey = (gcry_pk_spec_t *) module->spec;
+
+ elems = pubkey->elements_enc;
+ array = gcry_calloc (strlen (elems) + 1, sizeof (*array));
+ if (!array)
+ {
+ err = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+
+ err = sexp_elements_extract (list, elems, array, NULL);
+
+ leave:
+ gcry_sexp_release (list);
+ gcry_sexp_release (l2);
+ gcry_free (name);
+
+ 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;
+ *flags = parsed_flags;
+ }
+
+ return err;
+}
+
+/* Take the hash value and convert into an MPI, suitable for
+ passing to the low level functions. We currently support the
+ old style way of passing just a MPI and the modern interface which
+ allows to pass flags so that we can choose between raw and pkcs1
+ padding - may be more padding options later.
+
+ (<mpi>)
+ or
+ (data
+ [(flags [pkcs1])]
+ [(hash <algo> <value>)]
+ [(value <text>)]
+ )
+
+ Either the VALUE or the HASH element must be present for use
+ with signatures. VALUE is used for encryption.
+
+ 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)
+{
+ 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 parsed_flags = 0, dummy_flags;
+
+ if (! flags)
+ flags = &dummy_flags;
+
+ *ret_mpi = NULL;
+ ldata = gcry_sexp_find_token (input, "data", 0);
+ if (!ldata)
+ { /* assume old style */
+ *ret_mpi = gcry_sexp_nth_mpi (input, 0, 0);
+ return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ;
+ }
+
+ /* see whether there is a flags object */
+ {
+ gcry_sexp_t lflags = gcry_sexp_find_token (ldata, "flags", 0);
+ if (lflags)
+ { /* parse the flags list. */
+ for (i=gcry_sexp_length (lflags)-1; i > 0; i--)
+ {
+ s = gcry_sexp_nth_data (lflags, i, &n);
+ if (!s)
+ ; /* not a data element*/
+ else if ( n == 3 && !memcmp (s, "raw", 3))
+ is_raw = 1;
+ else if ( n == 5 && !memcmp (s, "pkcs1", 5))
+ is_pkcs1 = 1;
+ else if (n == 11 && ! memcmp (s, "no-blinding", 11))
+ parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
+ else
+ unknown_flag = 1;
+ }
+ gcry_sexp_release (lflags);
+ }
+ }
+
+ if (!is_pkcs1 && !is_raw)
+ is_raw = 1; /* default to raw */
+
+ /* Get HASH or MPI */
+ lhash = gcry_sexp_find_token (ldata, "hash", 0);
+ lvalue = lhash? NULL : gcry_sexp_find_token (ldata, "value", 0);
+
+ if (!(!lhash ^ !lvalue))
+ 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)
+ {
+ *ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, 0);
+ if (!*ret_mpi)
+ rc = GPG_ERR_INV_OBJ;
+ }
+ else if (is_pkcs1 && lvalue && for_encryption)
+ {
+ /* Create pkcs#1 block type 2 padding. */
+ unsigned char *frame = NULL;
+ size_t nframe = (nbits+7) / 8;
+ const void * value;
+ size_t valuelen;
+ unsigned char *p;
+
+ if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen )
+ rc = GPG_ERR_INV_OBJ;
+ else if (valuelen + 7 > nframe || !nframe)
+ {
+ /* Can't encode a VALUELEN value in a NFRAME bytes frame. */
+ rc = GPG_ERR_TOO_SHORT; /* the key is too short */
+ }
+ else if ( !(frame = gcry_malloc_secure (nframe)))
+ rc = gpg_err_code_from_errno (errno);
+ else
+ {
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 2; /* block type */
+ i = nframe - 3 - valuelen;
+ gcry_assert (i > 0);
+ 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);
+
+ /* FIXME, error checking? */
+ gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, frame, n, &nframe);
+ }
+
+ gcry_free(frame);
+ }
+ else if (is_pkcs1 && lhash && !for_encryption)
+ {
+ /* Create pkcs#1 block type 1 padding. */
+ if (gcry_sexp_length (lhash) != 3)
+ rc = GPG_ERR_INV_OBJ;
+ else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n )
+ 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;
+ size_t nframe = (nbits+7) / 8;
+ const void * value;
+ 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);
+ }
+ }
+
+ asnlen = DIM(asn);
+ dlen = gcry_md_get_algo_dlen (algo);
+
+ if (!algo)
+ rc = GPG_ERR_DIGEST_ALGO;
+ else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
+ || !valuelen )
+ rc = GPG_ERR_INV_OBJ;
+ else if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
+ {
+ /* We don't have yet all of the above algorithms. */
+ rc = GPG_ERR_NOT_IMPLEMENTED;
+ }
+ else if ( valuelen != dlen )
+ {
+ /* Hash value does not match the length of digest for
+ the given algorithm. */
+ rc = GPG_ERR_CONFLICT;
+ }
+ else if( !dlen || dlen + asnlen + 4 > nframe)
+ {
+ /* Can't encode an DLEN byte digest MD into a NFRAME
+ byte frame. */
+ rc = GPG_ERR_TOO_SHORT;
+ }
+ else if ( !(frame = gcry_malloc (nframe)) )
+ rc = gpg_err_code_from_errno (errno);
+ else
+ { /* 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. FIXME: error checking? */
+ gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, frame, n, &nframe);
+ }
+
+ gcry_free (frame);
+ }
+ }
+ else
+ rc = GPG_ERR_CONFLICT;
+
+ gcry_sexp_release (ldata);
+ gcry_sexp_release (lhash);
+ gcry_sexp_release (lvalue);
+
+ if (!rc)
+ *flags = parsed_flags;
+
+ return rc;
+}
+
+
+/*
+ Do a PK encrypt operation
+
+ Caller has to provide a public key as the SEXP pkey and data as a
+ SEXP with just one MPI in it. Alternativly S_DATA might be a
+ complex S-Expression, similar to the one used for signature
+ verification. This provides a flag which allows to handle PKCS#1
+ block type 2 padding. The function returns a a sexp which may be
+ passed to to pk_decrypt.
+
+ Returns: 0 or an errorcode.
+
+ s_data = See comment for sexp_data_to_mpi
+ s_pkey = <key-as-defined-in-sexp_to_key>
+ r_ciph = (enc-val
+ (<algo>
+ (<param_name1> <mpi>)
+ ...
+ (<param_namen> <mpi>)
+ ))
+
+*/
+gcry_error_t
+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;
+ gcry_err_code_t rc;
+ gcry_pk_spec_t *pubkey = NULL;
+ gcry_module_t module = NULL;
+
+ *r_ciph = NULL;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ /* Get the key. */
+ rc = sexp_to_key (s_pkey, 0, &pkey, &module);
+ if (rc)
+ goto leave;
+
+ gcry_assert (module);
+ pubkey = (gcry_pk_spec_t *) module->spec;
+
+ /* If aliases for the algorithm name exists, take the first one
+ instead of the regular name to adhere to SPKI conventions. We
+ assume that the first alias name is the lowercase version of the
+ regular one. This change is required for compatibility with
+ 1.1.12 generated S-expressions. */
+ algo_name = pubkey->aliases? *pubkey->aliases : NULL;
+ if (!algo_name || !*algo_name)
+ algo_name = pubkey->name;
+
+ algo_elems = pubkey->elements_enc;
+
+ /* Get the stuff we want to encrypt. */
+ rc = sexp_data_to_mpi (s_data, gcry_pk_get_nbits (s_pkey), &data, 1,
+ &flags);
+ if (rc)
+ goto leave;
+
+ /* Now we can encrypt DATA to CIPH. */
+ ciph = gcry_calloc (strlen (algo_elems) + 1, sizeof (*ciph));
+ if (!ciph)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ rc = pubkey_encrypt (module->mod_id, ciph, data, pkey, flags);
+ mpi_free (data);
+ data = NULL;
+ if (rc)
+ goto leave;
+
+ /* We did it. Now build the return list */
+ {
+ char *string, *p;
+ int i;
+ size_t nelem = strlen (algo_elems);
+ size_t needed = 19 + strlen (algo_name) + (nelem * 5);
+ void **arg_list;
+
+ /* Build the string. */
+ string = p = gcry_malloc (needed);
+ if (!string)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ p = stpcpy ( p, "(enc-val(" );
+ p = stpcpy ( p, algo_name );
+ for (i=0; algo_elems[i]; i++ )
+ {
+ *p++ = '(';
+ *p++ = algo_elems[i];
+ p = stpcpy ( p, "%m)" );
+ }
+ strcpy ( p, "))" );
+
+ /* And now the ugly part: We don't have a function to pass an
+ * array to a format string, so we have to do it this way :-(. */
+ /* FIXME: There is now such a format specifier, so we can
+ change the code to be more clear. */
+ arg_list = malloc (nelem * sizeof *arg_list);
+ if (!arg_list)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+
+ for (i = 0; i < nelem; i++)
+ arg_list[i] = ciph + i;
+
+ rc = gcry_sexp_build_array (r_ciph, NULL, string, arg_list);
+ free (arg_list);
+ if (rc)
+ BUG ();
+ gcry_free (string);
+ }
+
+ leave:
+ if (pkey)
+ {
+ release_mpi_array (pkey);
+ gcry_free (pkey);
+ }
+
+ if (ciph)
+ {
+ release_mpi_array (ciph);
+ gcry_free (ciph);
+ }
+
+ if (module)
+ {
+ ath_mutex_lock (&pubkeys_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ }
+
+ return gcry_error (rc);
+}
+
+/*
+ Do a PK decrypt operation
+
+ Caller has to provide a secret key as the SEXP skey and data in a
+ format as created by gcry_pk_encrypt. For historic reasons the
+ function returns simply an MPI as an S-expression part; this is
+ deprecated and the new method should be used which returns a real
+ S-expressionl this is selected by adding at least an empty flags
+ list to S_DATA.
+
+ Returns: 0 or an errorcode.
+
+ s_data = (enc-val
+ [(flags)]
+ (<algo>
+ (<param_name1> <mpi>)
+ ...
+ (<param_namen> <mpi>)
+ ))
+ s_skey = <key-as-defined-in-sexp_to_key>
+ r_plain= Either an incomplete S-expression without the parentheses
+ or if the flags list is used (even if empty) a real S-expression:
+ (value PLAIN).
+ */
+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_err_code_t rc;
+ gcry_module_t module_enc = NULL, module_key = NULL;
+ gcry_pk_spec_t *pubkey = NULL;
+
+ *r_plain = NULL;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ rc = sexp_to_key (s_skey, 1, &skey, &module_key);
+ if (rc)
+ goto leave;
+
+ rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &want_pkcs1, &flags);
+ if (rc)
+ goto leave;
+
+ if (module_key->mod_id != module_enc->mod_id)
+ {
+ rc = GPG_ERR_CONFLICT; /* Key algo does not match data algo. */
+ goto leave;
+ }
+
+ pubkey = (gcry_pk_spec_t *) module_key->spec;
+
+ rc = pubkey_decrypt (module_key->mod_id, &plain, data, skey, flags);
+ if (rc)
+ goto leave;
+
+ if (gcry_sexp_build (r_plain, NULL, modern? "(value %m)" : "%m", plain))
+ BUG ();
+
+ leave:
+ if (skey)
+ {
+ release_mpi_array (skey);
+ gcry_free (skey);
+ }
+
+ if (plain)
+ mpi_free (plain);
+
+ if (data)
+ {
+ release_mpi_array (data);
+ gcry_free (data);
+ }
+
+ if (module_key || module_enc)
+ {
+ ath_mutex_lock (&pubkeys_registered_lock);
+ if (module_key)
+ _gcry_module_release (module_key);
+ if (module_enc)
+ _gcry_module_release (module_enc);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ }
+
+ return gcry_error (rc);
+}
+
+
+
+/*
+ Create a signature.
+
+ Caller has to provide a secret key as the SEXP skey and data
+ expressed as a SEXP list hash with only one element which should
+ instantly be available as a MPI. Alternatively the structure given
+ below may be used for S_HASH, it provides the abiliy to pass flags
+ to the operation; the only flag defined by now is "pkcs1" which
+ does PKCS#1 block type 1 style padding.
+
+ Returns: 0 or an errorcode.
+ In case of 0 the function returns a new SEXP with the
+ signature value; the structure of this signature depends on the
+ other arguments but is always suitable to be passed to
+ gcry_pk_verify
+
+ s_hash = See comment for sexp_data_to_mpi
+
+ s_skey = <key-as-defined-in-sexp_to_key>
+ r_sig = (sig-val
+ (<algo>
+ (<param_name1> <mpi>)
+ ...
+ (<param_namen> <mpi>))
+ [(hash algo)])
+
+ Note that (hash algo) in R_SIG is not used.
+*/
+gcry_error_t
+gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
+{
+ gcry_mpi_t *skey = NULL, hash = NULL, *result = NULL;
+ gcry_pk_spec_t *pubkey = NULL;
+ gcry_module_t module = NULL;
+ const char *algo_name, *algo_elems;
+ int i;
+ gcry_err_code_t rc;
+
+ *r_sig = NULL;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ rc = sexp_to_key (s_skey, 1, &skey, &module);
+ if (rc)
+ goto leave;
+
+ gcry_assert (module);
+ pubkey = (gcry_pk_spec_t *) module->spec;
+ algo_name = pubkey->aliases? *pubkey->aliases : NULL;
+ if (!algo_name || !*algo_name)
+ algo_name = pubkey->name;
+
+ algo_elems = pubkey->elements_sig;
+
+ /* 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);
+ if (rc)
+ goto leave;
+
+ result = gcry_calloc (strlen (algo_elems) + 1, sizeof (*result));
+ if (!result)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ rc = pubkey_sign (module->mod_id, result, hash, skey);
+ if (rc)
+ goto leave;
+
+ {
+ char *string, *p;
+ size_t nelem, needed = strlen (algo_name) + 20;
+ void **arg_list;
+
+ nelem = strlen (algo_elems);
+
+ /* Count elements, so that we can allocate enough space. */
+ needed += 10 * nelem;
+
+ /* Build the string. */
+ string = p = gcry_malloc (needed);
+ if (!string)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ p = stpcpy (p, "(sig-val(");
+ p = stpcpy (p, algo_name);
+ for (i = 0; algo_elems[i]; i++)
+ {
+ *p++ = '(';
+ *p++ = algo_elems[i];
+ p = stpcpy (p, "%m)");
+ }
+ strcpy (p, "))");
+
+ arg_list = malloc (nelem * sizeof *arg_list);
+ if (!arg_list)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+
+ for (i = 0; i < nelem; i++)
+ arg_list[i] = result + i;
+
+ rc = gcry_sexp_build_array (r_sig, NULL, string, arg_list);
+ free (arg_list);
+ if (rc)
+ BUG ();
+ gcry_free (string);
+ }
+
+ leave:
+ if (skey)
+ {
+ release_mpi_array (skey);
+ gcry_free (skey);
+ }
+
+ if (hash)
+ mpi_free (hash);
+
+ if (result)
+ {
+ release_mpi_array (result);
+ gcry_free (result);
+ }
+
+ return gcry_error (rc);
+}
+
+
+/*
+ Verify a signature.
+
+ Caller has to supply the public key pkey, the signature sig and his
+ hashvalue data. Public key has to be a standard public key given
+ as an S-Exp, sig is a S-Exp as returned from gcry_pk_sign and data
+ must be an S-Exp like the one in sign too. */
+gcry_error_t
+gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
+{
+ gcry_module_t module_key = NULL, module_sig = NULL;
+ gcry_mpi_t *pkey = NULL, hash = NULL, *sig = NULL;
+ gcry_err_code_t rc;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ rc = sexp_to_key (s_pkey, 0, &pkey, &module_key);
+ if (rc)
+ goto leave;
+
+ rc = sexp_to_sig (s_sig, &sig, &module_sig);
+ if (rc)
+ goto leave;
+
+ /* Fixme: Check that the algorithm of S_SIG is compatible to the one
+ of S_PKEY. */
+
+ if (module_key->mod_id != module_sig->mod_id)
+ {
+ rc = GPG_ERR_CONFLICT;
+ goto leave;
+ }
+
+ rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_pkey), &hash, 0, 0);
+ if (rc)
+ goto leave;
+
+ rc = pubkey_verify (module_key->mod_id, hash, sig, pkey, NULL, NULL);
+
+ leave:
+ if (pkey)
+ {
+ release_mpi_array (pkey);
+ gcry_free (pkey);
+ }
+ if (sig)
+ {
+ release_mpi_array (sig);
+ gcry_free (sig);
+ }
+ if (hash)
+ mpi_free (hash);
+
+ if (module_key || module_sig)
+ {
+ ath_mutex_lock (&pubkeys_registered_lock);
+ if (module_key)
+ _gcry_module_release (module_key);
+ if (module_sig)
+ _gcry_module_release (module_sig);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ }
+
+ return gcry_error (rc);
+}
+
+
+/*
+ Test a key.
+
+ This may be used either for a public or a secret key to see whether
+ the internal structure is okay.
+
+ Returns: 0 or an errorcode.
+
+ s_key = <key-as-defined-in-sexp_to_key> */
+gcry_error_t
+gcry_pk_testkey (gcry_sexp_t s_key)
+{
+ gcry_module_t module = NULL;
+ gcry_mpi_t *key = NULL;
+ gcry_err_code_t rc;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ /* Note we currently support only secret key checking. */
+ rc = sexp_to_key (s_key, 1, &key, &module);
+ if (! rc)
+ {
+ rc = pubkey_check_secret_key (module->mod_id, key);
+ release_mpi_array (key);
+ gcry_free (key);
+ }
+ return gcry_error (rc);
+}
+
+
+/*
+ Create a public key pair and return it in r_key.
+ How the key is created depends on s_parms:
+ (genkey
+ (algo
+ (parameter_name_1 ....)
+ ....
+ (parameter_name_n ....)
+ ))
+ The key is returned in a format depending on the
+ algorithm. Both, private and secret keys are returned
+ and optionally some additional informatin.
+ For elgamal we return this structure:
+ (key-data
+ (public-key
+ (elg
+ (p <mpi>)
+ (g <mpi>)
+ (y <mpi>)
+ )
+ )
+ (private-key
+ (elg
+ (p <mpi>)
+ (g <mpi>)
+ (y <mpi>)
+ (x <mpi>)
+ )
+ )
+ (misc-key-info
+ (pm1-factors n1 n2 ... nn)
+ ))
+ */
+gcry_error_t
+gcry_pk_genkey (gcry_sexp_t *r_key, gcry_sexp_t s_parms)
+{
+ gcry_pk_spec_t *pubkey = NULL;
+ gcry_module_t module = NULL;
+ gcry_sexp_t list = NULL;
+ gcry_sexp_t l2 = NULL;
+ gcry_sexp_t l3 = NULL;
+ char *name = NULL;
+ size_t n;
+ gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+ int i;
+ const char *algo_name = NULL;
+ int algo;
+ const char *sec_elems = NULL, *pub_elems = NULL;
+ gcry_mpi_t skey[12];
+ gcry_mpi_t *factors = NULL;
+ gcry_sexp_t extrainfo = NULL;
+ unsigned int nbits = 0;
+ unsigned long use_e = 0;
+
+ skey[0] = NULL;
+ *r_key = NULL;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ list = gcry_sexp_find_token (s_parms, "genkey", 0);
+ if (!list)
+ {
+ rc = GPG_ERR_INV_OBJ; /* Does not contain genkey data. */
+ goto leave;
+ }
+
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ l2 = NULL;
+ if (! list)
+ {
+ rc = GPG_ERR_NO_OBJ; /* No cdr for the genkey. */
+ goto leave;
+ }
+
+ name = _gcry_sexp_nth_string (list, 0);
+ if (!name)
+ {
+ rc = GPG_ERR_INV_OBJ; /* Algo string missing. */
+ goto leave;
+ }
+
+ 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)
+ {
+ rc = GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
+ goto leave;
+ }
+
+ pubkey = (gcry_pk_spec_t *) module->spec;
+ algo = module->mod_id;
+ algo_name = pubkey->aliases? *pubkey->aliases : NULL;
+ if (!algo_name || !*algo_name)
+ algo_name = pubkey->name;
+ pub_elems = pubkey->elements_pkey;
+ sec_elems = pubkey->elements_skey;
+ if (strlen (sec_elems) >= DIM(skey))
+ BUG ();
+
+ /* Handle the optional rsa-use-e element. Actually this belong into
+ the algorithm module but we have this parameter in the public
+ module API, so we need to parse it right here. */
+ l2 = gcry_sexp_find_token (list, "rsa-use-e", 0);
+ if (l2)
+ {
+ char buf[50];
+ const char *s;
+
+ s = gcry_sexp_nth_data (l2, 1, &n);
+ if ( !s || n >= DIM (buf) - 1 )
+ {
+ rc = GPG_ERR_INV_OBJ; /* No value or value too large. */
+ goto leave;
+ }
+ memcpy (buf, s, n);
+ buf[n] = 0;
+ use_e = strtoul (buf, NULL, 0);
+ gcry_sexp_release (l2);
+ l2 = NULL;
+ }
+ else
+ use_e = 65537; /* Not given, use the value generated by old versions. */
+
+
+ /* Get the "nbits" parameter. */
+ l2 = gcry_sexp_find_token (list, "nbits", 0);
+ if (l2)
+ {
+ char buf[50];
+ const char *s;
+
+ s = gcry_sexp_nth_data (l2, 1, &n);
+ if (!s || n >= DIM (buf) - 1 )
+ {
+ rc = GPG_ERR_INV_OBJ; /* NBITS given without a cdr. */
+ goto leave;
+ }
+ memcpy (buf, s, n);
+ buf[n] = 0;
+ nbits = (unsigned int)strtoul (buf, NULL, 0);
+ gcry_sexp_release (l2); l2 = NULL;
+ }
+ else
+ nbits = 0;
+
+ /* Pass control to the algorithm module. */
+ rc = pubkey_generate (module->mod_id, nbits, use_e, list, skey,
+ &factors, &extrainfo);
+ gcry_sexp_release (list); list = NULL;
+ if (rc)
+ goto leave;
+
+ /* Key generation succeeded: Build an S-expression. */
+ {
+ char *string, *p;
+ size_t nelem=0, nelem_cp = 0, needed=0;
+ gcry_mpi_t mpis[30];
+
+ /* Estimate size of format string. */
+ nelem = strlen (pub_elems) + strlen (sec_elems);
+ if (factors)
+ {
+ for (i = 0; factors[i]; i++)
+ nelem++;
+ }
+ nelem_cp = nelem;
+
+ needed += nelem * 10;
+ /* (+5 is for EXTRAINFO ("%S")). */
+ needed += 2 * strlen (algo_name) + 300 + 5;
+ if (nelem > DIM (mpis))
+ BUG ();
+
+ /* Build the string. */
+ nelem = 0;
+ string = p = gcry_malloc (needed);
+ if (!string)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ p = stpcpy (p, "(key-data");
+ p = stpcpy (p, "(public-key(");
+ p = stpcpy (p, algo_name);
+ for(i = 0; pub_elems[i]; i++)
+ {
+ *p++ = '(';
+ *p++ = pub_elems[i];
+ p = stpcpy (p, "%m)");
+ mpis[nelem++] = skey[i];
+ }
+ p = stpcpy (p, "))");
+ p = stpcpy (p, "(private-key(");
+ p = stpcpy (p, algo_name);
+ for (i = 0; sec_elems[i]; i++)
+ {
+ *p++ = '(';
+ *p++ = sec_elems[i];
+ p = stpcpy (p, "%m)");
+ mpis[nelem++] = skey[i];
+ }
+ p = stpcpy (p, "))");
+
+ /* Hack to make release_mpi_array() work. */
+ skey[i] = NULL;
+
+ if (extrainfo)
+ {
+ /* If we have extrainfo we should not have any factors. */
+ p = stpcpy (p, "%S");
+ }
+ else if (factors && factors[0])
+ {
+ p = stpcpy (p, "(misc-key-info(pm1-factors");
+ for(i = 0; factors[i]; i++)
+ {
+ p = stpcpy (p, "%m");
+ mpis[nelem++] = factors[i];
+ }
+ p = stpcpy (p, "))");
+ }
+ strcpy (p, ")");
+ gcry_assert (p - string < needed);
+
+ while (nelem < DIM (mpis))
+ mpis[nelem++] = NULL;
+
+ {
+ int elem_n = strlen (pub_elems) + strlen (sec_elems);
+ void **arg_list;
+
+ /* Allocate one extra for EXTRAINFO ("%S"). */
+ arg_list = gcry_calloc (nelem_cp+1, sizeof *arg_list);
+ if (!arg_list)
+ {
+ rc = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ for (i = 0; i < elem_n; i++)
+ arg_list[i] = mpis + i;
+ if (extrainfo)
+ arg_list[i] = &extrainfo;
+ else if (factors && factors[0])
+ {
+ for (; i < nelem_cp; i++)
+ arg_list[i] = factors + i - elem_n;
+ }
+
+ rc = gcry_sexp_build_array (r_key, NULL, string, arg_list);
+ gcry_free (arg_list);
+ if (rc)
+ BUG ();
+ gcry_assert (DIM (mpis) == 30); /* Reminder to make sure that
+ the array gets increased if
+ new parameters are added. */
+ }
+ gcry_free (string);
+ }
+
+ leave:
+ gcry_free (name);
+ gcry_sexp_release (extrainfo);
+ release_mpi_array (skey);
+ /* Don't free SKEY itself, it is an stack allocated array. */
+
+ if (factors)
+ {
+ release_mpi_array ( factors );
+ gcry_free (factors);
+ }
+
+ gcry_sexp_release (l3);
+ gcry_sexp_release (l2);
+ gcry_sexp_release (list);
+
+ if (module)
+ {
+ ath_mutex_lock (&pubkeys_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ }
+
+ return gcry_error (rc);
+}
+
+
+/*
+ Get the number of nbits from the public key.
+
+ Hmmm: Should we have really this function or is it better to have a
+ more general function to retrieve different properties of the key? */
+unsigned int
+gcry_pk_get_nbits (gcry_sexp_t key)
+{
+ gcry_module_t module = NULL;
+ gcry_pk_spec_t *pubkey;
+ gcry_mpi_t *keyarr = NULL;
+ unsigned int nbits = 0;
+ gcry_err_code_t rc;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ rc = sexp_to_key (key, 0, &keyarr, &module);
+ if (rc == GPG_ERR_INV_OBJ)
+ rc = sexp_to_key (key, 1, &keyarr, &module);
+ if (rc)
+ return 0; /* Error - 0 is a suitable indication for that. */
+
+ pubkey = (gcry_pk_spec_t *) module->spec;
+ nbits = (*pubkey->get_nbits) (module->mod_id, keyarr);
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ release_mpi_array (keyarr);
+ gcry_free (keyarr);
+
+ return nbits;
+}
+
+
+/* Return the so called KEYGRIP which is the SHA-1 hash of the public
+ key parameters expressed in a way depended on the algorithm.
+
+ ARRAY must either be 20 bytes long or NULL; in the latter case a
+ newly allocated array of that size is returned, otherwise ARRAY or
+ NULL is returned to indicate an error which is most likely an
+ unknown algorithm. The function accepts public or secret keys. */
+unsigned char *
+gcry_pk_get_keygrip (gcry_sexp_t key, unsigned char *array)
+{
+ gcry_sexp_t list = NULL, l2 = NULL;
+ gcry_pk_spec_t *pubkey = NULL;
+ gcry_module_t module = NULL;
+ pk_extra_spec_t *extraspec;
+ const char *s;
+ char *name = NULL;
+ int idx;
+ const char *elems;
+ gcry_md_hd_t md = NULL;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ /* Check that the first element is valid. */
+ list = gcry_sexp_find_token (key, "public-key", 0);
+ if (! list)
+ list = gcry_sexp_find_token (key, "private-key", 0);
+ if (! list)
+ list = gcry_sexp_find_token (key, "protected-private-key", 0);
+ if (! list)
+ list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
+ if (! list)
+ return NULL; /* No public- or private-key object. */
+
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ l2 = NULL;
+
+ name = _gcry_sexp_nth_string (list, 0);
+ if (!name)
+ goto fail; /* Invalid structure of object. */
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ module = gcry_pk_lookup_name (name);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ if (!module)
+ goto fail; /* Unknown algorithm. */
+
+ pubkey = (gcry_pk_spec_t *) module->spec;
+ extraspec = module->extraspec;
+
+ elems = pubkey->elements_grip;
+ if (!elems)
+ goto fail; /* No grip parameter. */
+
+ if (gcry_md_open (&md, GCRY_MD_SHA1, 0))
+ goto fail;
+
+ if (extraspec && extraspec->comp_keygrip)
+ {
+ /* Module specific method to compute a keygrip. */
+ if (extraspec->comp_keygrip (md, list))
+ goto fail;
+ }
+ else
+ {
+ /* Generic method to compute a keygrip. */
+ for (idx = 0, s = elems; *s; s++, idx++)
+ {
+ const char *data;
+ size_t datalen;
+ char buf[30];
+
+ l2 = gcry_sexp_find_token (list, s, 1);
+ if (! l2)
+ goto fail;
+ data = gcry_sexp_nth_data (l2, 1, &datalen);
+ if (! data)
+ goto fail;
+
+ snprintf (buf, sizeof buf, "(1:%c%u:", *s, (unsigned int)datalen);
+ gcry_md_write (md, buf, strlen (buf));
+ gcry_md_write (md, data, datalen);
+ gcry_sexp_release (l2);
+ gcry_md_write (md, ")", 1);
+ }
+ }
+
+ if (!array)
+ {
+ array = gcry_malloc (20);
+ if (! array)
+ goto fail;
+ }
+
+ memcpy (array, gcry_md_read (md, GCRY_MD_SHA1), 20);
+ gcry_md_close (md);
+ gcry_sexp_release (list);
+ return array;
+
+ fail:
+ gcry_free (name);
+ gcry_sexp_release (l2);
+ gcry_md_close (md);
+ gcry_sexp_release (list);
+ return NULL;
+}
+
+
+gcry_error_t
+gcry_pk_ctl (int cmd, void *buffer, size_t buflen)
+{
+ gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ switch (cmd)
+ {
+ case GCRYCTL_DISABLE_ALGO:
+ /* This one expects a buffer pointing to an integer with the
+ algo number. */
+ if ((! buffer) || (buflen != sizeof (int)))
+ err = GPG_ERR_INV_ARG;
+ else
+ disable_pubkey_algo (*((int *) buffer));
+ break;
+
+ default:
+ err = GPG_ERR_INV_OP;
+ }
+
+ return gcry_error (err);
+}
+
+
+/* Return information about the given algorithm
+
+ WHAT selects the kind of information returned:
+
+ GCRYCTL_TEST_ALGO:
+ Returns 0 when the specified algorithm is available for use.
+ Buffer must be NULL, nbytes may have the address of a variable
+ with the required usage of the algorithm. It may be 0 for don't
+ care or a combination of the GCRY_PK_USAGE_xxx flags;
+
+ GCRYCTL_GET_ALGO_USAGE:
+ Return the usage glafs for the give algo. An invalid alog
+ does return 0. Disabled algos are ignored here becuase we
+ only want to know whether the algo is at all capable of
+ the usage.
+
+ Note: Because this function is in most cases used to return an
+ integer value, we can make it easier for the caller to just look at
+ the return value. The caller will in all cases consult the value
+ and thereby detecting whether a error occured or not (i.e. while
+ checking the block size) */
+gcry_error_t
+gcry_pk_algo_info (int algorithm, int what, void *buffer, size_t *nbytes)
+{
+ gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+ switch (what)
+ {
+ case GCRYCTL_TEST_ALGO:
+ {
+ int use = nbytes ? *nbytes : 0;
+ if (buffer)
+ err = GPG_ERR_INV_ARG;
+ else if (check_pubkey_algo (algorithm, use))
+ err = GPG_ERR_PUBKEY_ALGO;
+ break;
+ }
+
+ case GCRYCTL_GET_ALGO_USAGE:
+ {
+ gcry_module_t pubkey;
+ int use = 0;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
+ if (pubkey)
+ {
+ use = ((gcry_pk_spec_t *) pubkey->spec)->use;
+ _gcry_module_release (pubkey);
+ }
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ /* FIXME? */
+ *nbytes = use;
+
+ break;
+ }
+
+ case GCRYCTL_GET_ALGO_NPKEY:
+ {
+ /* FIXME? */
+ int npkey = pubkey_get_npkey (algorithm);
+ *nbytes = npkey;
+ break;
+ }
+ case GCRYCTL_GET_ALGO_NSKEY:
+ {
+ /* FIXME? */
+ int nskey = pubkey_get_nskey (algorithm);
+ *nbytes = nskey;
+ break;
+ }
+ case GCRYCTL_GET_ALGO_NSIGN:
+ {
+ /* FIXME? */
+ int nsign = pubkey_get_nsig (algorithm);
+ *nbytes = nsign;
+ break;
+ }
+ case GCRYCTL_GET_ALGO_NENCR:
+ {
+ /* FIXME? */
+ int nencr = pubkey_get_nenc (algorithm);
+ *nbytes = nencr;
+ break;
+ }
+
+ default:
+ err = GPG_ERR_INV_OP;
+ }
+
+ return gcry_error (err);
+}
+
+
+/* Explicitly initialize this module. */
+gcry_err_code_t
+_gcry_pk_init (void)
+{
+ gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ return err;
+}
+
+
+gcry_err_code_t
+_gcry_pk_module_lookup (int algorithm, gcry_module_t *module)
+{
+ gcry_err_code_t err = GPG_ERR_NO_ERROR;
+ gcry_module_t pubkey;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
+ if (pubkey)
+ *module = pubkey;
+ else
+ err = GPG_ERR_PUBKEY_ALGO;
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ return err;
+}
+
+
+void
+_gcry_pk_module_release (gcry_module_t module)
+{
+ ath_mutex_lock (&pubkeys_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+}
+
+/* Get a list consisting of the IDs of the loaded pubkey modules. If
+ LIST is zero, write the number of loaded pubkey modules to
+ LIST_LENGTH and return. If LIST is non-zero, the first
+ *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
+ according size. In case there are less pubkey modules than
+ *LIST_LENGTH, *LIST_LENGTH is updated to the correct number. */
+gcry_error_t
+gcry_pk_list (int *list, int *list_length)
+{
+ gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ err = _gcry_module_list (pubkeys_registered, list, list_length);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+
+ return err;
+}
+
+
+/* Run the selftests for pubkey algorithm ALGO with optional reporting
+ function REPORT. */
+gpg_error_t
+_gcry_pk_selftest (int algo, int extended, selftest_report_func_t report)
+{
+ gcry_module_t module = NULL;
+ pk_extra_spec_t *extraspec = NULL;
+ gcry_err_code_t ec = 0;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ ath_mutex_lock (&pubkeys_registered_lock);
+ module = _gcry_module_lookup_id (pubkeys_registered, algo);
+ if (module && !(module->flags & FLAG_MODULE_DISABLED))
+ extraspec = module->extraspec;
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ if (extraspec && extraspec->selftest)
+ ec = extraspec->selftest (algo, extended, report);
+ else
+ {
+ ec = GPG_ERR_PUBKEY_ALGO;
+ if (report)
+ report ("pubkey", algo, "module",
+ module && !(module->flags & FLAG_MODULE_DISABLED)?
+ "no selftest available" :
+ module? "algorithm disabled" : "algorithm not found");
+ }
+
+ if (module)
+ {
+ ath_mutex_lock (&pubkeys_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&pubkeys_registered_lock);
+ }
+ return gpg_error (ec);
+}
+
+
+/* This function is only used by ac.c! */
+gcry_err_code_t
+_gcry_pk_get_elements (int algo, char **enc, char **sig)
+{
+ gcry_module_t pubkey;
+ gcry_pk_spec_t *spec;
+ gcry_err_code_t err;
+ char *enc_cp;
+ char *sig_cp;
+
+ REGISTER_DEFAULT_PUBKEYS;
+
+ enc_cp = NULL;
+ sig_cp = NULL;
+ spec = NULL;
+
+ pubkey = _gcry_module_lookup_id (pubkeys_registered, algo);
+ if (! pubkey)
+ {
+ err = GPG_ERR_INTERNAL;
+ goto out;
+ }
+ spec = pubkey->spec;
+
+ if (enc)
+ {
+ enc_cp = strdup (spec->elements_enc);
+ if (! enc_cp)
+ {
+ err = gpg_err_code_from_errno (errno);
+ goto out;
+ }
+ }
+
+ if (sig)
+ {
+ sig_cp = strdup (spec->elements_sig);
+ if (! sig_cp)
+ {
+ err = gpg_err_code_from_errno (errno);
+ goto out;
+ }
+ }
+
+ if (enc)
+ *enc = enc_cp;
+ if (sig)
+ *sig = sig_cp;
+ err = 0;
+
+ out:
+
+ _gcry_module_release (pubkey);
+ if (err)
+ {
+ free (enc_cp);
+ free (sig_cp);
+ }
+
+ return err;
+}