f725fa7cb2
This modifies most of the places we do some form of: X = malloc(Y * Z); to use calloc(Y, Z) instead. Among other issues, this fixes: - allocation of integer overflow in grub_png_decode_image_header() reported by Chris Coulson, - allocation of integer overflow in luks_recover_key() reported by Chris Coulson, - allocation of integer overflow in grub_lvm_detect() reported by Chris Coulson. Fixes: CVE-2020-14308 Signed-off-by: Peter Jones <pjones@redhat.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
4212 lines
112 KiB
C
4212 lines
112 KiB
C
/* pubkey.c - pubkey dispatcher
|
||
* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005,
|
||
* 2007, 2008, 2011 Free Software Foundation, Inc.
|
||
*
|
||
* This file is part of Libgcrypt.
|
||
*
|
||
* Libgcrypt is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU Lesser general Public License as
|
||
* published by the Free Software Foundation; either version 2.1 of
|
||
* the License, or (at your option) any later version.
|
||
*
|
||
* Libgcrypt is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this program; if not, see <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 },
|
||
{ &_gcry_pubkey_spec_ecdh,
|
||
&_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDH, 0 },
|
||
#endif
|
||
{ NULL, 0 },
|
||
};
|
||
|
||
/* List of registered ciphers. */
|
||
static gcry_module_t pubkeys_registered;
|
||
|
||
/* This is the lock protecting PUBKEYS_REGISTERED. */
|
||
static ath_mutex_t pubkeys_registered_lock = ATH_MUTEX_INITIALIZER;;
|
||
|
||
/* Flag to check whether the default pubkeys have already been
|
||
registered. */
|
||
static int default_pubkeys_registered;
|
||
|
||
/* Convenient macro for registering the default digests. */
|
||
#define REGISTER_DEFAULT_PUBKEYS \
|
||
do \
|
||
{ \
|
||
ath_mutex_lock (&pubkeys_registered_lock); \
|
||
if (! default_pubkeys_registered) \
|
||
{ \
|
||
pk_register_default (); \
|
||
default_pubkeys_registered = 1; \
|
||
} \
|
||
ath_mutex_unlock (&pubkeys_registered_lock); \
|
||
} \
|
||
while (0)
|
||
|
||
/* These dummy functions are used in case a cipher implementation
|
||
refuses to provide it's own functions. */
|
||
|
||
static gcry_err_code_t
|
||
dummy_generate (int algorithm, unsigned int nbits, unsigned long dummy,
|
||
gcry_mpi_t *skey, gcry_mpi_t **retfactors)
|
||
{
|
||
(void)algorithm;
|
||
(void)nbits;
|
||
(void)dummy;
|
||
(void)skey;
|
||
(void)retfactors;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static gcry_err_code_t
|
||
dummy_check_secret_key (int algorithm, gcry_mpi_t *skey)
|
||
{
|
||
(void)algorithm;
|
||
(void)skey;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static gcry_err_code_t
|
||
dummy_encrypt (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
|
||
gcry_mpi_t *pkey, int flags)
|
||
{
|
||
(void)algorithm;
|
||
(void)resarr;
|
||
(void)data;
|
||
(void)pkey;
|
||
(void)flags;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static gcry_err_code_t
|
||
dummy_decrypt (int algorithm, gcry_mpi_t *result, gcry_mpi_t *data,
|
||
gcry_mpi_t *skey, int flags)
|
||
{
|
||
(void)algorithm;
|
||
(void)result;
|
||
(void)data;
|
||
(void)skey;
|
||
(void)flags;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static gcry_err_code_t
|
||
dummy_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
|
||
gcry_mpi_t *skey)
|
||
{
|
||
(void)algorithm;
|
||
(void)resarr;
|
||
(void)data;
|
||
(void)skey;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static gcry_err_code_t
|
||
dummy_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
|
||
gcry_mpi_t *pkey,
|
||
int (*cmp) (void *, gcry_mpi_t), void *opaquev)
|
||
{
|
||
(void)algorithm;
|
||
(void)hash;
|
||
(void)data;
|
||
(void)pkey;
|
||
(void)cmp;
|
||
(void)opaquev;
|
||
fips_signal_error ("using dummy public key function");
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
static unsigned
|
||
dummy_get_nbits (int algorithm, gcry_mpi_t *pkey)
|
||
{
|
||
(void)algorithm;
|
||
(void)pkey;
|
||
fips_signal_error ("using dummy public key function");
|
||
return 0;
|
||
}
|
||
|
||
/* Internal function. Register all the pubkeys included in
|
||
PUBKEY_TABLE. Returns zero on success or an error code. */
|
||
static void
|
||
pk_register_default (void)
|
||
{
|
||
gcry_err_code_t err = 0;
|
||
int i;
|
||
|
||
for (i = 0; (! err) && pubkey_table[i].pubkey; i++)
|
||
{
|
||
#define pubkey_use_dummy(func) \
|
||
if (! pubkey_table[i].pubkey->func) \
|
||
pubkey_table[i].pubkey->func = dummy_##func;
|
||
|
||
pubkey_use_dummy (generate);
|
||
pubkey_use_dummy (check_secret_key);
|
||
pubkey_use_dummy (encrypt);
|
||
pubkey_use_dummy (decrypt);
|
||
pubkey_use_dummy (sign);
|
||
pubkey_use_dummy (verify);
|
||
pubkey_use_dummy (get_nbits);
|
||
#undef pubkey_use_dummy
|
||
|
||
err = _gcry_module_add (&pubkeys_registered,
|
||
pubkey_table[i].algorithm,
|
||
(void *) pubkey_table[i].pubkey,
|
||
(void *) pubkey_table[i].extraspec,
|
||
NULL);
|
||
}
|
||
|
||
if (err)
|
||
BUG ();
|
||
}
|
||
|
||
/* Internal callback function. Used via _gcry_module_lookup. */
|
||
static int
|
||
gcry_pk_lookup_func_name (void *spec, void *data)
|
||
{
|
||
gcry_pk_spec_t *pubkey = (gcry_pk_spec_t *) spec;
|
||
char *name = (char *) data;
|
||
const char **aliases = pubkey->aliases;
|
||
int ret = stricmp (name, pubkey->name);
|
||
|
||
while (ret && *aliases)
|
||
ret = stricmp (name, *aliases++);
|
||
|
||
return ! ret;
|
||
}
|
||
|
||
/* Internal function. Lookup a pubkey entry by it's name. */
|
||
static gcry_module_t
|
||
gcry_pk_lookup_name (const char *name)
|
||
{
|
||
gcry_module_t pubkey;
|
||
|
||
pubkey = _gcry_module_lookup (pubkeys_registered, (void *) name,
|
||
gcry_pk_lookup_func_name);
|
||
|
||
return pubkey;
|
||
}
|
||
|
||
/* Register a new pubkey module whose specification can be found in
|
||
PUBKEY. On success, a new algorithm ID is stored in ALGORITHM_ID
|
||
and a pointer representhing this module is stored in MODULE. */
|
||
gcry_error_t
|
||
_gcry_pk_register (gcry_pk_spec_t *pubkey,
|
||
pk_extra_spec_t *extraspec,
|
||
unsigned int *algorithm_id,
|
||
gcry_module_t *module)
|
||
{
|
||
gcry_err_code_t err = GPG_ERR_NO_ERROR;
|
||
gcry_module_t mod;
|
||
|
||
/* We do not support module loading in fips mode. */
|
||
if (fips_mode ())
|
||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
err = _gcry_module_add (&pubkeys_registered, 0,
|
||
(void *) pubkey,
|
||
(void *)(extraspec? extraspec : &dummy_extra_spec),
|
||
&mod);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
if (! err)
|
||
{
|
||
*module = mod;
|
||
*algorithm_id = mod->mod_id;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
/* Unregister the pubkey identified by ID, which must have been
|
||
registered with gcry_pk_register. */
|
||
void
|
||
gcry_pk_unregister (gcry_module_t module)
|
||
{
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
_gcry_module_release (module);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
}
|
||
|
||
static void
|
||
release_mpi_array (gcry_mpi_t *array)
|
||
{
|
||
for (; *array; array++)
|
||
{
|
||
mpi_free(*array);
|
||
*array = NULL;
|
||
}
|
||
}
|
||
|
||
/****************
|
||
* Map a string to the pubkey algo
|
||
*/
|
||
int
|
||
gcry_pk_map_name (const char *string)
|
||
{
|
||
gcry_module_t pubkey;
|
||
int algorithm = 0;
|
||
|
||
if (!string)
|
||
return 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = gcry_pk_lookup_name (string);
|
||
if (pubkey)
|
||
{
|
||
algorithm = pubkey->mod_id;
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return algorithm;
|
||
}
|
||
|
||
|
||
/* Map the public key algorithm whose ID is contained in ALGORITHM to
|
||
a string representation of the algorithm name. For unknown
|
||
algorithm IDs this functions returns "?". */
|
||
const char *
|
||
gcry_pk_algo_name (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
const char *name;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
name = ((gcry_pk_spec_t *) pubkey->spec)->name;
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
else
|
||
name = "?";
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return name;
|
||
}
|
||
|
||
|
||
/* A special version of gcry_pk_algo name to return the first aliased
|
||
name of the algorithm. This is required to adhere to the spki
|
||
specs where the algorithm names are lowercase. */
|
||
const char *
|
||
_gcry_pk_aliased_algo_name (int algorithm)
|
||
{
|
||
const char *name = NULL;
|
||
gcry_module_t module;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
gcry_pk_spec_t *pubkey = (gcry_pk_spec_t *) module->spec;
|
||
|
||
name = pubkey->aliases? *pubkey->aliases : NULL;
|
||
if (!name || !*name)
|
||
name = pubkey->name;
|
||
_gcry_module_release (module);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return name;
|
||
}
|
||
|
||
|
||
static void
|
||
disable_pubkey_algo (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
if (! (pubkey-> flags & FLAG_MODULE_DISABLED))
|
||
pubkey->flags |= FLAG_MODULE_DISABLED;
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
}
|
||
|
||
|
||
/****************
|
||
* A USE of 0 means: don't care.
|
||
*/
|
||
static gcry_err_code_t
|
||
check_pubkey_algo (int algorithm, unsigned use)
|
||
{
|
||
gcry_err_code_t err = GPG_ERR_NO_ERROR;
|
||
gcry_pk_spec_t *pubkey;
|
||
gcry_module_t module;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
|
||
if (((use & GCRY_PK_USAGE_SIGN)
|
||
&& (! (pubkey->use & GCRY_PK_USAGE_SIGN)))
|
||
|| ((use & GCRY_PK_USAGE_ENCR)
|
||
&& (! (pubkey->use & GCRY_PK_USAGE_ENCR))))
|
||
err = GPG_ERR_WRONG_PUBKEY_ALGO;
|
||
else if (module->flags & FLAG_MODULE_DISABLED)
|
||
err = GPG_ERR_PUBKEY_ALGO;
|
||
_gcry_module_release (module);
|
||
}
|
||
else
|
||
err = GPG_ERR_PUBKEY_ALGO;
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
/****************
|
||
* Return the number of public key material numbers
|
||
*/
|
||
static int
|
||
pubkey_get_npkey (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
int npkey = 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
npkey = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_pkey);
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return npkey;
|
||
}
|
||
|
||
/****************
|
||
* Return the number of secret key material numbers
|
||
*/
|
||
static int
|
||
pubkey_get_nskey (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
int nskey = 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
nskey = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_skey);
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return nskey;
|
||
}
|
||
|
||
/****************
|
||
* Return the number of signature material numbers
|
||
*/
|
||
static int
|
||
pubkey_get_nsig (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
int nsig = 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
nsig = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_sig);
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return nsig;
|
||
}
|
||
|
||
/****************
|
||
* Return the number of encryption material numbers
|
||
*/
|
||
static int
|
||
pubkey_get_nenc (int algorithm)
|
||
{
|
||
gcry_module_t pubkey;
|
||
int nenc = 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
nenc = strlen (((gcry_pk_spec_t *) pubkey->spec)->elements_enc);
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return nenc;
|
||
}
|
||
|
||
|
||
/* Generate a new public key with algorithm ALGORITHM of size NBITS
|
||
and return it at SKEY. USE_E depends on the ALGORITHM. GENPARMS
|
||
is passed to the algorithm module if it features an extended
|
||
generation function. RETFACTOR is used by some algorithms to
|
||
return certain additional information which are in general not
|
||
required.
|
||
|
||
The function returns the error code number or 0 on success. */
|
||
static gcry_err_code_t
|
||
pubkey_generate (int algorithm,
|
||
unsigned int nbits,
|
||
unsigned long use_e,
|
||
gcry_sexp_t genparms,
|
||
gcry_mpi_t *skey, gcry_mpi_t **retfactors,
|
||
gcry_sexp_t *r_extrainfo)
|
||
{
|
||
gcry_err_code_t ec = GPG_ERR_PUBKEY_ALGO;
|
||
gcry_module_t pubkey;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
pk_extra_spec_t *extraspec = pubkey->extraspec;
|
||
|
||
if (extraspec && extraspec->ext_generate)
|
||
{
|
||
/* Use the extended generate function. */
|
||
ec = extraspec->ext_generate
|
||
(algorithm, nbits, use_e, genparms, skey, retfactors, r_extrainfo);
|
||
}
|
||
else
|
||
{
|
||
/* Use the standard generate function. */
|
||
ec = ((gcry_pk_spec_t *) pubkey->spec)->generate
|
||
(algorithm, nbits, use_e, skey, retfactors);
|
||
}
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return ec;
|
||
}
|
||
|
||
|
||
static gcry_err_code_t
|
||
pubkey_check_secret_key (int algorithm, gcry_mpi_t *skey)
|
||
{
|
||
gcry_err_code_t err = GPG_ERR_PUBKEY_ALGO;
|
||
gcry_module_t pubkey;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
pubkey = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (pubkey)
|
||
{
|
||
err = ((gcry_pk_spec_t *) pubkey->spec)->check_secret_key
|
||
(algorithm, skey);
|
||
_gcry_module_release (pubkey);
|
||
}
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
/****************
|
||
* This is the interface to the public key encryption. Encrypt DATA
|
||
* with PKEY and put it into RESARR which should be an array of MPIs
|
||
* of size PUBKEY_MAX_NENC (or less if the algorithm allows this -
|
||
* check with pubkey_get_nenc() )
|
||
*/
|
||
static gcry_err_code_t
|
||
pubkey_encrypt (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
|
||
gcry_mpi_t *pkey, int flags)
|
||
{
|
||
gcry_pk_spec_t *pubkey;
|
||
gcry_module_t module;
|
||
gcry_err_code_t rc;
|
||
int i;
|
||
|
||
/* Note: In fips mode DBG_CIPHER will enver evaluate to true but as
|
||
an extra failsafe protection we explicitly test for fips mode
|
||
here. */
|
||
if (DBG_CIPHER && !fips_mode ())
|
||
{
|
||
log_debug ("pubkey_encrypt: algo=%d\n", algorithm);
|
||
for(i = 0; i < pubkey_get_npkey (algorithm); i++)
|
||
log_mpidump (" pkey:", pkey[i]);
|
||
log_mpidump (" data:", data);
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
rc = pubkey->encrypt (algorithm, resarr, data, pkey, flags);
|
||
_gcry_module_release (module);
|
||
goto ready;
|
||
}
|
||
rc = GPG_ERR_PUBKEY_ALGO;
|
||
|
||
ready:
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
if (!rc && DBG_CIPHER && !fips_mode ())
|
||
{
|
||
for(i = 0; i < pubkey_get_nenc (algorithm); i++)
|
||
log_mpidump(" encr:", resarr[i] );
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
|
||
/****************
|
||
* This is the interface to the public key decryption.
|
||
* ALGO gives the algorithm to use and this implicitly determines
|
||
* the size of the arrays.
|
||
* result is a pointer to a mpi variable which will receive a
|
||
* newly allocated mpi or NULL in case of an error.
|
||
*/
|
||
static gcry_err_code_t
|
||
pubkey_decrypt (int algorithm, gcry_mpi_t *result, gcry_mpi_t *data,
|
||
gcry_mpi_t *skey, int flags)
|
||
{
|
||
gcry_pk_spec_t *pubkey;
|
||
gcry_module_t module;
|
||
gcry_err_code_t rc;
|
||
int i;
|
||
|
||
*result = NULL; /* so the caller can always do a mpi_free */
|
||
if (DBG_CIPHER && !fips_mode ())
|
||
{
|
||
log_debug ("pubkey_decrypt: algo=%d\n", algorithm);
|
||
for(i = 0; i < pubkey_get_nskey (algorithm); i++)
|
||
log_mpidump (" skey:", skey[i]);
|
||
for(i = 0; i < pubkey_get_nenc (algorithm); i++)
|
||
log_mpidump (" data:", data[i]);
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
rc = pubkey->decrypt (algorithm, result, data, skey, flags);
|
||
_gcry_module_release (module);
|
||
goto ready;
|
||
}
|
||
|
||
rc = GPG_ERR_PUBKEY_ALGO;
|
||
|
||
ready:
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
if (!rc && DBG_CIPHER && !fips_mode ())
|
||
log_mpidump (" plain:", *result);
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/****************
|
||
* This is the interface to the public key signing.
|
||
* Sign data with skey and put the result into resarr which
|
||
* should be an array of MPIs of size PUBKEY_MAX_NSIG (or less if the
|
||
* algorithm allows this - check with pubkey_get_nsig() )
|
||
*/
|
||
static gcry_err_code_t
|
||
pubkey_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
|
||
gcry_mpi_t *skey)
|
||
{
|
||
gcry_pk_spec_t *pubkey;
|
||
gcry_module_t module;
|
||
gcry_err_code_t rc;
|
||
int i;
|
||
|
||
if (DBG_CIPHER && !fips_mode ())
|
||
{
|
||
log_debug ("pubkey_sign: algo=%d\n", algorithm);
|
||
for(i = 0; i < pubkey_get_nskey (algorithm); i++)
|
||
log_mpidump (" skey:", skey[i]);
|
||
log_mpidump(" data:", data );
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
rc = pubkey->sign (algorithm, resarr, data, skey);
|
||
_gcry_module_release (module);
|
||
goto ready;
|
||
}
|
||
|
||
rc = GPG_ERR_PUBKEY_ALGO;
|
||
|
||
ready:
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
if (!rc && DBG_CIPHER && !fips_mode ())
|
||
for (i = 0; i < pubkey_get_nsig (algorithm); i++)
|
||
log_mpidump (" sig:", resarr[i]);
|
||
|
||
return rc;
|
||
}
|
||
|
||
/****************
|
||
* Verify a public key signature.
|
||
* Return 0 if the signature is good
|
||
*/
|
||
static gcry_err_code_t
|
||
pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
|
||
gcry_mpi_t *pkey,
|
||
int (*cmp)(void *, gcry_mpi_t), void *opaquev)
|
||
{
|
||
gcry_pk_spec_t *pubkey;
|
||
gcry_module_t module;
|
||
gcry_err_code_t rc;
|
||
int i;
|
||
|
||
if (DBG_CIPHER && !fips_mode ())
|
||
{
|
||
log_debug ("pubkey_verify: algo=%d\n", algorithm);
|
||
for (i = 0; i < pubkey_get_npkey (algorithm); i++)
|
||
log_mpidump (" pkey", pkey[i]);
|
||
for (i = 0; i < pubkey_get_nsig (algorithm); i++)
|
||
log_mpidump (" sig", data[i]);
|
||
log_mpidump (" hash", hash);
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = _gcry_module_lookup_id (pubkeys_registered, algorithm);
|
||
if (module)
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
rc = pubkey->verify (algorithm, hash, data, pkey, cmp, opaquev);
|
||
_gcry_module_release (module);
|
||
goto ready;
|
||
}
|
||
|
||
rc = GPG_ERR_PUBKEY_ALGO;
|
||
|
||
ready:
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* Turn VALUE into an octet string and store it in an allocated buffer
|
||
at R_FRAME or - if R_RAME is NULL - copy it into the caller
|
||
provided buffer SPACE; either SPACE or R_FRAME may be used. If
|
||
SPACE if not NULL, the caller must provide a buffer of at least
|
||
NBYTES. If the resulting octet string is shorter than NBYTES pad
|
||
it to the left with zeroes. If VALUE does not fit into NBYTES
|
||
return an error code. */
|
||
static gpg_err_code_t
|
||
octet_string_from_mpi (unsigned char **r_frame, void *space,
|
||
gcry_mpi_t value, size_t nbytes)
|
||
{
|
||
gpg_err_code_t rc;
|
||
size_t nframe, noff, n;
|
||
unsigned char *frame;
|
||
|
||
if (!r_frame == !space)
|
||
return GPG_ERR_INV_ARG; /* Only one may be used. */
|
||
|
||
if (r_frame)
|
||
*r_frame = NULL;
|
||
|
||
rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG,
|
||
NULL, 0, &nframe, value));
|
||
if (rc)
|
||
return rc;
|
||
if (nframe > nbytes)
|
||
return GPG_ERR_TOO_LARGE; /* Value too long to fit into NBYTES. */
|
||
|
||
noff = (nframe < nbytes)? nbytes - nframe : 0;
|
||
n = nframe + noff;
|
||
if (space)
|
||
frame = space;
|
||
else
|
||
{
|
||
frame = mpi_is_secure (value)? gcry_malloc_secure (n) : gcry_malloc (n);
|
||
if (!frame)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
return rc;
|
||
}
|
||
}
|
||
if (noff)
|
||
memset (frame, 0, noff);
|
||
nframe += noff;
|
||
rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG,
|
||
frame+noff, nframe-noff, NULL, value));
|
||
if (rc)
|
||
{
|
||
gcry_free (frame);
|
||
return rc;
|
||
}
|
||
|
||
if (r_frame)
|
||
*r_frame = frame;
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Encode {VALUE,VALUELEN} for an NBITS keys using the pkcs#1 block
|
||
type 2 padding. On sucess the result is stored as a new MPI at
|
||
R_RESULT. On error the value at R_RESULT is undefined.
|
||
|
||
If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as
|
||
the seed instead of using a random string for it. This feature is
|
||
only useful for regression tests. Note that this value may not
|
||
contain zero bytes.
|
||
|
||
We encode the value in this way:
|
||
|
||
0 2 RND(n bytes) 0 VALUE
|
||
|
||
0 is a marker we unfortunately can't encode because we return an
|
||
MPI which strips all leading zeroes.
|
||
2 is the block type.
|
||
RND are non-zero random bytes.
|
||
|
||
(Note that OpenPGP includes the cipher algorithm and a checksum in
|
||
VALUE; the caller needs to prepare the value accordingly.)
|
||
*/
|
||
static gcry_err_code_t
|
||
pkcs1_encode_for_encryption (gcry_mpi_t *r_result, unsigned int nbits,
|
||
const unsigned char *value, size_t valuelen,
|
||
const unsigned char *random_override,
|
||
size_t random_override_len)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
gcry_error_t err;
|
||
unsigned char *frame = NULL;
|
||
size_t nframe = (nbits+7) / 8;
|
||
int i;
|
||
size_t n;
|
||
unsigned char *p;
|
||
|
||
if (valuelen + 7 > nframe || !nframe)
|
||
{
|
||
/* Can't encode a VALUELEN value in a NFRAME bytes frame. */
|
||
return GPG_ERR_TOO_SHORT; /* The key is too short. */
|
||
}
|
||
|
||
if ( !(frame = gcry_malloc_secure (nframe)))
|
||
return gpg_err_code_from_syserror ();
|
||
|
||
n = 0;
|
||
frame[n++] = 0;
|
||
frame[n++] = 2; /* block type */
|
||
i = nframe - 3 - valuelen;
|
||
gcry_assert (i > 0);
|
||
|
||
if (random_override)
|
||
{
|
||
int j;
|
||
|
||
if (random_override_len != i)
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_INV_ARG;
|
||
}
|
||
/* Check that random does not include a zero byte. */
|
||
for (j=0; j < random_override_len; j++)
|
||
if (!random_override[j])
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_INV_ARG;
|
||
}
|
||
memcpy (frame + n, random_override, random_override_len);
|
||
n += random_override_len;
|
||
}
|
||
else
|
||
{
|
||
p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM);
|
||
/* Replace zero bytes by new values. */
|
||
for (;;)
|
||
{
|
||
int j, k;
|
||
unsigned char *pp;
|
||
|
||
/* Count the zero bytes. */
|
||
for (j=k=0; j < i; j++)
|
||
{
|
||
if (!p[j])
|
||
k++;
|
||
}
|
||
if (!k)
|
||
break; /* Okay: no (more) zero bytes. */
|
||
|
||
k += k/128 + 3; /* Better get some more. */
|
||
pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM);
|
||
for (j=0; j < i && k; )
|
||
{
|
||
if (!p[j])
|
||
p[j] = pp[--k];
|
||
if (p[j])
|
||
j++;
|
||
}
|
||
gcry_free (pp);
|
||
}
|
||
memcpy (frame+n, p, i);
|
||
n += i;
|
||
gcry_free (p);
|
||
}
|
||
|
||
frame[n++] = 0;
|
||
memcpy (frame+n, value, valuelen);
|
||
n += valuelen;
|
||
gcry_assert (n == nframe);
|
||
|
||
err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe);
|
||
if (err)
|
||
rc = gcry_err_code (err);
|
||
else if (DBG_CIPHER)
|
||
log_mpidump ("PKCS#1 block type 2 encoded data", *r_result);
|
||
gcry_free (frame);
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding.
|
||
NBITS is the size of the secret key. On success the result is
|
||
stored as a newly allocated buffer at R_RESULT and its valid length at
|
||
R_RESULTLEN. On error NULL is stored at R_RESULT. */
|
||
static gcry_err_code_t
|
||
pkcs1_decode_for_encryption (unsigned char **r_result, size_t *r_resultlen,
|
||
unsigned int nbits, gcry_mpi_t value)
|
||
{
|
||
gcry_error_t err;
|
||
unsigned char *frame = NULL;
|
||
size_t nframe = (nbits+7) / 8;
|
||
size_t n;
|
||
|
||
*r_result = NULL;
|
||
|
||
if ( !(frame = gcry_malloc_secure (nframe)))
|
||
return gpg_err_code_from_syserror ();
|
||
|
||
err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &n, value);
|
||
if (err)
|
||
{
|
||
gcry_free (frame);
|
||
return gcry_err_code (err);
|
||
}
|
||
|
||
nframe = n; /* Set NFRAME to the actual length. */
|
||
|
||
/* FRAME = 0x00 || 0x02 || PS || 0x00 || M
|
||
|
||
pkcs#1 requires that the first byte is zero. Our MPIs usually
|
||
strip leading zero bytes; thus we are not able to detect them.
|
||
However due to the way gcry_mpi_print is implemented we may see
|
||
leading zero bytes nevertheless. We handle this by making the
|
||
first zero byte optional. */
|
||
if (nframe < 4)
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_ENCODING_PROBLEM; /* Too short. */
|
||
}
|
||
n = 0;
|
||
if (!frame[0])
|
||
n++;
|
||
if (frame[n++] != 0x02)
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_ENCODING_PROBLEM; /* Wrong block type. */
|
||
}
|
||
|
||
/* Skip the non-zero random bytes and the terminating zero byte. */
|
||
for (; n < nframe && frame[n] != 0x00; n++)
|
||
;
|
||
if (n+1 >= nframe)
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_ENCODING_PROBLEM; /* No zero byte. */
|
||
}
|
||
n++; /* Skip the zero byte. */
|
||
|
||
/* To avoid an extra allocation we reuse the frame buffer. The only
|
||
caller of this function will anyway free the result soon. */
|
||
memmove (frame, frame + n, nframe - n);
|
||
*r_result = frame;
|
||
*r_resultlen = nframe - n;
|
||
|
||
if (DBG_CIPHER)
|
||
log_printhex ("value extracted from PKCS#1 block type 2 encoded data:",
|
||
*r_result, *r_resultlen);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Encode {VALUE,VALUELEN} for an NBITS keys and hash algorith ALGO
|
||
using the pkcs#1 block type 1 padding. On success the result is
|
||
stored as a new MPI at R_RESULT. On error the value at R_RESULT is
|
||
undefined.
|
||
|
||
We encode the value in this way:
|
||
|
||
0 1 PAD(n bytes) 0 ASN(asnlen bytes) VALUE(valuelen bytes)
|
||
|
||
0 is a marker we unfortunately can't encode because we return an
|
||
MPI which strips all leading zeroes.
|
||
1 is the block type.
|
||
PAD consists of 0xff bytes.
|
||
0 marks the end of the padding.
|
||
ASN is the DER encoding of the hash algorithm; along with the VALUE
|
||
it yields a valid DER encoding.
|
||
|
||
(Note that PGP prior to version 2.3 encoded the message digest as:
|
||
0 1 MD(16 bytes) 0 PAD(n bytes) 1
|
||
The MD is always 16 bytes here because it's always MD5. GnuPG
|
||
does not not support pre-v2.3 signatures, but I'm including this
|
||
comment so the information is easily found if needed.)
|
||
*/
|
||
static gcry_err_code_t
|
||
pkcs1_encode_for_signature (gcry_mpi_t *r_result, unsigned int nbits,
|
||
const unsigned char *value, size_t valuelen,
|
||
int algo)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
gcry_error_t err;
|
||
byte asn[100];
|
||
byte *frame = NULL;
|
||
size_t nframe = (nbits+7) / 8;
|
||
int i;
|
||
size_t n;
|
||
size_t asnlen, dlen;
|
||
|
||
asnlen = DIM(asn);
|
||
dlen = gcry_md_get_algo_dlen (algo);
|
||
|
||
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
|
||
{
|
||
/* We don't have yet all of the above algorithms. */
|
||
return GPG_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
if ( valuelen != dlen )
|
||
{
|
||
/* Hash value does not match the length of digest for
|
||
the given algorithm. */
|
||
return GPG_ERR_CONFLICT;
|
||
}
|
||
|
||
if ( !dlen || dlen + asnlen + 4 > nframe)
|
||
{
|
||
/* Can't encode an DLEN byte digest MD into an NFRAME byte
|
||
frame. */
|
||
return GPG_ERR_TOO_SHORT;
|
||
}
|
||
|
||
if ( !(frame = gcry_malloc (nframe)) )
|
||
return gpg_err_code_from_syserror ();
|
||
|
||
/* Assemble the pkcs#1 block type 1. */
|
||
n = 0;
|
||
frame[n++] = 0;
|
||
frame[n++] = 1; /* block type */
|
||
i = nframe - valuelen - asnlen - 3 ;
|
||
gcry_assert (i > 1);
|
||
memset (frame+n, 0xff, i );
|
||
n += i;
|
||
frame[n++] = 0;
|
||
memcpy (frame+n, asn, asnlen);
|
||
n += asnlen;
|
||
memcpy (frame+n, value, valuelen );
|
||
n += valuelen;
|
||
gcry_assert (n == nframe);
|
||
|
||
/* Convert it into an MPI. */
|
||
err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe);
|
||
if (err)
|
||
rc = gcry_err_code (err);
|
||
else if (DBG_CIPHER)
|
||
log_mpidump ("PKCS#1 block type 1 encoded data", *r_result);
|
||
gcry_free (frame);
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* Mask generation function for OAEP. See RFC-3447 B.2.1. */
|
||
static gcry_err_code_t
|
||
mgf1 (unsigned char *output, size_t outlen, unsigned char *seed, size_t seedlen,
|
||
int algo)
|
||
{
|
||
size_t dlen, nbytes, n;
|
||
int idx;
|
||
gcry_md_hd_t hd;
|
||
gcry_error_t err;
|
||
|
||
err = gcry_md_open (&hd, algo, 0);
|
||
if (err)
|
||
return gpg_err_code (err);
|
||
|
||
dlen = gcry_md_get_algo_dlen (algo);
|
||
|
||
/* We skip step 1 which would be assert(OUTLEN <= 2^32). The loop
|
||
in step 3 is merged with step 4 by concatenating no more octets
|
||
than what would fit into OUTPUT. The ceiling for the counter IDX
|
||
is implemented indirectly. */
|
||
nbytes = 0; /* Step 2. */
|
||
idx = 0;
|
||
while ( nbytes < outlen )
|
||
{
|
||
unsigned char c[4], *digest;
|
||
|
||
if (idx)
|
||
gcry_md_reset (hd);
|
||
|
||
c[0] = (idx >> 24) & 0xFF;
|
||
c[1] = (idx >> 16) & 0xFF;
|
||
c[2] = (idx >> 8) & 0xFF;
|
||
c[3] = idx & 0xFF;
|
||
idx++;
|
||
|
||
gcry_md_write (hd, seed, seedlen);
|
||
gcry_md_write (hd, c, 4);
|
||
digest = gcry_md_read (hd, 0);
|
||
|
||
n = (outlen - nbytes < dlen)? (outlen - nbytes) : dlen;
|
||
memcpy (output+nbytes, digest, n);
|
||
nbytes += n;
|
||
}
|
||
|
||
gcry_md_close (hd);
|
||
return GPG_ERR_NO_ERROR;
|
||
}
|
||
|
||
|
||
/* RFC-3447 (pkcs#1 v2.1) OAEP encoding. NBITS is the length of the
|
||
key measured in bits. ALGO is the hash function; it must be a
|
||
valid and usable algorithm. {VALUE,VALUELEN} is the message to
|
||
encrypt. {LABEL,LABELLEN} is the optional label to be associated
|
||
with the message, if LABEL is NULL the default is to use the empty
|
||
string as label. On success the encoded ciphertext is returned at
|
||
R_RESULT.
|
||
|
||
If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as
|
||
the seed instead of using a random string for it. This feature is
|
||
only useful for regression tests.
|
||
|
||
Here is figure 1 from the RFC depicting the process:
|
||
|
||
+----------+---------+-------+
|
||
DB = | lHash | PS | M |
|
||
+----------+---------+-------+
|
||
|
|
||
+----------+ V
|
||
| seed |--> MGF ---> xor
|
||
+----------+ |
|
||
| |
|
||
+--+ V |
|
||
|00| xor <----- MGF <-----|
|
||
+--+ | |
|
||
| | |
|
||
V V V
|
||
+--+----------+----------------------------+
|
||
EM = |00|maskedSeed| maskedDB |
|
||
+--+----------+----------------------------+
|
||
*/
|
||
static gcry_err_code_t
|
||
oaep_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo,
|
||
const unsigned char *value, size_t valuelen,
|
||
const unsigned char *label, size_t labellen,
|
||
const void *random_override, size_t random_override_len)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
gcry_error_t err;
|
||
unsigned char *frame = NULL;
|
||
size_t nframe = (nbits+7) / 8;
|
||
unsigned char *p;
|
||
size_t hlen;
|
||
size_t n;
|
||
|
||
*r_result = NULL;
|
||
|
||
/* Set defaults for LABEL. */
|
||
if (!label || !labellen)
|
||
{
|
||
label = (const unsigned char*)"";
|
||
labellen = 0;
|
||
}
|
||
|
||
hlen = gcry_md_get_algo_dlen (algo);
|
||
|
||
/* We skip step 1a which would be to check that LABELLEN is not
|
||
greater than 2^61-1. See rfc-3447 7.1.1. */
|
||
|
||
/* Step 1b. Note that the obsolete rfc-2437 uses the check:
|
||
valuelen > nframe - 2 * hlen - 1 . */
|
||
if (valuelen > nframe - 2 * hlen - 2 || !nframe)
|
||
{
|
||
/* Can't encode a VALUELEN value in a NFRAME bytes frame. */
|
||
return GPG_ERR_TOO_SHORT; /* The key is too short. */
|
||
}
|
||
|
||
/* Allocate the frame. */
|
||
frame = gcry_calloc_secure (1, nframe);
|
||
if (!frame)
|
||
return gpg_err_code_from_syserror ();
|
||
|
||
/* Step 2a: Compute the hash of the label. We store it in the frame
|
||
where later the maskedDB will commence. */
|
||
gcry_md_hash_buffer (algo, frame + 1 + hlen, label, labellen);
|
||
|
||
/* Step 2b: Set octet string to zero. */
|
||
/* This has already been done while allocating FRAME. */
|
||
|
||
/* Step 2c: Create DB by concatenating lHash, PS, 0x01 and M. */
|
||
n = nframe - valuelen - 1;
|
||
frame[n] = 0x01;
|
||
memcpy (frame + n + 1, value, valuelen);
|
||
|
||
/* Step 3d: Generate seed. We store it where the maskedSeed will go
|
||
later. */
|
||
if (random_override)
|
||
{
|
||
if (random_override_len != hlen)
|
||
{
|
||
gcry_free (frame);
|
||
return GPG_ERR_INV_ARG;
|
||
}
|
||
memcpy (frame + 1, random_override, hlen);
|
||
}
|
||
else
|
||
gcry_randomize (frame + 1, hlen, GCRY_STRONG_RANDOM);
|
||
|
||
/* Step 2e and 2f: Create maskedDB. */
|
||
{
|
||
unsigned char *dmask;
|
||
|
||
dmask = gcry_malloc_secure (nframe - hlen - 1);
|
||
if (!dmask)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
gcry_free (frame);
|
||
return rc;
|
||
}
|
||
rc = mgf1 (dmask, nframe - hlen - 1, frame+1, hlen, algo);
|
||
if (rc)
|
||
{
|
||
gcry_free (dmask);
|
||
gcry_free (frame);
|
||
return rc;
|
||
}
|
||
for (n = 1 + hlen, p = dmask; n < nframe; n++)
|
||
frame[n] ^= *p++;
|
||
gcry_free (dmask);
|
||
}
|
||
|
||
/* Step 2g and 2h: Create maskedSeed. */
|
||
{
|
||
unsigned char *smask;
|
||
|
||
smask = gcry_malloc_secure (hlen);
|
||
if (!smask)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
gcry_free (frame);
|
||
return rc;
|
||
}
|
||
rc = mgf1 (smask, hlen, frame + 1 + hlen, nframe - hlen - 1, algo);
|
||
if (rc)
|
||
{
|
||
gcry_free (smask);
|
||
gcry_free (frame);
|
||
return rc;
|
||
}
|
||
for (n = 1, p = smask; n < 1 + hlen; n++)
|
||
frame[n] ^= *p++;
|
||
gcry_free (smask);
|
||
}
|
||
|
||
/* Step 2i: Concatenate 0x00, maskedSeed and maskedDB. */
|
||
/* This has already been done by using in-place operations. */
|
||
|
||
/* Convert the stuff into an MPI as expected by the caller. */
|
||
err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, nframe, NULL);
|
||
if (err)
|
||
rc = gcry_err_code (err);
|
||
else if (DBG_CIPHER)
|
||
log_mpidump ("OAEP encoded data", *r_result);
|
||
gcry_free (frame);
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* RFC-3447 (pkcs#1 v2.1) OAEP decoding. NBITS is the length of the
|
||
key measured in bits. ALGO is the hash function; it must be a
|
||
valid and usable algorithm. VALUE is the raw decrypted message
|
||
{LABEL,LABELLEN} is the optional label to be associated with the
|
||
message, if LABEL is NULL the default is to use the empty string as
|
||
label. On success the plaintext is returned as a newly allocated
|
||
buffer at R_RESULT; its valid length is stored at R_RESULTLEN. On
|
||
error NULL is stored at R_RESULT. */
|
||
static gcry_err_code_t
|
||
oaep_decode (unsigned char **r_result, size_t *r_resultlen,
|
||
unsigned int nbits, int algo,
|
||
gcry_mpi_t value, const unsigned char *label, size_t labellen)
|
||
{
|
||
gcry_err_code_t rc;
|
||
unsigned char *frame = NULL; /* Encoded messages (EM). */
|
||
unsigned char *masked_seed; /* Points into FRAME. */
|
||
unsigned char *masked_db; /* Points into FRAME. */
|
||
unsigned char *seed = NULL; /* Allocated space for the seed and DB. */
|
||
unsigned char *db; /* Points into SEED. */
|
||
unsigned char *lhash = NULL; /* Hash of the label. */
|
||
size_t nframe; /* Length of the ciphertext (EM). */
|
||
size_t hlen; /* Length of the hash digest. */
|
||
size_t db_len; /* Length of DB and masked_db. */
|
||
size_t nkey = (nbits+7)/8; /* Length of the key in bytes. */
|
||
int failed = 0; /* Error indicator. */
|
||
size_t n;
|
||
|
||
*r_result = NULL;
|
||
|
||
/* This code is implemented as described by rfc-3447 7.1.2. */
|
||
|
||
/* Set defaults for LABEL. */
|
||
if (!label || !labellen)
|
||
{
|
||
label = (const unsigned char*)"";
|
||
labellen = 0;
|
||
}
|
||
|
||
/* Get the length of the digest. */
|
||
hlen = gcry_md_get_algo_dlen (algo);
|
||
|
||
/* Hash the label right away. */
|
||
lhash = gcry_malloc (hlen);
|
||
if (!lhash)
|
||
return gpg_err_code_from_syserror ();
|
||
gcry_md_hash_buffer (algo, lhash, label, labellen);
|
||
|
||
/* Turn the MPI into an octet string. If the octet string is
|
||
shorter than the key we pad it to the left with zeroes. This may
|
||
happen due to the leading zero in OAEP frames and due to the
|
||
following random octets (seed^mask) which may have leading zero
|
||
bytes. This all is needed to cope with our leading zeroes
|
||
suppressing MPI implementation. The code implictly implements
|
||
Step 1b (bail out if NFRAME != N). */
|
||
rc = octet_string_from_mpi (&frame, NULL, value, nkey);
|
||
if (rc)
|
||
{
|
||
gcry_free (lhash);
|
||
return GPG_ERR_ENCODING_PROBLEM;
|
||
}
|
||
nframe = nkey;
|
||
|
||
/* Step 1c: Check that the key is long enough. */
|
||
if ( nframe < 2 * hlen + 2 )
|
||
{
|
||
gcry_free (frame);
|
||
gcry_free (lhash);
|
||
return GPG_ERR_ENCODING_PROBLEM;
|
||
}
|
||
|
||
/* Step 2 has already been done by the caller and the
|
||
gcry_mpi_aprint above. */
|
||
|
||
/* Allocate space for SEED and DB. */
|
||
seed = gcry_malloc_secure (nframe - 1);
|
||
if (!seed)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
gcry_free (frame);
|
||
gcry_free (lhash);
|
||
return rc;
|
||
}
|
||
db = seed + hlen;
|
||
|
||
/* To avoid choosen ciphertext attacks from now on we make sure to
|
||
run all code even in the error case; this avoids possible timing
|
||
attacks as described by Manger. */
|
||
|
||
/* Step 3a: Hash the label. */
|
||
/* This has already been done. */
|
||
|
||
/* Step 3b: Separate the encoded message. */
|
||
masked_seed = frame + 1;
|
||
masked_db = frame + 1 + hlen;
|
||
db_len = nframe - 1 - hlen;
|
||
|
||
/* Step 3c and 3d: seed = maskedSeed ^ mgf(maskedDB, hlen). */
|
||
if (mgf1 (seed, hlen, masked_db, db_len, algo))
|
||
failed = 1;
|
||
for (n = 0; n < hlen; n++)
|
||
seed[n] ^= masked_seed[n];
|
||
|
||
/* Step 3e and 3f: db = maskedDB ^ mgf(seed, db_len). */
|
||
if (mgf1 (db, db_len, seed, hlen, algo))
|
||
failed = 1;
|
||
for (n = 0; n < db_len; n++)
|
||
db[n] ^= masked_db[n];
|
||
|
||
/* Step 3g: Check lhash, an possible empty padding string terminated
|
||
by 0x01 and the first byte of EM being 0. */
|
||
if (memcmp (lhash, db, hlen))
|
||
failed = 1;
|
||
for (n = hlen; n < db_len; n++)
|
||
if (db[n] == 0x01)
|
||
break;
|
||
if (n == db_len)
|
||
failed = 1;
|
||
if (frame[0])
|
||
failed = 1;
|
||
|
||
gcry_free (lhash);
|
||
gcry_free (frame);
|
||
if (failed)
|
||
{
|
||
gcry_free (seed);
|
||
return GPG_ERR_ENCODING_PROBLEM;
|
||
}
|
||
|
||
/* Step 4: Output M. */
|
||
/* To avoid an extra allocation we reuse the seed buffer. The only
|
||
caller of this function will anyway free the result soon. */
|
||
n++;
|
||
memmove (seed, db + n, db_len - n);
|
||
*r_result = seed;
|
||
*r_resultlen = db_len - n;
|
||
seed = NULL;
|
||
|
||
if (DBG_CIPHER)
|
||
log_printhex ("value extracted from OAEP encoded data:",
|
||
*r_result, *r_resultlen);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* RFC-3447 (pkcs#1 v2.1) PSS encoding. Encode {VALUE,VALUELEN} for
|
||
an NBITS key. Note that VALUE is already the mHash from the
|
||
picture below. ALGO is a valid hash algorithm and SALTLEN is the
|
||
length of salt to be used. On success the result is stored as a
|
||
new MPI at R_RESULT. On error the value at R_RESULT is undefined.
|
||
|
||
If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as
|
||
the salt instead of using a random string for the salt. This
|
||
feature is only useful for regression tests.
|
||
|
||
Here is figure 2 from the RFC (errata 595 applied) depicting the
|
||
process:
|
||
|
||
+-----------+
|
||
| M |
|
||
+-----------+
|
||
|
|
||
V
|
||
Hash
|
||
|
|
||
V
|
||
+--------+----------+----------+
|
||
M' = |Padding1| mHash | salt |
|
||
+--------+----------+----------+
|
||
|
|
||
+--------+----------+ V
|
||
DB = |Padding2| salt | Hash
|
||
+--------+----------+ |
|
||
| |
|
||
V | +----+
|
||
xor <--- MGF <---| |0xbc|
|
||
| | +----+
|
||
| | |
|
||
V V V
|
||
+-------------------+----------+----+
|
||
EM = | maskedDB | H |0xbc|
|
||
+-------------------+----------+----+
|
||
|
||
*/
|
||
static gcry_err_code_t
|
||
pss_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo,
|
||
const unsigned char *value, size_t valuelen, int saltlen,
|
||
const void *random_override, size_t random_override_len)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
gcry_error_t err;
|
||
size_t hlen; /* Length of the hash digest. */
|
||
unsigned char *em = NULL; /* Encoded message. */
|
||
size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */
|
||
unsigned char *h; /* Points into EM. */
|
||
unsigned char *buf = NULL; /* Help buffer. */
|
||
size_t buflen; /* Length of BUF. */
|
||
unsigned char *mhash; /* Points into BUF. */
|
||
unsigned char *salt; /* Points into BUF. */
|
||
unsigned char *dbmask; /* Points into BUF. */
|
||
unsigned char *p;
|
||
size_t n;
|
||
|
||
/* This code is implemented as described by rfc-3447 9.1.1. */
|
||
|
||
/* Get the length of the digest. */
|
||
hlen = gcry_md_get_algo_dlen (algo);
|
||
gcry_assert (hlen); /* We expect a valid ALGO here. */
|
||
|
||
/* Allocate a help buffer and setup some pointers. */
|
||
buflen = 8 + hlen + saltlen + (emlen - hlen - 1);
|
||
buf = gcry_malloc (buflen);
|
||
if (!buf)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
goto leave;
|
||
}
|
||
mhash = buf + 8;
|
||
salt = mhash + hlen;
|
||
dbmask= salt + saltlen;
|
||
|
||
/* Step 2: That would be: mHash = Hash(M) but our input is already
|
||
mHash thus we do only a consistency check and copy to MHASH. */
|
||
if (valuelen != hlen)
|
||
{
|
||
rc = GPG_ERR_INV_LENGTH;
|
||
goto leave;
|
||
}
|
||
memcpy (mhash, value, hlen);
|
||
|
||
/* Step 3: Check length constraints. */
|
||
if (emlen < hlen + saltlen + 2)
|
||
{
|
||
rc = GPG_ERR_TOO_SHORT;
|
||
goto leave;
|
||
}
|
||
|
||
/* Allocate space for EM. */
|
||
em = gcry_malloc (emlen);
|
||
if (!em)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
goto leave;
|
||
}
|
||
h = em + emlen - 1 - hlen;
|
||
|
||
/* Step 4: Create a salt. */
|
||
if (saltlen)
|
||
{
|
||
if (random_override)
|
||
{
|
||
if (random_override_len != saltlen)
|
||
{
|
||
rc = GPG_ERR_INV_ARG;
|
||
goto leave;
|
||
}
|
||
memcpy (salt, random_override, saltlen);
|
||
}
|
||
else
|
||
gcry_randomize (salt, saltlen, GCRY_STRONG_RANDOM);
|
||
}
|
||
|
||
/* Step 5 and 6: M' = Hash(Padding1 || mHash || salt). */
|
||
memset (buf, 0, 8); /* Padding. */
|
||
gcry_md_hash_buffer (algo, h, buf, 8 + hlen + saltlen);
|
||
|
||
/* Step 7 and 8: DB = PS || 0x01 || salt. */
|
||
/* Note that we use EM to store DB and later Xor in-place. */
|
||
p = em + emlen - 1 - hlen - saltlen - 1;
|
||
memset (em, 0, p - em);
|
||
*p++ = 0x01;
|
||
memcpy (p, salt, saltlen);
|
||
|
||
/* Step 9: dbmask = MGF(H, emlen - hlen - 1). */
|
||
mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo);
|
||
|
||
/* Step 10: maskedDB = DB ^ dbMask */
|
||
for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++)
|
||
em[n] ^= *p;
|
||
|
||
/* Step 11: Set the leftmost bits to zero. */
|
||
em[0] &= 0xFF >> (8 * emlen - nbits);
|
||
|
||
/* Step 12: EM = maskedDB || H || 0xbc. */
|
||
em[emlen-1] = 0xbc;
|
||
|
||
/* Convert EM into an MPI. */
|
||
err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, em, emlen, NULL);
|
||
if (err)
|
||
rc = gcry_err_code (err);
|
||
else if (DBG_CIPHER)
|
||
log_mpidump ("PSS encoded data", *r_result);
|
||
|
||
leave:
|
||
if (em)
|
||
{
|
||
wipememory (em, emlen);
|
||
gcry_free (em);
|
||
}
|
||
if (buf)
|
||
{
|
||
wipememory (buf, buflen);
|
||
gcry_free (buf);
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* Verify a signature assuming PSS padding. VALUE is the hash of the
|
||
message (mHash) encoded as an MPI; its length must match the digest
|
||
length of ALGO. ENCODED is the output of the RSA public key
|
||
function (EM). NBITS is the size of the public key. ALGO is the
|
||
hash algorithm and SALTLEN is the length of the used salt. The
|
||
function returns 0 on success or on error code. */
|
||
static gcry_err_code_t
|
||
pss_verify (gcry_mpi_t value, gcry_mpi_t encoded, unsigned int nbits, int algo,
|
||
size_t saltlen)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
size_t hlen; /* Length of the hash digest. */
|
||
unsigned char *em = NULL; /* Encoded message. */
|
||
size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */
|
||
unsigned char *salt; /* Points into EM. */
|
||
unsigned char *h; /* Points into EM. */
|
||
unsigned char *buf = NULL; /* Help buffer. */
|
||
size_t buflen; /* Length of BUF. */
|
||
unsigned char *dbmask; /* Points into BUF. */
|
||
unsigned char *mhash; /* Points into BUF. */
|
||
unsigned char *p;
|
||
size_t n;
|
||
|
||
/* This code is implemented as described by rfc-3447 9.1.2. */
|
||
|
||
/* Get the length of the digest. */
|
||
hlen = gcry_md_get_algo_dlen (algo);
|
||
gcry_assert (hlen); /* We expect a valid ALGO here. */
|
||
|
||
/* Allocate a help buffer and setup some pointers.
|
||
This buffer is used for two purposes:
|
||
+------------------------------+-------+
|
||
1. | dbmask | mHash |
|
||
+------------------------------+-------+
|
||
emlen - hlen - 1 hlen
|
||
|
||
+----------+-------+---------+-+-------+
|
||
2. | padding1 | mHash | salt | | mHash |
|
||
+----------+-------+---------+-+-------+
|
||
8 hlen saltlen hlen
|
||
*/
|
||
buflen = 8 + hlen + saltlen;
|
||
if (buflen < emlen - hlen - 1)
|
||
buflen = emlen - hlen - 1;
|
||
buflen += hlen;
|
||
buf = gcry_malloc (buflen);
|
||
if (!buf)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
goto leave;
|
||
}
|
||
dbmask = buf;
|
||
mhash = buf + buflen - hlen;
|
||
|
||
/* Step 2: That would be: mHash = Hash(M) but our input is already
|
||
mHash thus we only need to convert VALUE into MHASH. */
|
||
rc = octet_string_from_mpi (NULL, mhash, value, hlen);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
/* Convert the signature into an octet string. */
|
||
rc = octet_string_from_mpi (&em, NULL, encoded, emlen);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
/* Step 3: Check length of EM. Because we internally use MPI
|
||
functions we can't do this properly; EMLEN is always the length
|
||
of the key because octet_string_from_mpi needs to left pad the
|
||
result with zero to cope with the fact that our MPIs suppress all
|
||
leading zeroes. Thus what we test here are merely the digest and
|
||
salt lengths to the key. */
|
||
if (emlen < hlen + saltlen + 2)
|
||
{
|
||
rc = GPG_ERR_TOO_SHORT; /* For the hash and saltlen. */
|
||
goto leave;
|
||
}
|
||
|
||
/* Step 4: Check last octet. */
|
||
if (em[emlen - 1] != 0xbc)
|
||
{
|
||
rc = GPG_ERR_BAD_SIGNATURE;
|
||
goto leave;
|
||
}
|
||
|
||
/* Step 5: Split EM. */
|
||
h = em + emlen - 1 - hlen;
|
||
|
||
/* Step 6: Check the leftmost bits. */
|
||
if ((em[0] & ~(0xFF >> (8 * emlen - nbits))))
|
||
{
|
||
rc = GPG_ERR_BAD_SIGNATURE;
|
||
goto leave;
|
||
}
|
||
|
||
/* Step 7: dbmask = MGF(H, emlen - hlen - 1). */
|
||
mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo);
|
||
|
||
/* Step 8: maskedDB = DB ^ dbMask. */
|
||
for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++)
|
||
em[n] ^= *p;
|
||
|
||
/* Step 9: Set leftmost bits in DB to zero. */
|
||
em[0] &= 0xFF >> (8 * emlen - nbits);
|
||
|
||
/* Step 10: Check the padding of DB. */
|
||
for (n = 0; n < emlen - hlen - saltlen - 2 && !em[n]; n++)
|
||
;
|
||
if (n != emlen - hlen - saltlen - 2 || em[n++] != 1)
|
||
{
|
||
rc = GPG_ERR_BAD_SIGNATURE;
|
||
goto leave;
|
||
}
|
||
|
||
/* Step 11: Extract salt from DB. */
|
||
salt = em + n;
|
||
|
||
/* Step 12: M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
|
||
memset (buf, 0, 8);
|
||
memcpy (buf+8, mhash, hlen);
|
||
memcpy (buf+8+hlen, salt, saltlen);
|
||
|
||
/* Step 13: H' = Hash(M'). */
|
||
gcry_md_hash_buffer (algo, buf, buf, 8 + hlen + saltlen);
|
||
|
||
/* Step 14: Check H == H'. */
|
||
rc = memcmp (h, buf, hlen) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR;
|
||
|
||
leave:
|
||
if (em)
|
||
{
|
||
wipememory (em, emlen);
|
||
gcry_free (em);
|
||
}
|
||
if (buf)
|
||
{
|
||
wipememory (buf, buflen);
|
||
gcry_free (buf);
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
|
||
/* Callback for the pubkey algorithm code to verify PSS signatures.
|
||
OPAQUE is the data provided by the actual caller. The meaning of
|
||
TMP depends on the actual algorithm (but there is only RSA); now
|
||
for RSA it is the output of running the public key function on the
|
||
input. */
|
||
static int
|
||
pss_verify_cmp (void *opaque, gcry_mpi_t tmp)
|
||
{
|
||
struct pk_encoding_ctx *ctx = opaque;
|
||
gcry_mpi_t hash = ctx->verify_arg;
|
||
|
||
return pss_verify (hash, tmp, ctx->nbits - 1, ctx->hash_algo, ctx->saltlen);
|
||
}
|
||
|
||
|
||
/* Internal function. */
|
||
static gcry_err_code_t
|
||
sexp_elements_extract (gcry_sexp_t key_sexp, const char *element_names,
|
||
gcry_mpi_t *elements, const char *algo_name)
|
||
{
|
||
gcry_err_code_t err = 0;
|
||
int i, idx;
|
||
const char *name;
|
||
gcry_sexp_t list;
|
||
|
||
for (name = element_names, idx = 0; *name && !err; name++, idx++)
|
||
{
|
||
list = gcry_sexp_find_token (key_sexp, name, 1);
|
||
if (!list)
|
||
elements[idx] = NULL;
|
||
else
|
||
{
|
||
elements[idx] = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG);
|
||
gcry_sexp_release (list);
|
||
if (!elements[idx])
|
||
err = GPG_ERR_INV_OBJ;
|
||
}
|
||
}
|
||
|
||
if (!err)
|
||
{
|
||
/* Check that all elements are available. */
|
||
for (name = element_names, idx = 0; *name; name++, idx++)
|
||
if (!elements[idx])
|
||
break;
|
||
if (*name)
|
||
{
|
||
err = GPG_ERR_NO_OBJ;
|
||
/* Some are missing. Before bailing out we test for
|
||
optional parameters. */
|
||
if (algo_name && !strcmp (algo_name, "RSA")
|
||
&& !strcmp (element_names, "nedpqu") )
|
||
{
|
||
/* This is RSA. Test whether we got N, E and D and that
|
||
the optional P, Q and U are all missing. */
|
||
if (elements[0] && elements[1] && elements[2]
|
||
&& !elements[3] && !elements[4] && !elements[5])
|
||
err = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (err)
|
||
{
|
||
for (i = 0; i < idx; i++)
|
||
if (elements[i])
|
||
gcry_free (elements[i]);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
/* Internal function used for ecc. Note, that this function makes use
|
||
of its intimate knowledge about the ECC parameters from ecc.c. */
|
||
static gcry_err_code_t
|
||
sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names,
|
||
gcry_mpi_t *elements, pk_extra_spec_t *extraspec)
|
||
|
||
{
|
||
gcry_err_code_t err = 0;
|
||
int idx;
|
||
const char *name;
|
||
gcry_sexp_t list;
|
||
|
||
/* Clear the array for easier error cleanup. */
|
||
for (name = element_names, idx = 0; *name; name++, idx++)
|
||
elements[idx] = NULL;
|
||
gcry_assert (idx >= 5); /* We know that ECC has at least 5 elements
|
||
(params only) or 6 (full public key). */
|
||
if (idx == 5)
|
||
elements[5] = NULL; /* Extra clear for the params only case. */
|
||
|
||
|
||
/* Init the array with the available curve parameters. */
|
||
for (name = element_names, idx = 0; *name && !err; name++, idx++)
|
||
{
|
||
list = gcry_sexp_find_token (key_sexp, name, 1);
|
||
if (!list)
|
||
elements[idx] = NULL;
|
||
else
|
||
{
|
||
elements[idx] = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG);
|
||
gcry_sexp_release (list);
|
||
if (!elements[idx])
|
||
{
|
||
err = GPG_ERR_INV_OBJ;
|
||
goto leave;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Check whether a curve parameter has been given and then fill any
|
||
missing elements. */
|
||
list = gcry_sexp_find_token (key_sexp, "curve", 5);
|
||
if (list)
|
||
{
|
||
if (extraspec->get_param)
|
||
{
|
||
char *curve;
|
||
gcry_mpi_t params[6];
|
||
|
||
for (idx = 0; idx < DIM(params); idx++)
|
||
params[idx] = NULL;
|
||
|
||
curve = _gcry_sexp_nth_string (list, 1);
|
||
gcry_sexp_release (list);
|
||
if (!curve)
|
||
{
|
||
/* No curve name given (or out of core). */
|
||
err = GPG_ERR_INV_OBJ;
|
||
goto leave;
|
||
}
|
||
err = extraspec->get_param (curve, params);
|
||
gcry_free (curve);
|
||
if (err)
|
||
goto leave;
|
||
|
||
for (idx = 0; idx < DIM(params); idx++)
|
||
{
|
||
if (!elements[idx])
|
||
elements[idx] = params[idx];
|
||
else
|
||
mpi_free (params[idx]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gcry_sexp_release (list);
|
||
err = GPG_ERR_INV_OBJ; /* "curve" given but ECC not supported. */
|
||
goto leave;
|
||
}
|
||
}
|
||
|
||
/* Check that all parameters are known. */
|
||
for (name = element_names, idx = 0; *name; name++, idx++)
|
||
if (!elements[idx])
|
||
{
|
||
err = GPG_ERR_NO_OBJ;
|
||
goto leave;
|
||
}
|
||
|
||
leave:
|
||
if (err)
|
||
{
|
||
for (name = element_names, idx = 0; *name; name++, idx++)
|
||
if (elements[idx])
|
||
gcry_free (elements[idx]);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
/****************
|
||
* Convert a S-Exp with either a private or a public key to our
|
||
* internal format. Currently we do only support the following
|
||
* algorithms:
|
||
* dsa
|
||
* rsa
|
||
* openpgp-dsa
|
||
* openpgp-rsa
|
||
* openpgp-elg
|
||
* openpgp-elg-sig
|
||
* ecdsa
|
||
* ecdh
|
||
* Provide a SE with the first element be either "private-key" or
|
||
* or "public-key". It is followed by a list with its first element
|
||
* be one of the above algorithm identifiers and the remaning
|
||
* elements are pairs with parameter-id and value.
|
||
* NOTE: we look through the list to find a list beginning with
|
||
* "private-key" or "public-key" - the first one found is used.
|
||
*
|
||
* If OVERRIDE_ELEMS is not NULL those elems override the parameter
|
||
* specification taken from the module. This ise used by
|
||
* gcry_pk_get_curve.
|
||
*
|
||
* Returns: A pointer to an allocated array of MPIs if the return value is
|
||
* zero; the caller has to release this array.
|
||
*
|
||
* Example of a DSA public key:
|
||
* (private-key
|
||
* (dsa
|
||
* (p <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, const char *override_elems,
|
||
gcry_mpi_t **retarray, gcry_module_t *retalgo)
|
||
{
|
||
gcry_err_code_t err = 0;
|
||
gcry_sexp_t list, l2;
|
||
char *name;
|
||
const char *elems;
|
||
gcry_mpi_t *array;
|
||
gcry_module_t module;
|
||
gcry_pk_spec_t *pubkey;
|
||
pk_extra_spec_t *extraspec;
|
||
int is_ecc;
|
||
|
||
/* Check that the first element is valid. */
|
||
list = gcry_sexp_find_token (sexp,
|
||
want_private? "private-key":"public-key", 0);
|
||
if (!list)
|
||
return GPG_ERR_INV_OBJ; /* Does not contain a key object. */
|
||
|
||
l2 = gcry_sexp_cadr( list );
|
||
gcry_sexp_release ( list );
|
||
list = l2;
|
||
name = _gcry_sexp_nth_string (list, 0);
|
||
if (!name)
|
||
{
|
||
gcry_sexp_release ( list );
|
||
return GPG_ERR_INV_OBJ; /* Invalid structure of object. */
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = gcry_pk_lookup_name (name);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
/* Fixme: We should make sure that an ECC key is always named "ecc"
|
||
and not "ecdsa". "ecdsa" should be used for the signature
|
||
itself. We need a function to test whether an algorithm given
|
||
with a key is compatible with an application of the key (signing,
|
||
encryption). For RSA this is easy, but ECC is the first
|
||
algorithm which has many flavours. */
|
||
is_ecc = ( !strcmp (name, "ecdsa")
|
||
|| !strcmp (name, "ecdh")
|
||
|| !strcmp (name, "ecc") );
|
||
gcry_free (name);
|
||
|
||
if (!module)
|
||
{
|
||
gcry_sexp_release (list);
|
||
return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
|
||
}
|
||
else
|
||
{
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
extraspec = module->extraspec;
|
||
}
|
||
|
||
if (override_elems)
|
||
elems = override_elems;
|
||
else if (want_private)
|
||
elems = pubkey->elements_skey;
|
||
else
|
||
elems = pubkey->elements_pkey;
|
||
array = gcry_calloc (strlen (elems) + 1, sizeof (*array));
|
||
if (!array)
|
||
err = gpg_err_code_from_syserror ();
|
||
if (!err)
|
||
{
|
||
if (is_ecc)
|
||
err = sexp_elements_extract_ecc (list, elems, array, extraspec);
|
||
else
|
||
err = sexp_elements_extract (list, elems, array, pubkey->name);
|
||
}
|
||
|
||
gcry_sexp_release (list);
|
||
|
||
if (err)
|
||
{
|
||
gcry_free (array);
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
_gcry_module_release (module);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
}
|
||
else
|
||
{
|
||
*retarray = array;
|
||
*retalgo = module;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
static gcry_err_code_t
|
||
sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray,
|
||
gcry_module_t *retalgo)
|
||
{
|
||
gcry_err_code_t err = 0;
|
||
gcry_sexp_t list, l2;
|
||
char *name;
|
||
const char *elems;
|
||
gcry_mpi_t *array;
|
||
gcry_module_t module;
|
||
gcry_pk_spec_t *pubkey;
|
||
|
||
/* Check that the first element is valid. */
|
||
list = gcry_sexp_find_token( sexp, "sig-val" , 0 );
|
||
if (!list)
|
||
return GPG_ERR_INV_OBJ; /* Does not contain a signature value object. */
|
||
|
||
l2 = gcry_sexp_nth (list, 1);
|
||
if (!l2)
|
||
{
|
||
gcry_sexp_release (list);
|
||
return GPG_ERR_NO_OBJ; /* No cadr for the sig object. */
|
||
}
|
||
name = _gcry_sexp_nth_string (l2, 0);
|
||
if (!name)
|
||
{
|
||
gcry_sexp_release (list);
|
||
gcry_sexp_release (l2);
|
||
return GPG_ERR_INV_OBJ; /* Invalid structure of object. */
|
||
}
|
||
else if (!strcmp (name, "flags"))
|
||
{
|
||
/* Skip flags, since they are not used but here just for the
|
||
sake of consistent S-expressions. */
|
||
gcry_free (name);
|
||
gcry_sexp_release (l2);
|
||
l2 = gcry_sexp_nth (list, 2);
|
||
if (!l2)
|
||
{
|
||
gcry_sexp_release (list);
|
||
return GPG_ERR_INV_OBJ;
|
||
}
|
||
name = _gcry_sexp_nth_string (l2, 0);
|
||
}
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = gcry_pk_lookup_name (name);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
gcry_free (name);
|
||
name = NULL;
|
||
|
||
if (!module)
|
||
{
|
||
gcry_sexp_release (l2);
|
||
gcry_sexp_release (list);
|
||
return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
|
||
}
|
||
else
|
||
pubkey = (gcry_pk_spec_t *) module->spec;
|
||
|
||
elems = pubkey->elements_sig;
|
||
array = gcry_calloc (strlen (elems) + 1 , sizeof *array );
|
||
if (!array)
|
||
err = gpg_err_code_from_syserror ();
|
||
|
||
if (!err)
|
||
err = sexp_elements_extract (list, elems, array, NULL);
|
||
|
||
gcry_sexp_release (l2);
|
||
gcry_sexp_release (list);
|
||
|
||
if (err)
|
||
{
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
_gcry_module_release (module);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
|
||
gcry_free (array);
|
||
}
|
||
else
|
||
{
|
||
*retarray = array;
|
||
*retalgo = module;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
static inline int
|
||
get_hash_algo (const char *s, size_t n)
|
||
{
|
||
static const struct { const char *name; int algo; } hashnames[] = {
|
||
{ "sha1", GCRY_MD_SHA1 },
|
||
{ "md5", GCRY_MD_MD5 },
|
||
{ "sha256", GCRY_MD_SHA256 },
|
||
{ "ripemd160", GCRY_MD_RMD160 },
|
||
{ "rmd160", GCRY_MD_RMD160 },
|
||
{ "sha384", GCRY_MD_SHA384 },
|
||
{ "sha512", GCRY_MD_SHA512 },
|
||
{ "sha224", GCRY_MD_SHA224 },
|
||
{ "md2", GCRY_MD_MD2 },
|
||
{ "md4", GCRY_MD_MD4 },
|
||
{ "tiger", GCRY_MD_TIGER },
|
||
{ "haval", GCRY_MD_HAVAL },
|
||
{ NULL, 0 }
|
||
};
|
||
int algo;
|
||
int i;
|
||
|
||
for (i=0; hashnames[i].name; i++)
|
||
{
|
||
if ( strlen (hashnames[i].name) == n
|
||
&& !memcmp (hashnames[i].name, s, n))
|
||
break;
|
||
}
|
||
if (hashnames[i].name)
|
||
algo = hashnames[i].algo;
|
||
else
|
||
{
|
||
/* In case of not listed or dynamically allocated hash
|
||
algorithm we fall back to this somewhat slower
|
||
method. Further, it also allows to use OIDs as
|
||
algorithm names. */
|
||
char *tmpname;
|
||
|
||
tmpname = gcry_malloc (n+1);
|
||
if (!tmpname)
|
||
algo = 0; /* Out of core - silently give up. */
|
||
else
|
||
{
|
||
memcpy (tmpname, s, n);
|
||
tmpname[n] = 0;
|
||
algo = gcry_md_map_name (tmpname);
|
||
gcry_free (tmpname);
|
||
}
|
||
}
|
||
return algo;
|
||
}
|
||
|
||
|
||
/****************
|
||
* Take sexp and return an array of MPI as used for our internal decrypt
|
||
* function.
|
||
* s_data = (enc-val
|
||
* [(flags [raw, pkcs1, oaep, no-blinding])]
|
||
* [(hash-algo <algo>)]
|
||
* [(label <label>)]
|
||
* (<algo>
|
||
* (<param_name1> <mpi>)
|
||
* ...
|
||
* (<param_namen> <mpi>)
|
||
* ))
|
||
* HASH-ALGO and LABEL are specific to OAEP.
|
||
* RET_MODERN is set to true when at least an empty flags list has been found.
|
||
* CTX is used to return encoding information; it may be NULL in which
|
||
* case raw encoding is used.
|
||
*/
|
||
static gcry_err_code_t
|
||
sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
|
||
int *ret_modern, int *flags, struct pk_encoding_ctx *ctx)
|
||
{
|
||
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_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)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_RAW;
|
||
else if (n == 5 && !memcmp (s, "pkcs1", 5)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_PKCS1;
|
||
else if (n == 4 && !memcmp (s, "oaep", 4)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_OAEP;
|
||
else if (n == 3 && !memcmp (s, "pss", 3)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
{
|
||
err = GPG_ERR_CONFLICT;
|
||
goto leave;
|
||
}
|
||
else if (n == 11 && ! memcmp (s, "no-blinding", 11))
|
||
parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
|
||
else
|
||
{
|
||
err = GPG_ERR_INV_FLAG;
|
||
goto leave;
|
||
}
|
||
}
|
||
gcry_sexp_release (l2);
|
||
|
||
/* Get the OAEP parameters HASH-ALGO and LABEL, if any. */
|
||
if (ctx->encoding == PUBKEY_ENC_OAEP)
|
||
{
|
||
/* Get HASH-ALGO. */
|
||
l2 = gcry_sexp_find_token (list, "hash-algo", 0);
|
||
if (l2)
|
||
{
|
||
s = gcry_sexp_nth_data (l2, 1, &n);
|
||
if (!s)
|
||
err = GPG_ERR_NO_OBJ;
|
||
else
|
||
{
|
||
ctx->hash_algo = get_hash_algo (s, n);
|
||
if (!ctx->hash_algo)
|
||
err = GPG_ERR_DIGEST_ALGO;
|
||
}
|
||
gcry_sexp_release (l2);
|
||
if (err)
|
||
goto leave;
|
||
}
|
||
|
||
/* Get LABEL. */
|
||
l2 = gcry_sexp_find_token (list, "label", 0);
|
||
if (l2)
|
||
{
|
||
s = gcry_sexp_nth_data (l2, 1, &n);
|
||
if (!s)
|
||
err = GPG_ERR_NO_OBJ;
|
||
else if (n > 0)
|
||
{
|
||
ctx->label = gcry_malloc (n);
|
||
if (!ctx->label)
|
||
err = gpg_err_code_from_syserror ();
|
||
else
|
||
{
|
||
memcpy (ctx->label, s, n);
|
||
ctx->labellen = n;
|
||
}
|
||
}
|
||
gcry_sexp_release (l2);
|
||
if (err)
|
||
goto leave;
|
||
}
|
||
}
|
||
|
||
/* Get the next which has the actual data - skip HASH-ALGO and LABEL. */
|
||
for (i = 2; (l2 = gcry_sexp_nth (list, i)) != NULL; i++)
|
||
{
|
||
s = gcry_sexp_nth_data (l2, 0, &n);
|
||
if (!(n == 9 && !memcmp (s, "hash-algo", 9))
|
||
&& !(n == 5 && !memcmp (s, "label", 5))
|
||
&& !(n == 15 && !memcmp (s, "random-override", 15)))
|
||
break;
|
||
gcry_sexp_release (l2);
|
||
}
|
||
|
||
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_syserror ();
|
||
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);
|
||
gcry_free (ctx->label);
|
||
ctx->label = NULL;
|
||
}
|
||
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 [raw, pkcs1, oaep, pss, no-blinding])]
|
||
[(hash <algo> <value>)]
|
||
[(value <text>)]
|
||
[(hash-algo <algo>)]
|
||
[(label <label>)]
|
||
[(salt-length <length>)]
|
||
[(random-override <data>)]
|
||
)
|
||
|
||
Either the VALUE or the HASH element must be present for use
|
||
with signatures. VALUE is used for encryption.
|
||
|
||
HASH-ALGO and LABEL are specific to OAEP.
|
||
|
||
SALT-LENGTH is for PSS.
|
||
|
||
RANDOM-OVERRIDE is used to replace random nonces for regression
|
||
testing. */
|
||
static gcry_err_code_t
|
||
sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
|
||
struct pk_encoding_ctx *ctx)
|
||
{
|
||
gcry_err_code_t rc = 0;
|
||
gcry_sexp_t ldata, lhash, lvalue;
|
||
int i;
|
||
size_t n;
|
||
const char *s;
|
||
int unknown_flag=0;
|
||
int parsed_flags = 0;
|
||
|
||
*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)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_RAW;
|
||
else if ( n == 5 && !memcmp (s, "pkcs1", 5)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_PKCS1;
|
||
else if ( n == 4 && !memcmp (s, "oaep", 4)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_OAEP;
|
||
else if ( n == 3 && !memcmp (s, "pss", 3)
|
||
&& ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_PSS;
|
||
else if (n == 11 && ! memcmp (s, "no-blinding", 11))
|
||
parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
|
||
else
|
||
unknown_flag = 1;
|
||
}
|
||
gcry_sexp_release (lflags);
|
||
}
|
||
}
|
||
|
||
if (ctx->encoding == PUBKEY_ENC_UNKNOWN)
|
||
ctx->encoding = PUBKEY_ENC_RAW; /* default to raw */
|
||
|
||
/* Get HASH or MPI */
|
||
lhash = gcry_sexp_find_token (ldata, "hash", 0);
|
||
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 (ctx->encoding == PUBKEY_ENC_RAW && lvalue)
|
||
{
|
||
*ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, GCRYMPI_FMT_USG);
|
||
if (!*ret_mpi)
|
||
rc = GPG_ERR_INV_OBJ;
|
||
}
|
||
else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue
|
||
&& ctx->op == PUBKEY_OP_ENCRYPT)
|
||
{
|
||
const void * value;
|
||
size_t valuelen;
|
||
gcry_sexp_t list;
|
||
void *random_override = NULL;
|
||
size_t random_override_len = 0;
|
||
|
||
if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen )
|
||
rc = GPG_ERR_INV_OBJ;
|
||
else
|
||
{
|
||
/* Get optional RANDOM-OVERRIDE. */
|
||
list = gcry_sexp_find_token (ldata, "random-override", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
rc = GPG_ERR_NO_OBJ;
|
||
else if (n > 0)
|
||
{
|
||
random_override = gcry_malloc (n);
|
||
if (!random_override)
|
||
rc = gpg_err_code_from_syserror ();
|
||
else
|
||
{
|
||
memcpy (random_override, s, n);
|
||
random_override_len = n;
|
||
}
|
||
}
|
||
gcry_sexp_release (list);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
|
||
rc = pkcs1_encode_for_encryption (ret_mpi, ctx->nbits,
|
||
value, valuelen,
|
||
random_override,
|
||
random_override_len);
|
||
gcry_free (random_override);
|
||
}
|
||
}
|
||
else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash
|
||
&& (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY))
|
||
{
|
||
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
|
||
{
|
||
const void * value;
|
||
size_t valuelen;
|
||
|
||
ctx->hash_algo = get_hash_algo (s, n);
|
||
|
||
if (!ctx->hash_algo)
|
||
rc = GPG_ERR_DIGEST_ALGO;
|
||
else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
|
||
|| !valuelen )
|
||
rc = GPG_ERR_INV_OBJ;
|
||
else
|
||
rc = pkcs1_encode_for_signature (ret_mpi, ctx->nbits,
|
||
value, valuelen,
|
||
ctx->hash_algo);
|
||
}
|
||
}
|
||
else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue
|
||
&& ctx->op == PUBKEY_OP_ENCRYPT)
|
||
{
|
||
const void * value;
|
||
size_t valuelen;
|
||
|
||
if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen )
|
||
rc = GPG_ERR_INV_OBJ;
|
||
else
|
||
{
|
||
gcry_sexp_t list;
|
||
void *random_override = NULL;
|
||
size_t random_override_len = 0;
|
||
|
||
/* Get HASH-ALGO. */
|
||
list = gcry_sexp_find_token (ldata, "hash-algo", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
rc = GPG_ERR_NO_OBJ;
|
||
else
|
||
{
|
||
ctx->hash_algo = get_hash_algo (s, n);
|
||
if (!ctx->hash_algo)
|
||
rc = GPG_ERR_DIGEST_ALGO;
|
||
}
|
||
gcry_sexp_release (list);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
|
||
/* Get LABEL. */
|
||
list = gcry_sexp_find_token (ldata, "label", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
rc = GPG_ERR_NO_OBJ;
|
||
else if (n > 0)
|
||
{
|
||
ctx->label = gcry_malloc (n);
|
||
if (!ctx->label)
|
||
rc = gpg_err_code_from_syserror ();
|
||
else
|
||
{
|
||
memcpy (ctx->label, s, n);
|
||
ctx->labellen = n;
|
||
}
|
||
}
|
||
gcry_sexp_release (list);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
/* Get optional RANDOM-OVERRIDE. */
|
||
list = gcry_sexp_find_token (ldata, "random-override", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
rc = GPG_ERR_NO_OBJ;
|
||
else if (n > 0)
|
||
{
|
||
random_override = gcry_malloc (n);
|
||
if (!random_override)
|
||
rc = gpg_err_code_from_syserror ();
|
||
else
|
||
{
|
||
memcpy (random_override, s, n);
|
||
random_override_len = n;
|
||
}
|
||
}
|
||
gcry_sexp_release (list);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
|
||
rc = oaep_encode (ret_mpi, ctx->nbits, ctx->hash_algo,
|
||
value, valuelen,
|
||
ctx->label, ctx->labellen,
|
||
random_override, random_override_len);
|
||
|
||
gcry_free (random_override);
|
||
}
|
||
}
|
||
else if (ctx->encoding == PUBKEY_ENC_PSS && lhash
|
||
&& ctx->op == PUBKEY_OP_SIGN)
|
||
{
|
||
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
|
||
{
|
||
const void * value;
|
||
size_t valuelen;
|
||
void *random_override = NULL;
|
||
size_t random_override_len = 0;
|
||
|
||
ctx->hash_algo = get_hash_algo (s, n);
|
||
|
||
if (!ctx->hash_algo)
|
||
rc = GPG_ERR_DIGEST_ALGO;
|
||
else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
|
||
|| !valuelen )
|
||
rc = GPG_ERR_INV_OBJ;
|
||
else
|
||
{
|
||
gcry_sexp_t list;
|
||
|
||
/* Get SALT-LENGTH. */
|
||
list = gcry_sexp_find_token (ldata, "salt-length", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
{
|
||
rc = GPG_ERR_NO_OBJ;
|
||
goto leave;
|
||
}
|
||
ctx->saltlen = (unsigned int)strtoul (s, NULL, 10);
|
||
gcry_sexp_release (list);
|
||
}
|
||
|
||
/* Get optional RANDOM-OVERRIDE. */
|
||
list = gcry_sexp_find_token (ldata, "random-override", 0);
|
||
if (list)
|
||
{
|
||
s = gcry_sexp_nth_data (list, 1, &n);
|
||
if (!s)
|
||
rc = GPG_ERR_NO_OBJ;
|
||
else if (n > 0)
|
||
{
|
||
random_override = gcry_malloc (n);
|
||
if (!random_override)
|
||
rc = gpg_err_code_from_syserror ();
|
||
else
|
||
{
|
||
memcpy (random_override, s, n);
|
||
random_override_len = n;
|
||
}
|
||
}
|
||
gcry_sexp_release (list);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
|
||
/* Encode the data. (NBITS-1 is due to 8.1.1, step 1.) */
|
||
rc = pss_encode (ret_mpi, ctx->nbits - 1, ctx->hash_algo,
|
||
value, valuelen, ctx->saltlen,
|
||
random_override, random_override_len);
|
||
|
||
gcry_free (random_override);
|
||
}
|
||
}
|
||
}
|
||
else if (ctx->encoding == PUBKEY_ENC_PSS && lhash
|
||
&& ctx->op == PUBKEY_OP_VERIFY)
|
||
{
|
||
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
|
||
{
|
||
ctx->hash_algo = get_hash_algo (s, n);
|
||
|
||
if (!ctx->hash_algo)
|
||
rc = GPG_ERR_DIGEST_ALGO;
|
||
else
|
||
{
|
||
*ret_mpi = gcry_sexp_nth_mpi (lhash, 2, GCRYMPI_FMT_USG);
|
||
if (!*ret_mpi)
|
||
rc = GPG_ERR_INV_OBJ;
|
||
ctx->verify_cmp = pss_verify_cmp;
|
||
ctx->verify_arg = *ret_mpi;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
rc = GPG_ERR_CONFLICT;
|
||
|
||
leave:
|
||
gcry_sexp_release (ldata);
|
||
gcry_sexp_release (lhash);
|
||
gcry_sexp_release (lvalue);
|
||
|
||
if (!rc)
|
||
ctx->flags = parsed_flags;
|
||
else
|
||
{
|
||
gcry_free (ctx->label);
|
||
ctx->label = NULL;
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
static void
|
||
init_encoding_ctx (struct pk_encoding_ctx *ctx, enum pk_operation op,
|
||
unsigned int nbits)
|
||
{
|
||
ctx->op = op;
|
||
ctx->nbits = nbits;
|
||
ctx->encoding = PUBKEY_ENC_UNKNOWN;
|
||
ctx->flags = 0;
|
||
ctx->hash_algo = GCRY_MD_SHA1;
|
||
ctx->label = NULL;
|
||
ctx->labellen = 0;
|
||
ctx->saltlen = 20;
|
||
ctx->verify_cmp = NULL;
|
||
ctx->verify_arg = NULL;
|
||
}
|
||
|
||
|
||
/*
|
||
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 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;
|
||
struct pk_encoding_ctx ctx;
|
||
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, NULL, &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. */
|
||
init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, gcry_pk_get_nbits (s_pkey));
|
||
rc = sexp_data_to_mpi (s_data, &data, &ctx);
|
||
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_syserror ();
|
||
goto leave;
|
||
}
|
||
rc = pubkey_encrypt (module->mod_id, ciph, data, pkey, ctx.flags);
|
||
mpi_free (data);
|
||
data = NULL;
|
||
if (rc)
|
||
goto leave;
|
||
|
||
/* We did it. Now build the return list */
|
||
if (ctx.encoding == PUBKEY_ENC_OAEP
|
||
|| ctx.encoding == PUBKEY_ENC_PKCS1)
|
||
{
|
||
/* We need to make sure to return the correct length to avoid
|
||
problems with missing leading zeroes. We know that this
|
||
encoding does only make sense with RSA thus we don't need to
|
||
build the S-expression on the fly. */
|
||
unsigned char *em;
|
||
size_t emlen = (ctx.nbits+7)/8;
|
||
|
||
rc = octet_string_from_mpi (&em, NULL, ciph[0], emlen);
|
||
if (rc)
|
||
goto leave;
|
||
rc = gcry_err_code (gcry_sexp_build (r_ciph, NULL,
|
||
"(enc-val(%s(a%b)))",
|
||
algo_name, (int)emlen, em));
|
||
gcry_free (em);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
else
|
||
{
|
||
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_syserror ();
|
||
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 = calloc (nelem, sizeof *arg_list);
|
||
if (!arg_list)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
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);
|
||
}
|
||
|
||
gcry_free (ctx.label);
|
||
|
||
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 [raw, pkcs1, oaep])]
|
||
(<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). In raw mode (or no flags given) the returned value
|
||
is to be interpreted as a signed MPI, thus it may have an extra
|
||
leading zero octet even if not included in the original data.
|
||
With pkcs1 or oaep decoding enabled the returned value is a
|
||
verbatim octet string.
|
||
*/
|
||
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;
|
||
unsigned char *unpad = NULL;
|
||
size_t unpadlen = 0;
|
||
int modern, flags;
|
||
struct pk_encoding_ctx ctx;
|
||
gcry_err_code_t rc;
|
||
gcry_module_t module_enc = NULL, module_key = NULL;
|
||
|
||
*r_plain = NULL;
|
||
ctx.label = NULL;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
rc = sexp_to_key (s_skey, 1, NULL, &skey, &module_key);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT, gcry_pk_get_nbits (s_skey));
|
||
rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &flags, &ctx);
|
||
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;
|
||
}
|
||
|
||
rc = pubkey_decrypt (module_key->mod_id, &plain, data, skey, flags);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
/* Do un-padding if necessary. */
|
||
switch (ctx.encoding)
|
||
{
|
||
case PUBKEY_ENC_PKCS1:
|
||
rc = pkcs1_decode_for_encryption (&unpad, &unpadlen,
|
||
gcry_pk_get_nbits (s_skey), plain);
|
||
mpi_free (plain);
|
||
plain = NULL;
|
||
if (!rc)
|
||
rc = gcry_err_code (gcry_sexp_build (r_plain, NULL, "(value %b)",
|
||
(int)unpadlen, unpad));
|
||
break;
|
||
|
||
case PUBKEY_ENC_OAEP:
|
||
rc = oaep_decode (&unpad, &unpadlen,
|
||
gcry_pk_get_nbits (s_skey), ctx.hash_algo,
|
||
plain, ctx.label, ctx.labellen);
|
||
mpi_free (plain);
|
||
plain = NULL;
|
||
if (!rc)
|
||
rc = gcry_err_code (gcry_sexp_build (r_plain, NULL, "(value %b)",
|
||
(int)unpadlen, unpad));
|
||
break;
|
||
|
||
default:
|
||
/* Raw format. For backward compatibility we need to assume a
|
||
signed mpi by using the sexp format string "%m". */
|
||
rc = gcry_err_code (gcry_sexp_build
|
||
(r_plain, NULL, modern? "(value %m)" : "%m", plain));
|
||
break;
|
||
}
|
||
|
||
leave:
|
||
gcry_free (unpad);
|
||
|
||
if (skey)
|
||
{
|
||
release_mpi_array (skey);
|
||
gcry_free (skey);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
gcry_free (ctx.label);
|
||
|
||
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 flags defined by now are "pkcs1" which does
|
||
PKCS#1 block type 1 style padding and "pss" for PSS encoding.
|
||
|
||
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;
|
||
struct pk_encoding_ctx ctx;
|
||
int i;
|
||
gcry_err_code_t rc;
|
||
|
||
*r_sig = NULL;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
rc = sexp_to_key (s_skey, 1, NULL, &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. */
|
||
init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, gcry_pk_get_nbits (s_skey));
|
||
rc = sexp_data_to_mpi (s_hash, &hash, &ctx);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
result = gcry_calloc (strlen (algo_elems) + 1, sizeof (*result));
|
||
if (!result)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
goto leave;
|
||
}
|
||
rc = pubkey_sign (module->mod_id, result, hash, skey);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
if (ctx.encoding == PUBKEY_ENC_PSS
|
||
|| ctx.encoding == PUBKEY_ENC_PKCS1)
|
||
{
|
||
/* We need to make sure to return the correct length to avoid
|
||
problems with missing leading zeroes. We know that this
|
||
encoding does only make sense with RSA thus we don't need to
|
||
build the S-expression on the fly. */
|
||
unsigned char *em;
|
||
size_t emlen = (ctx.nbits+7)/8;
|
||
|
||
rc = octet_string_from_mpi (&em, NULL, result[0], emlen);
|
||
if (rc)
|
||
goto leave;
|
||
rc = gcry_err_code (gcry_sexp_build (r_sig, NULL,
|
||
"(sig-val(%s(s%b)))",
|
||
algo_name, (int)emlen, em));
|
||
gcry_free (em);
|
||
if (rc)
|
||
goto leave;
|
||
}
|
||
else
|
||
{
|
||
/* General purpose output encoding. Do it on the fly. */
|
||
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_syserror ();
|
||
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 = calloc (nelem, sizeof *arg_list);
|
||
if (!arg_list)
|
||
{
|
||
rc = gpg_err_code_from_syserror ();
|
||
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;
|
||
struct pk_encoding_ctx ctx;
|
||
gcry_err_code_t rc;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
rc = sexp_to_key (s_pkey, 0, NULL, &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;
|
||
}
|
||
|
||
/* Get the stuff we want to verify. */
|
||
init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, gcry_pk_get_nbits (s_pkey));
|
||
rc = sexp_data_to_mpi (s_hash, &hash, &ctx);
|
||
if (rc)
|
||
goto leave;
|
||
|
||
rc = pubkey_verify (module_key->mod_id, hash, sig, pkey,
|
||
ctx.verify_cmp, &ctx);
|
||
|
||
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, NULL, &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, j;
|
||
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];
|
||
int percent_s_idx = -1;
|
||
|
||
/* 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_syserror ();
|
||
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];
|
||
}
|
||
if (extrainfo && (algo == GCRY_PK_ECDSA || algo == GCRY_PK_ECDH))
|
||
{
|
||
/* Very ugly hack to insert the used curve parameter into the
|
||
list of public key parameters. */
|
||
percent_s_idx = nelem;
|
||
p = stpcpy (p, "%S");
|
||
}
|
||
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 && percent_s_idx == -1)
|
||
{
|
||
/* 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_syserror ();
|
||
goto leave;
|
||
}
|
||
for (i = j = 0; i < elem_n; i++)
|
||
{
|
||
if (i == percent_s_idx)
|
||
arg_list[j++] = &extrainfo;
|
||
arg_list[j++] = mpis + i;
|
||
}
|
||
if (extrainfo && percent_s_idx == -1)
|
||
arg_list[j] = &extrainfo;
|
||
else if (factors && factors[0])
|
||
{
|
||
for (; i < nelem_cp; i++)
|
||
arg_list[j++] = 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, NULL, &keyarr, &module);
|
||
if (rc == GPG_ERR_INV_OBJ)
|
||
rc = sexp_to_key (key, 1, NULL, &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;
|
||
int okay = 0;
|
||
|
||
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);
|
||
l2 = NULL;
|
||
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);
|
||
okay = 1;
|
||
|
||
fail:
|
||
gcry_free (name);
|
||
gcry_sexp_release (l2);
|
||
gcry_md_close (md);
|
||
gcry_sexp_release (list);
|
||
return okay? array : NULL;
|
||
}
|
||
|
||
|
||
|
||
const char *
|
||
gcry_pk_get_curve (gcry_sexp_t key, int iterator, unsigned int *r_nbits)
|
||
{
|
||
gcry_mpi_t *pkey = NULL;
|
||
gcry_sexp_t list = NULL;
|
||
gcry_sexp_t l2;
|
||
gcry_module_t module = NULL;
|
||
pk_extra_spec_t *extraspec;
|
||
char *name = NULL;
|
||
const char *result = NULL;
|
||
int want_private = 1;
|
||
|
||
if (r_nbits)
|
||
*r_nbits = 0;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
if (key)
|
||
{
|
||
iterator = 0;
|
||
|
||
/* Check that the first element is valid. */
|
||
list = gcry_sexp_find_token (key, "public-key", 0);
|
||
if (list)
|
||
want_private = 0;
|
||
if (!list)
|
||
list = gcry_sexp_find_token (key, "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 leave; /* Invalid structure of object. */
|
||
|
||
/* Get the key. We pass the names of the parameters for
|
||
override_elems; this allows to call this function without the
|
||
actual public key parameter. */
|
||
if (sexp_to_key (key, want_private, "pabgn", &pkey, &module))
|
||
goto leave;
|
||
}
|
||
else
|
||
{
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = gcry_pk_lookup_name ("ecc");
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
if (!module)
|
||
goto leave;
|
||
}
|
||
|
||
extraspec = module->extraspec;
|
||
if (!extraspec || !extraspec->get_curve)
|
||
goto leave;
|
||
|
||
result = extraspec->get_curve (pkey, iterator, r_nbits);
|
||
|
||
leave:
|
||
if (pkey)
|
||
{
|
||
release_mpi_array (pkey);
|
||
gcry_free (pkey);
|
||
}
|
||
if (module)
|
||
{
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
_gcry_module_release (module);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
}
|
||
gcry_free (name);
|
||
gcry_sexp_release (list);
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
gcry_sexp_t
|
||
gcry_pk_get_param (int algo, const char *name)
|
||
{
|
||
gcry_module_t module = NULL;
|
||
pk_extra_spec_t *extraspec;
|
||
gcry_sexp_t result = NULL;
|
||
|
||
if (algo != GCRY_PK_ECDSA && algo != GCRY_PK_ECDH)
|
||
return NULL;
|
||
|
||
REGISTER_DEFAULT_PUBKEYS;
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
module = gcry_pk_lookup_name ("ecc");
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
if (module)
|
||
{
|
||
extraspec = module->extraspec;
|
||
if (extraspec && extraspec->get_curve_param)
|
||
result = extraspec->get_curve_param (name);
|
||
|
||
ath_mutex_lock (&pubkeys_registered_lock);
|
||
_gcry_module_release (module);
|
||
ath_mutex_unlock (&pubkeys_registered_lock);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
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 flags for the given algo. An invalid algo
|
||
returns 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_syserror ();
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (sig)
|
||
{
|
||
sig_cp = strdup (spec->elements_sig);
|
||
if (! sig_cp)
|
||
{
|
||
err = gpg_err_code_from_syserror ();
|
||
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;
|
||
}
|