2749 lines
70 KiB
C
2749 lines
70 KiB
C
/* 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;
|
|
}
|