mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-04 20:38:50 +01:00
1222 lines
32 KiB
C
1222 lines
32 KiB
C
/* pubkey.c - pubkey dispatcher
|
|
* Copyright (C) 1998, 1999 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include "g10lib.h"
|
|
#include "mpi.h"
|
|
#include "cipher.h"
|
|
#include "elgamal.h"
|
|
#include "dsa.h"
|
|
#include "dynload.h"
|
|
|
|
/* FIXME: use set_lasterr() */
|
|
|
|
#define TABLE_SIZE 10
|
|
|
|
struct pubkey_table_s {
|
|
const char *name;
|
|
int algo;
|
|
int npkey;
|
|
int nskey;
|
|
int nenc;
|
|
int nsig;
|
|
int use;
|
|
int (*generate)( int algo, unsigned nbits, MPI *skey, MPI **retfactors );
|
|
int (*check_secret_key)( int algo, MPI *skey );
|
|
int (*encrypt)( int algo, MPI *resarr, MPI data, MPI *pkey );
|
|
int (*decrypt)( int algo, MPI *result, MPI *data, MPI *skey );
|
|
int (*sign)( int algo, MPI *resarr, MPI data, MPI *skey );
|
|
int (*verify)( int algo, MPI hash, MPI *data, MPI *pkey,
|
|
int (*cmp)(void *, MPI), void *opaquev );
|
|
unsigned (*get_nbits)( int algo, MPI *pkey );
|
|
};
|
|
|
|
static struct pubkey_table_s pubkey_table[TABLE_SIZE];
|
|
static int disabled_algos[TABLE_SIZE];
|
|
|
|
static struct { const char* name; int algo;
|
|
const char* common_elements;
|
|
const char* public_elements;
|
|
const char* secret_elements;
|
|
} algo_info_table[] = {
|
|
{ "dsa" , PUBKEY_ALGO_DSA , "pqgy", "", "x" },
|
|
{ "rsa" , PUBKEY_ALGO_RSA , "ne", "", "dpqu" },
|
|
{ "elg" , PUBKEY_ALGO_ELGAMAL , "pgy", "", "x" },
|
|
{ "openpgp-dsa" , PUBKEY_ALGO_DSA , "pqgy", "", "x" },
|
|
{ "openpgp-rsa" , PUBKEY_ALGO_RSA , "pqgy", "", "x" },
|
|
{ "openpgp-elg" , PUBKEY_ALGO_ELGAMAL_E , "pgy", "", "x" },
|
|
{ "openpgp-elg-sig", PUBKEY_ALGO_ELGAMAL , "pgy", "", "x" },
|
|
{ NULL }};
|
|
|
|
static struct {
|
|
const char* name; int algo;
|
|
const char* elements;
|
|
} sig_info_table[] = {
|
|
{ "dsa" , PUBKEY_ALGO_DSA , "rs" },
|
|
{ "rsa" , PUBKEY_ALGO_RSA , "s" },
|
|
{ "elg" , PUBKEY_ALGO_ELGAMAL , "rs" },
|
|
{ "openpgp-dsa" , PUBKEY_ALGO_DSA , "rs" },
|
|
{ "openpgp-rsa" , PUBKEY_ALGO_RSA , "s" },
|
|
{ "openpgp-elg-sig", PUBKEY_ALGO_ELGAMAL , "rs" },
|
|
{ NULL }};
|
|
|
|
static struct {
|
|
const char* name; int algo;
|
|
const char* elements;
|
|
} enc_info_table[] = {
|
|
{ "elg" , PUBKEY_ALGO_ELGAMAL , "ab" },
|
|
{ "rsa" , PUBKEY_ALGO_RSA , "a" },
|
|
{ "openpgp-rsa" , PUBKEY_ALGO_RSA , "a" },
|
|
{ "openpgp-elg" , PUBKEY_ALGO_ELGAMAL_E , "ab" },
|
|
{ "openpgp-elg-sig", PUBKEY_ALGO_ELGAMAL , "ab" },
|
|
{ NULL }};
|
|
|
|
|
|
static int pubkey_decrypt( int algo, MPI *result, MPI *data, MPI *skey );
|
|
static int pubkey_sign( int algo, MPI *resarr, MPI hash, MPI *skey );
|
|
static int pubkey_verify( int algo, MPI hash, MPI *data, MPI *pkey,
|
|
int (*cmp)(void *, MPI), void *opaque );
|
|
|
|
static int
|
|
dummy_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors )
|
|
{ log_bug("no generate() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static int
|
|
dummy_check_secret_key( int algo, MPI *skey )
|
|
{ log_bug("no check_secret_key() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static int
|
|
dummy_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey )
|
|
{ log_bug("no encrypt() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static int
|
|
dummy_decrypt( int algo, MPI *result, MPI *data, MPI *skey )
|
|
{ log_bug("no decrypt() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static int
|
|
dummy_sign( int algo, MPI *resarr, MPI data, MPI *skey )
|
|
{ log_bug("no sign() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static int
|
|
dummy_verify( int algo, MPI hash, MPI *data, MPI *pkey,
|
|
int (*cmp)(void *, MPI), void *opaquev )
|
|
{ log_bug("no verify() for %d\n", algo ); return GCRYERR_INV_PK_ALGO; }
|
|
|
|
static unsigned
|
|
dummy_get_nbits( int algo, MPI *pkey )
|
|
{ log_bug("no get_nbits() for %d\n", algo ); return 0; }
|
|
|
|
|
|
/****************
|
|
* Put the static entries into the table.
|
|
* This is out constructor function which fill the table
|
|
* of algorithms with the one we have statically linked.
|
|
*/
|
|
static void
|
|
setup_pubkey_table(void)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
pubkey_table[i].algo = PUBKEY_ALGO_ELGAMAL;
|
|
pubkey_table[i].name = elg_get_info( pubkey_table[i].algo,
|
|
&pubkey_table[i].npkey,
|
|
&pubkey_table[i].nskey,
|
|
&pubkey_table[i].nenc,
|
|
&pubkey_table[i].nsig,
|
|
&pubkey_table[i].use );
|
|
pubkey_table[i].generate = elg_generate;
|
|
pubkey_table[i].check_secret_key = elg_check_secret_key;
|
|
pubkey_table[i].encrypt = elg_encrypt;
|
|
pubkey_table[i].decrypt = elg_decrypt;
|
|
pubkey_table[i].sign = elg_sign;
|
|
pubkey_table[i].verify = elg_verify;
|
|
pubkey_table[i].get_nbits = elg_get_nbits;
|
|
if( !pubkey_table[i].name )
|
|
BUG();
|
|
i++;
|
|
pubkey_table[i].algo = PUBKEY_ALGO_ELGAMAL_E;
|
|
pubkey_table[i].name = elg_get_info( pubkey_table[i].algo,
|
|
&pubkey_table[i].npkey,
|
|
&pubkey_table[i].nskey,
|
|
&pubkey_table[i].nenc,
|
|
&pubkey_table[i].nsig,
|
|
&pubkey_table[i].use );
|
|
pubkey_table[i].generate = elg_generate;
|
|
pubkey_table[i].check_secret_key = elg_check_secret_key;
|
|
pubkey_table[i].encrypt = elg_encrypt;
|
|
pubkey_table[i].decrypt = elg_decrypt;
|
|
pubkey_table[i].sign = elg_sign;
|
|
pubkey_table[i].verify = elg_verify;
|
|
pubkey_table[i].get_nbits = elg_get_nbits;
|
|
if( !pubkey_table[i].name )
|
|
BUG();
|
|
i++;
|
|
pubkey_table[i].algo = PUBKEY_ALGO_DSA;
|
|
pubkey_table[i].name = dsa_get_info( pubkey_table[i].algo,
|
|
&pubkey_table[i].npkey,
|
|
&pubkey_table[i].nskey,
|
|
&pubkey_table[i].nenc,
|
|
&pubkey_table[i].nsig,
|
|
&pubkey_table[i].use );
|
|
pubkey_table[i].generate = dsa_generate;
|
|
pubkey_table[i].check_secret_key = dsa_check_secret_key;
|
|
pubkey_table[i].encrypt = dummy_encrypt;
|
|
pubkey_table[i].decrypt = dummy_decrypt;
|
|
pubkey_table[i].sign = dsa_sign;
|
|
pubkey_table[i].verify = dsa_verify;
|
|
pubkey_table[i].get_nbits = dsa_get_nbits;
|
|
if( !pubkey_table[i].name )
|
|
BUG();
|
|
i++;
|
|
|
|
for( ; i < TABLE_SIZE; i++ )
|
|
pubkey_table[i].name = NULL;
|
|
}
|
|
|
|
static void
|
|
release_mpi_array( MPI *array )
|
|
{
|
|
for( ; *array; array++ ) {
|
|
mpi_free(*array);
|
|
*array = NULL;
|
|
}
|
|
}
|
|
|
|
/****************
|
|
* Try to load all modules and return true if new modules are available
|
|
*/
|
|
static int
|
|
load_pubkey_modules(void)
|
|
{
|
|
static int initialized = 0;
|
|
static int done = 0;
|
|
void *context = NULL;
|
|
struct pubkey_table_s *ct;
|
|
int ct_idx;
|
|
int i;
|
|
const char *name;
|
|
int any = 0;
|
|
|
|
|
|
if( !initialized ) {
|
|
cipher_modules_constructor();
|
|
setup_pubkey_table();
|
|
initialized = 1;
|
|
return 1;
|
|
}
|
|
if( done )
|
|
return 0;
|
|
done = 1;
|
|
for(ct_idx=0, ct = pubkey_table; ct_idx < TABLE_SIZE; ct_idx++,ct++ ) {
|
|
if( !ct->name )
|
|
break;
|
|
}
|
|
if( ct_idx >= TABLE_SIZE-1 )
|
|
BUG(); /* table already full */
|
|
/* now load all extensions */
|
|
while( (name = enum_gnupgext_pubkeys( &context, &ct->algo,
|
|
&ct->npkey, &ct->nskey, &ct->nenc,
|
|
&ct->nsig, &ct->use,
|
|
&ct->generate,
|
|
&ct->check_secret_key,
|
|
&ct->encrypt,
|
|
&ct->decrypt,
|
|
&ct->sign,
|
|
&ct->verify,
|
|
&ct->get_nbits )) ) {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == ct->algo )
|
|
break;
|
|
if( pubkey_table[i].name ) {
|
|
log_info("skipping pubkey %d: already loaded\n", ct->algo );
|
|
continue;
|
|
}
|
|
|
|
if( !ct->generate ) ct->generate = dummy_generate;
|
|
if( !ct->check_secret_key ) ct->check_secret_key =
|
|
dummy_check_secret_key;
|
|
if( !ct->encrypt ) ct->encrypt = dummy_encrypt;
|
|
if( !ct->decrypt ) ct->decrypt = dummy_decrypt;
|
|
if( !ct->sign ) ct->sign = dummy_sign;
|
|
if( !ct->verify ) ct->verify = dummy_verify;
|
|
if( !ct->get_nbits ) ct->get_nbits= dummy_get_nbits;
|
|
/* put it into the table */
|
|
if( g10_log_verbosity( 2 ) )
|
|
log_info("loaded pubkey %d (%s)\n", ct->algo, name);
|
|
ct->name = name;
|
|
ct_idx++;
|
|
ct++;
|
|
any = 1;
|
|
/* check whether there are more available table slots */
|
|
if( ct_idx >= TABLE_SIZE-1 ) {
|
|
log_info("pubkey table full; ignoring other extensions\n");
|
|
break;
|
|
}
|
|
}
|
|
enum_gnupgext_pubkeys( &context, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
|
return any;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Map a string to the pubkey algo
|
|
*/
|
|
int
|
|
gcry_pk_map_name( const char *string )
|
|
{
|
|
int i;
|
|
const char *s;
|
|
|
|
do {
|
|
for(i=0; (s=pubkey_table[i].name); i++ )
|
|
if( !stricmp( s, string ) )
|
|
return pubkey_table[i].algo;
|
|
} while( load_pubkey_modules() );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Map a pubkey algo to a string
|
|
*/
|
|
const char *
|
|
gcry_pk_algo_name( int algo )
|
|
{
|
|
int i;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return pubkey_table[i].name;
|
|
} while( load_pubkey_modules() );
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
disable_pubkey_algo( int algo )
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i < DIM(disabled_algos); i++ ) {
|
|
if( !disabled_algos[i] || disabled_algos[i] == algo ) {
|
|
disabled_algos[i] = algo;
|
|
return;
|
|
}
|
|
}
|
|
log_fatal("can't disable pubkey algo %d: table full\n", algo );
|
|
}
|
|
|
|
|
|
/****************
|
|
* a use of 0 means: don't care
|
|
*/
|
|
static int
|
|
check_pubkey_algo( int algo, unsigned use )
|
|
{
|
|
int i;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
if( (use & GCRY_PK_USAGE_SIGN)
|
|
&& !(pubkey_table[i].use & GCRY_PK_USAGE_SIGN) )
|
|
return GCRYERR_WRONG_PK_ALGO;
|
|
if( (use & GCRY_PK_USAGE_ENCR)
|
|
&& !(pubkey_table[i].use & GCRY_PK_USAGE_ENCR) )
|
|
return GCRYERR_WRONG_PK_ALGO;
|
|
|
|
for(i=0; i < DIM(disabled_algos); i++ ) {
|
|
if( disabled_algos[i] == algo )
|
|
return GCRYERR_INV_PK_ALGO;
|
|
}
|
|
return 0; /* okay */
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
return GCRYERR_INV_PK_ALGO;
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************
|
|
* Return the number of public key material numbers
|
|
*/
|
|
static int
|
|
pubkey_get_npkey( int algo )
|
|
{
|
|
int i;
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return pubkey_table[i].npkey;
|
|
} while( load_pubkey_modules() );
|
|
if( is_RSA(algo) ) /* special hack, so that we are able to */
|
|
return 2; /* see the RSA keyids */
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* Return the number of secret key material numbers
|
|
*/
|
|
static int
|
|
pubkey_get_nskey( int algo )
|
|
{
|
|
int i;
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return pubkey_table[i].nskey;
|
|
} while( load_pubkey_modules() );
|
|
if( is_RSA(algo) ) /* special hack, so that we are able to */
|
|
return 6; /* see the RSA keyids */
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* Return the number of signature material numbers
|
|
*/
|
|
static int
|
|
pubkey_get_nsig( int algo )
|
|
{
|
|
int i;
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return pubkey_table[i].nsig;
|
|
} while( load_pubkey_modules() );
|
|
if( is_RSA(algo) ) /* special hack, so that we are able to */
|
|
return 1; /* see the RSA keyids */
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* Return the number of encryption material numbers
|
|
*/
|
|
static int
|
|
pubkey_get_nenc( int algo )
|
|
{
|
|
int i;
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return pubkey_table[i].nenc;
|
|
} while( load_pubkey_modules() );
|
|
if( is_RSA(algo) ) /* special hack, so that we are able to */
|
|
return 1; /* see the RSA keyids */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
pubkey_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors )
|
|
{
|
|
int i;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return (*pubkey_table[i].generate)( algo, nbits,
|
|
skey, retfactors );
|
|
} while( load_pubkey_modules() );
|
|
return GCRYERR_INV_PK_ALGO;
|
|
}
|
|
|
|
|
|
static int
|
|
pubkey_check_secret_key( int algo, MPI *skey )
|
|
{
|
|
int i;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo )
|
|
return (*pubkey_table[i].check_secret_key)( algo, skey );
|
|
} while( load_pubkey_modules() );
|
|
return GCRYERR_INV_PK_ALGO;
|
|
}
|
|
|
|
|
|
/****************
|
|
* 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 int
|
|
pubkey_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey )
|
|
{
|
|
int i, rc;
|
|
|
|
if( DBG_CIPHER ) {
|
|
log_debug("pubkey_encrypt: algo=%d\n", algo );
|
|
for(i=0; i < pubkey_get_npkey(algo); i++ )
|
|
log_mpidump(" pkey:", pkey[i] );
|
|
log_mpidump(" data:", data );
|
|
}
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
rc = (*pubkey_table[i].encrypt)( algo, resarr, data, pkey );
|
|
goto ready;
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
rc = GCRYERR_INV_PK_ALGO;
|
|
ready:
|
|
if( !rc && DBG_CIPHER ) {
|
|
for(i=0; i < pubkey_get_nenc(algo); 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 int
|
|
pubkey_decrypt( int algo, MPI *result, MPI *data, MPI *skey )
|
|
{
|
|
int i, rc;
|
|
|
|
*result = NULL; /* so the caller can always do a mpi_free */
|
|
if( DBG_CIPHER ) {
|
|
log_debug("pubkey_decrypt: algo=%d\n", algo );
|
|
for(i=0; i < pubkey_get_nskey(algo); i++ )
|
|
log_mpidump(" skey:", skey[i] );
|
|
for(i=0; i < pubkey_get_nenc(algo); i++ )
|
|
log_mpidump(" data:", data[i] );
|
|
}
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
rc = (*pubkey_table[i].decrypt)( algo, result, data, skey );
|
|
goto ready;
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
rc = GCRYERR_INV_PK_ALGO;
|
|
ready:
|
|
if( !rc && DBG_CIPHER ) {
|
|
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 int
|
|
pubkey_sign( int algo, MPI *resarr, MPI data, MPI *skey )
|
|
{
|
|
int i, rc;
|
|
|
|
if( DBG_CIPHER ) {
|
|
log_debug("pubkey_sign: algo=%d\n", algo );
|
|
for(i=0; i < pubkey_get_nskey(algo); i++ )
|
|
log_mpidump(" skey:", skey[i] );
|
|
log_mpidump(" data:", data );
|
|
}
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
rc = (*pubkey_table[i].sign)( algo, resarr, data, skey );
|
|
goto ready;
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
rc = GCRYERR_INV_PK_ALGO;
|
|
ready:
|
|
if( !rc && DBG_CIPHER ) {
|
|
for(i=0; i < pubkey_get_nsig(algo); i++ )
|
|
log_mpidump(" sig:", resarr[i] );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/****************
|
|
* Verify a public key signature.
|
|
* Return 0 if the signature is good
|
|
*/
|
|
static int
|
|
pubkey_verify( int algo, MPI hash, MPI *data, MPI *pkey,
|
|
int (*cmp)(void *, MPI), void *opaquev )
|
|
{
|
|
int i, rc;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
rc = (*pubkey_table[i].verify)( algo, hash, data, pkey,
|
|
cmp, opaquev );
|
|
goto ready;
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
rc = GCRYERR_INV_PK_ALGO;
|
|
ready:
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* 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
|
|
* Provide a SE with the first element be either "private-key" or
|
|
* or "public-key". the followed by a list with its first element
|
|
* be one of the above algorithm identifiers and the following
|
|
* 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.
|
|
*
|
|
* FIXME: Allow for encrypted secret keys here.
|
|
*
|
|
* 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 int
|
|
sexp_to_key( GCRY_SEXP sexp, int want_private, MPI **retarray, int *retalgo)
|
|
{
|
|
GCRY_SEXP list, l2;
|
|
const char *name;
|
|
const char *s;
|
|
size_t n;
|
|
int i, idx;
|
|
int algo;
|
|
const char *elems1, *elems2;
|
|
GCRY_MPI *array;
|
|
|
|
/* check that the first element is valid */
|
|
list = gcry_sexp_find_token( sexp, want_private? "private-key"
|
|
:"public-key", 0 );
|
|
if( !list )
|
|
return GCRYERR_INV_OBJ; /* Does not contain a public- or private-key object */
|
|
list = gcry_sexp_cdr( list );
|
|
if( !list )
|
|
return GCRYERR_NO_OBJ; /* no cdr for the key object */
|
|
name = gcry_sexp_car_data( list, &n );
|
|
if( !name )
|
|
return GCRYERR_INV_OBJ; /* invalid structure of object */
|
|
for(i=0; (s=algo_info_table[i].name); i++ ) {
|
|
if( strlen(s) == n && !memcmp( s, name, n ) )
|
|
break;
|
|
}
|
|
if( !s )
|
|
return GCRYERR_INV_PK_ALGO; /* unknown algorithm */
|
|
algo = algo_info_table[i].algo;
|
|
elems1 = algo_info_table[i].common_elements;
|
|
elems2 = want_private? algo_info_table[i].secret_elements
|
|
: algo_info_table[i].public_elements;
|
|
array = g10_calloc( strlen(elems1)+strlen(elems2)+1, sizeof *array );
|
|
if( !array )
|
|
return GCRYERR_NO_MEM;
|
|
|
|
idx = 0;
|
|
for(s=elems1; *s; s++, idx++ ) {
|
|
l2 = gcry_sexp_find_token( list, s, 1 );
|
|
if( !l2 ) {
|
|
g10_free( array );
|
|
return GCRYERR_NO_OBJ; /* required parameter not found */
|
|
}
|
|
array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
|
|
if( !array[idx] ) {
|
|
g10_free( array );
|
|
return GCRYERR_INV_OBJ; /* required parameter is invalid */
|
|
}
|
|
}
|
|
for(s=elems2; *s; s++, idx++ ) {
|
|
l2 = gcry_sexp_find_token( list, s, 1 );
|
|
if( !l2 ) {
|
|
g10_free( array );
|
|
return GCRYERR_NO_OBJ; /* required parameter not found */
|
|
}
|
|
/* FIXME: put the MPI in secure memory when needed */
|
|
array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
|
|
if( !array[idx] ) {
|
|
g10_free( array );
|
|
return GCRYERR_INV_OBJ; /* required parameter is invalid */
|
|
}
|
|
}
|
|
|
|
*retarray = array;
|
|
*retalgo = algo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sexp_to_sig( GCRY_SEXP sexp, MPI **retarray, int *retalgo)
|
|
{
|
|
GCRY_SEXP list, l2;
|
|
const char *name;
|
|
const char *s;
|
|
size_t n;
|
|
int i, idx;
|
|
int algo;
|
|
const char *elems;
|
|
GCRY_MPI *array;
|
|
|
|
/* check that the first element is valid */
|
|
list = gcry_sexp_find_token( sexp, "sig-val" , 0 );
|
|
if( !list )
|
|
return GCRYERR_INV_OBJ; /* Does not contain a signature value object */
|
|
list = gcry_sexp_cdr( list );
|
|
if( !list )
|
|
return GCRYERR_NO_OBJ; /* no cdr for the sig object */
|
|
name = gcry_sexp_car_data( list, &n );
|
|
if( !name )
|
|
return GCRYERR_INV_OBJ; /* invalid structure of object */
|
|
for(i=0; (s=sig_info_table[i].name); i++ ) {
|
|
if( strlen(s) == n && !memcmp( s, name, n ) )
|
|
break;
|
|
}
|
|
if( !s )
|
|
return GCRYERR_INV_PK_ALGO; /* unknown algorithm */
|
|
algo = sig_info_table[i].algo;
|
|
elems = sig_info_table[i].elements;
|
|
array = g10_calloc( (strlen(elems)+1) , sizeof *array );
|
|
if( !array )
|
|
return GCRYERR_NO_MEM;
|
|
|
|
idx = 0;
|
|
for(s=elems; *s; s++, idx++ ) {
|
|
l2 = gcry_sexp_find_token( list, s, 1 );
|
|
if( !l2 ) {
|
|
g10_free( array );
|
|
return GCRYERR_NO_OBJ; /* required parameter not found */
|
|
}
|
|
array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
|
|
if( !array[idx] ) {
|
|
g10_free( array );
|
|
return GCRYERR_INV_OBJ; /* required parameter is invalid */
|
|
}
|
|
}
|
|
|
|
*retarray = array;
|
|
*retalgo = algo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Take sexp and return an array of MPI as used for our internal decrypt
|
|
* function.
|
|
*/
|
|
static int
|
|
sexp_to_enc( GCRY_SEXP sexp, MPI **retarray, int *retalgo)
|
|
{
|
|
GCRY_SEXP list, l2;
|
|
const char *name;
|
|
const char *s;
|
|
size_t n;
|
|
int i, idx;
|
|
int algo;
|
|
const char *elems;
|
|
GCRY_MPI *array;
|
|
|
|
/* check that the first element is valid */
|
|
list = gcry_sexp_find_token( sexp, "enc-val" , 0 );
|
|
if( !list )
|
|
return GCRYERR_INV_OBJ; /* Does not contain a encrypted value object */
|
|
list = gcry_sexp_cdr( list );
|
|
if( !list )
|
|
return GCRYERR_NO_OBJ; /* no cdr for the data object */
|
|
name = gcry_sexp_car_data( list, &n );
|
|
if( !name )
|
|
return GCRYERR_INV_OBJ; /* invalid structure of object */
|
|
for(i=0; (s=enc_info_table[i].name); i++ ) {
|
|
if( strlen(s) == n && !memcmp( s, name, n ) )
|
|
break;
|
|
}
|
|
if( !s )
|
|
return GCRYERR_INV_PK_ALGO; /* unknown algorithm */
|
|
algo = enc_info_table[i].algo;
|
|
elems = enc_info_table[i].elements;
|
|
array = g10_calloc( (strlen(elems)+1) , sizeof *array );
|
|
if( !array )
|
|
return GCRYERR_NO_MEM;
|
|
|
|
idx = 0;
|
|
for(s=elems; *s; s++, idx++ ) {
|
|
l2 = gcry_sexp_find_token( list, s, 1 );
|
|
if( !l2 ) {
|
|
g10_free( array );
|
|
return GCRYERR_NO_OBJ; /* required parameter not found */
|
|
}
|
|
array[idx] = gcry_sexp_cdr_mpi( l2, GCRYMPI_FMT_USG );
|
|
if( !array[idx] ) {
|
|
g10_free( array );
|
|
return GCRYERR_INV_OBJ; /* required parameter is invalid */
|
|
}
|
|
}
|
|
|
|
*retarray = array;
|
|
*retalgo = algo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* 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. The function returns a a sexp which may
|
|
* be passed tp to pk_decrypt.
|
|
* Later versions of this functions may take more complex input data.
|
|
*
|
|
* Returns: 0 or an errorcode.
|
|
*
|
|
* s_data = (<mpi>)
|
|
* s_pkey = <key-as-defined-in-sexp_to_key>
|
|
* r_ciph = (enc-val
|
|
* (<algo>
|
|
* (<param_name1> <mpi>)
|
|
* ...
|
|
* (<param_namen> <mpi>)
|
|
* ))
|
|
*/
|
|
int
|
|
gcry_pk_encrypt( GCRY_SEXP *r_ciph, GCRY_SEXP s_data, GCRY_SEXP s_pkey )
|
|
{
|
|
MPI *pkey, data, *ciph;
|
|
const char *algo_name, *algo_elems;
|
|
GCRY_SEXP *s_elems;
|
|
int i, rc, algo;
|
|
|
|
/* get the key */
|
|
rc = sexp_to_key( s_pkey, 0, &pkey, &algo );
|
|
if( rc ) {
|
|
return rc;
|
|
}
|
|
|
|
/* get the name and the required size of the return value */
|
|
for(i=0; (algo_name = enc_info_table[i].name); i++ ) {
|
|
if( enc_info_table[i].algo == algo )
|
|
break;
|
|
}
|
|
if( !algo_name ) {
|
|
release_mpi_array( pkey );
|
|
return GCRYERR_INV_PK_ALGO;
|
|
}
|
|
algo_elems = enc_info_table[i].elements;
|
|
|
|
/* get the stuff we want to encrypt */
|
|
data = gcry_sexp_car_mpi( s_data, 0 );
|
|
if( !data ) {
|
|
release_mpi_array( pkey );
|
|
return GCRYERR_INV_OBJ;
|
|
}
|
|
|
|
/* Now we can encrypt data to ciph */
|
|
ciph = g10_xcalloc( (strlen(algo_elems)+1) , sizeof *ciph );
|
|
rc = pubkey_encrypt( algo, ciph, data, pkey );
|
|
release_mpi_array( pkey );
|
|
mpi_free( data );
|
|
if( rc ) {
|
|
g10_free( ciph );
|
|
return rc;
|
|
}
|
|
|
|
/* We did it. Now build the return list */
|
|
s_elems = g10_xcalloc( (strlen(algo_elems)+2), sizeof *s_elems );
|
|
s_elems[0] = SEXP_NEW( algo_name, 0 );
|
|
for(i=0; algo_elems[i]; i++ ) {
|
|
char tmp[2];
|
|
tmp[0] = algo_elems[i];
|
|
tmp[1] = 0;
|
|
s_elems[i+1] = gcry_sexp_new_name_mpi( tmp, ciph[i] );
|
|
}
|
|
release_mpi_array( ciph );
|
|
g10_free( ciph );
|
|
|
|
*r_ciph = SEXP_CONS( SEXP_NEW( "enc-val", 0 ),
|
|
gcry_sexp_alist( s_elems ) );
|
|
|
|
g10_free( s_elems );
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* 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. Currently the function returns
|
|
* simply a MPI. Later versions of this functions may return a more
|
|
* complex data structure.
|
|
*
|
|
* Returns: 0 or an errorcode.
|
|
*
|
|
* s_data = (enc-val
|
|
* (<algo>
|
|
* (<param_name1> <mpi>)
|
|
* ...
|
|
* (<param_namen> <mpi>)
|
|
* ))
|
|
* s_skey = <key-as-defined-in-sexp_to_key>
|
|
* r_plain= (<mpi>) FIXME: Return a more structered value
|
|
*/
|
|
int
|
|
gcry_pk_decrypt( GCRY_SEXP *r_plain, GCRY_SEXP s_data, GCRY_SEXP s_skey )
|
|
{
|
|
MPI *skey, *data, plain;
|
|
int rc, algo, dataalgo;
|
|
|
|
rc = sexp_to_key( s_skey, 1, &skey, &algo );
|
|
if( rc ) {
|
|
return rc;
|
|
}
|
|
rc = sexp_to_enc( s_data, &data, &dataalgo );
|
|
if( rc ) {
|
|
release_mpi_array( skey );
|
|
return rc;
|
|
}
|
|
if( algo != dataalgo ) {
|
|
release_mpi_array( skey );
|
|
release_mpi_array( data );
|
|
return -1; /* fixme: add real errornumber - algo does not match */
|
|
}
|
|
|
|
rc = pubkey_decrypt( algo, &plain, data, skey );
|
|
if( rc ) {
|
|
release_mpi_array( skey );
|
|
release_mpi_array( data );
|
|
return -1; /* fixme: add real errornumber - decryption failed */
|
|
}
|
|
|
|
*r_plain = gcry_sexp_new_mpi( plain );
|
|
mpi_free( plain );
|
|
release_mpi_array( data );
|
|
release_mpi_array( skey );
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* 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. Later versions of this functions may provide padding
|
|
* and other things depending on data.
|
|
*
|
|
* 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 = (<mpi>)
|
|
* s_skey = <key-as-defined-in-sexp_to_key>
|
|
* r_sig = (sig-val
|
|
* (<algo>
|
|
* (<param_name1> <mpi>)
|
|
* ...
|
|
* (<param_namen> <mpi>)
|
|
* ))
|
|
*/
|
|
int
|
|
gcry_pk_sign( GCRY_SEXP *r_sig, GCRY_SEXP s_hash, GCRY_SEXP s_skey )
|
|
{
|
|
MPI *skey, hash;
|
|
MPI *result;
|
|
int i, algo, rc;
|
|
const char *algo_name, *algo_elems;
|
|
GCRY_SEXP *s_elems;
|
|
|
|
rc = sexp_to_key( s_skey, 1, &skey, &algo );
|
|
if( rc )
|
|
return rc;
|
|
|
|
/* get the name and the required size of the result array */
|
|
for(i=0; (algo_name = sig_info_table[i].name); i++ ) {
|
|
if( sig_info_table[i].algo == algo )
|
|
break;
|
|
}
|
|
if( !algo_name ) {
|
|
release_mpi_array( skey );
|
|
return -4; /* oops: unknown algorithm */
|
|
}
|
|
algo_elems = sig_info_table[i].elements;
|
|
|
|
/* get the stuff we want to sign */
|
|
hash = gcry_sexp_car_mpi( s_hash, 0 );
|
|
if( !hash ) {
|
|
release_mpi_array( skey );
|
|
return -1; /* fixme: get a real errorcode for this */
|
|
}
|
|
result = g10_xcalloc( (strlen(algo_elems)+1) , sizeof *result );
|
|
rc = pubkey_sign( algo, result, hash, skey );
|
|
release_mpi_array( skey );
|
|
mpi_free( hash );
|
|
if( rc ) {
|
|
g10_free( result );
|
|
return rc;
|
|
}
|
|
|
|
s_elems = g10_xcalloc( (strlen(algo_elems)+2), sizeof *s_elems );
|
|
s_elems[0] = SEXP_NEW( algo_name, 0 );
|
|
for(i=0; algo_elems[i]; i++ ) {
|
|
char tmp[2];
|
|
tmp[0] = algo_elems[i];
|
|
tmp[1] = 0;
|
|
s_elems[i+1] = gcry_sexp_new_name_mpi( tmp, result[i] );
|
|
}
|
|
release_mpi_array( result );
|
|
g10_free( result );
|
|
|
|
*r_sig = SEXP_CONS( SEXP_NEW( "sig-val", 0 ),
|
|
gcry_sexp_alist( s_elems ) );
|
|
|
|
g10_free( s_elems );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Verify a sgnature. 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.
|
|
*/
|
|
int
|
|
gcry_pk_verify( GCRY_SEXP s_sig, GCRY_SEXP s_hash, GCRY_SEXP s_pkey )
|
|
{
|
|
MPI *pkey, hash, *sig;
|
|
int algo, sigalgo;
|
|
int rc;
|
|
|
|
rc = sexp_to_key( s_pkey, 0, &pkey, &algo );
|
|
if( rc )
|
|
return rc;
|
|
rc = sexp_to_sig( s_sig, &sig, &sigalgo );
|
|
if( rc ) {
|
|
release_mpi_array( pkey );
|
|
return rc;
|
|
}
|
|
if( algo != sigalgo ) {
|
|
release_mpi_array( pkey );
|
|
release_mpi_array( sig );
|
|
return -1; /* fixme: add real errornumber - algo does not match */
|
|
}
|
|
|
|
hash = gcry_sexp_car_mpi( s_hash, 0 );
|
|
if( !hash ) {
|
|
release_mpi_array( pkey );
|
|
release_mpi_array( sig );
|
|
return -1; /* fixme: get a real errorcode for this */
|
|
}
|
|
|
|
rc = pubkey_verify( algo, hash, sig, pkey, NULL, NULL );
|
|
release_mpi_array( pkey );
|
|
release_mpi_array( sig );
|
|
mpi_free(hash);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Test a key. This may be used either for a public or a secret key
|
|
* to see whether internal structre is valid.
|
|
*
|
|
* Returns: 0 or an errorcode.
|
|
*
|
|
* s_key = <key-as-defined-in-sexp_to_key>
|
|
*/
|
|
int
|
|
gcry_pk_testkey( GCRY_SEXP s_key )
|
|
{
|
|
MPI *key;
|
|
int rc, algo;
|
|
|
|
/* Note we currently support only secret key checking */
|
|
rc = sexp_to_key( s_key, 1, &key, &algo );
|
|
if( rc ) {
|
|
return rc;
|
|
}
|
|
|
|
rc = pubkey_check_secret_key( algo, key );
|
|
release_mpi_array( key );
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Create a public key pair and return it in r_key.
|
|
* How the key is created depends on s_parms:
|
|
* (GNU
|
|
* (genkey
|
|
* (algo
|
|
* (parameter_name_1 ....)
|
|
* ....
|
|
* (parameter_name_n ....)
|
|
* )))
|
|
* The key is returned in a format depending on the
|
|
* algorithm. Both, private and secret key are returned
|
|
* and optionally some additional informatin.
|
|
*/
|
|
int
|
|
gcry_pk_genkey( GCRY_SEXP *r_key, GCRY_SEXP s_parms )
|
|
{
|
|
return GCRYERR_NOT_IMPL;
|
|
}
|
|
|
|
/****************
|
|
* 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 propoerties of the key?
|
|
*/
|
|
unsigned int
|
|
gcry_pk_get_nbits( GCRY_SEXP key )
|
|
{
|
|
int rc, i, algo;
|
|
MPI *keyarr;
|
|
unsigned int nbits = 0;
|
|
|
|
rc = sexp_to_key( key, 0, &keyarr, &algo );
|
|
if( rc == GCRYERR_INV_OBJ )
|
|
rc = sexp_to_key( key, 0, &keyarr, &algo );
|
|
if( rc )
|
|
return 0;
|
|
|
|
do {
|
|
for(i=0; pubkey_table[i].name; i++ )
|
|
if( pubkey_table[i].algo == algo ) {
|
|
nbits = (*pubkey_table[i].get_nbits)( algo, keyarr );
|
|
goto leave;
|
|
}
|
|
} while( load_pubkey_modules() );
|
|
if( is_RSA(algo) ) /* we always wanna see the length of a key :-) */
|
|
nbits = mpi_get_nbits( keyarr[0] );
|
|
leave:
|
|
release_mpi_array( keyarr );
|
|
return nbits;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
gcry_pk_ctl( int cmd, void *buffer, size_t buflen)
|
|
{
|
|
switch( cmd ) {
|
|
case GCRYCTL_DISABLE_ALGO:
|
|
/* this one expects a buffer pointing to an
|
|
* integer with the algo number.
|
|
*/
|
|
if( !buffer || buflen != sizeof(int) )
|
|
return set_lasterr( GCRYERR_INV_CIPHER_ALGO );
|
|
disable_pubkey_algo( *(int*)buffer );
|
|
break;
|
|
|
|
default:
|
|
return set_lasterr( GCRYERR_INV_OP );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Return information about the given algorithm
|
|
* WHAT select 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;
|
|
*
|
|
* On error the value -1 is returned and the error reason may be
|
|
* retrieved by gcry_errno().
|
|
* Note: Because this function is in most caes used to return an
|
|
* integer value, we can make it easier for the caller to just look at
|
|
* the return value. The caller will in all cases consult the value
|
|
* and thereby detecting whether a error occured or not (i.e. while checking
|
|
* the block size)
|
|
*/
|
|
int
|
|
gcry_pk_algo_info( int algo, int what, void *buffer, size_t *nbytes)
|
|
{
|
|
switch( what ) {
|
|
case GCRYCTL_TEST_ALGO: {
|
|
int use = nbytes? *nbytes: 0;
|
|
if( buffer ) {
|
|
set_lasterr( GCRYERR_INV_ARG );
|
|
return -1;
|
|
}
|
|
if( check_pubkey_algo( algo, use ) ) {
|
|
set_lasterr( GCRYERR_INV_PK_ALGO );
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GCRYCTL_GET_ALGO_NPKEY: return pubkey_get_npkey( algo );
|
|
case GCRYCTL_GET_ALGO_NSKEY: return pubkey_get_nskey( algo );
|
|
case GCRYCTL_GET_ALGO_NSIGN: return pubkey_get_nsig( algo );
|
|
case GCRYCTL_GET_ALGO_NENCR: return pubkey_get_nenc( algo );
|
|
|
|
default:
|
|
set_lasterr( GCRYERR_INV_OP );
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|