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