1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-25 01:42:45 +02:00

Reworked the ECC changes to better fit into the Libgcrypt API.

See ChangeLog for details.  Key generation, signing and verification works.
Encryption does not yet work.  Requires latest Libgcrypt changes.
This commit is contained in:
Werner Koch 2011-01-31 09:27:06 +01:00
parent a58a6b5b78
commit 0fb0bb8d9a
12 changed files with 622 additions and 359 deletions

View File

@ -1,3 +1,8 @@
2011-01-27 Werner Koch <wk@g10code.com>
* protect.c (protect_info): Adjust ECDSA and ECDH parameter names.
Add "ecc".
2011-01-19 Werner Koch <wk@g10code.com>
* trustlist.c (read_one_trustfile): Also chop an CR.

View File

@ -52,8 +52,9 @@ static struct {
{ "rsa", "nedpqu", 2, 5 },
{ "dsa", "pqgyx", 4, 4 },
{ "elg", "pgyx", 3, 3 },
{ "ecdsa","cqd", 2, 2 },
{ "ecdh", "cqpd", 3, 3 },
{ "ecdsa","pabgnqd", 6, 6 },
{ "ecdh", "pabgnqd", 6, 6 },
{ "ecc", "pabgnqd", 6, 6 },
{ NULL }
};

View File

@ -1,3 +1,37 @@
2011-01-30 Werner Koch <wk@g10code.com>
* keyid.c (keygrip_from_pk): Adjust ECC cases.
* pkglue.c (pk_verify): Ditto.
* parse-packet.c (read_size_body): Rewrite.
(parse_key): Simply ECC case.
(parse_pubkeyenc): Ditto.
* misc.c (pubkey_get_npkey): Special case ECC.
(pubkey_get_nskey): Ditto.
(mpi_print): Support printfing of opaque values.
(openpgp_oid_to_str): New.
(pubkey_nbits): For ECC pass curve parameter.
* ecdh.c (pk_ecdh_default_params): Change to return an opaque MPI.
* build-packet.c (do_key): Automatically handle real and opaque
key parameters.
(write_fake_data): Return an error code.
(mpi_write): Support writing opaque MPIs.
(do_pubkey_enc): Simplify ECC handling.
2011-01-28 Werner Koch <wk@g10code.com>
* keygen.c (gen_ecc): Rewrite. Select a named curve and create a
keyspec based on that.
(pk_ecc_build_key_params): Remove.
(get_parameter_algo): Map algo number.
(ecckey_from_sexp): New.
* misc.c (map_pk_gcry_to_openpgp): New.
(openpgp_oid_from_str): New. Based on libksba code.
2011-01-26 Werner Koch <wk@g10code.com>
* misc.c (ecdsa_qbits_from_Q): Use unsigned int.

View File

@ -1,6 +1,6 @@
/* build-packet.c - assemble packets and write them
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2010 Free Software Foundation, Inc.
* 2006, 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -160,19 +160,31 @@ build_packet( IOBUF out, PACKET *pkt )
static int
mpi_write (iobuf_t out, gcry_mpi_t a)
{
char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
size_t nbytes;
int rc;
nbytes = DIM(buffer);
rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
if( !rc )
rc = iobuf_write( out, buffer, nbytes );
else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT )
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
/* The buffer was too small. We better tell the user about the MPI. */
rc = gpg_error (GPG_ERR_TOO_LARGE);
size_t nbits;
const void *p;
p = gcry_mpi_get_opaque (a, &nbits);
rc = iobuf_write (out, p, (nbits+7)/8);
}
else
{
char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
size_t nbytes;
nbytes = DIM(buffer);
rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
if( !rc )
rc = iobuf_write( out, buffer, nbytes );
else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT )
{
log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
/* The buffer was too small. We better tell the user about the MPI. */
rc = gpg_error (GPG_ERR_TOO_LARGE);
}
}
return rc;
@ -252,19 +264,20 @@ calc_packet_length( PACKET *pkt )
return n;
}
static void
static gpg_error_t
write_fake_data (IOBUF out, gcry_mpi_t a)
{
if (a)
{
unsigned int n;
void *p;
unsigned int n;
void *p;
p = gcry_mpi_get_opaque ( a, &n );
iobuf_write (out, p, (n+7)/8 );
}
if (!a)
return 0;
p = gcry_mpi_get_opaque ( a, &n);
return iobuf_write (out, p, (n+7)/8 );
}
static int
do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
{
@ -326,33 +339,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
}
assert (npkey < nskey);
/* Writing the public parameters is easy. Except if we do an
adjustment for ECC OID and possibly KEK params for ECDH. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
for (i=0; i < npkey; i++ )
{
/* Write DER of OID with preceeding length byte. */
err = write_size_body_mpi (a, pk->pkey[0]);
err = mpi_write (a, pk->pkey[i]);
if (err)
goto leave;
/* Write point Q, the public key. */
err = mpi_write (a, pk->pkey[1]);
if (err)
goto leave;
/* Write one more public field for ECDH. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
err = write_size_body_mpi (a, pk->pkey[2]);
if (err)
goto leave;
}
}
else
{
for (i=0; i < npkey; i++ )
if ((err = mpi_write (a, pk->pkey[i])))
goto leave;
}
@ -520,20 +511,8 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
if ( !n )
write_fake_data( a, enc->data[0] );
if (enc->pubkey_algo == PUBKEY_ALGO_ECDH )
{
/* The second field persists as a LEN+field structure, even
* though it is stored for uniformity as an MPI internally. */
assert (n == 2);
rc = mpi_write (a, enc->data[0]);
if (!rc)
rc = write_size_body_mpi (a, enc->data[1]);
}
else
{
for (i=0; i < n && !rc ; i++ )
rc = mpi_write(a, enc->data[i] );
}
for (i=0; i < n && !rc ; i++ )
rc = mpi_write (a, enc->data[i]);
if (!rc)
{

View File

@ -48,16 +48,17 @@ static const struct
/* Returns allocated (binary) KEK parameters; the size is returned in
sizeout. The caller must free the returned value. Returns NULL
and sets ERRNO on error. */
byte *
pk_ecdh_default_params (unsigned int qbits, size_t *sizeout)
/* Return KEK parameters as an opaque MPI The caller must free the
returned value. Returns NULL and sets ERRNO on error. */
gcry_mpi_t
pk_ecdh_default_params (unsigned int qbits)
{
byte kek_params[4];
byte *kek_params;
int i;
byte *buffer;
kek_params = xtrymalloc (4);
if (!kek_params)
return NULL;
kek_params[0] = 3; /* Number of bytes to follow. */
kek_params[1] = 1; /* Version for KDF+AESWRAP. */
@ -78,12 +79,7 @@ pk_ecdh_default_params (unsigned int qbits, size_t *sizeout)
if (DBG_CIPHER)
log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) );
buffer = xtrymalloc (sizeof(kek_params));
if (!buffer)
return NULL;
memcpy (buffer, kek_params, sizeof (kek_params));
*sizeout = sizeof (kek_params);
return buffer;
return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8);
}
@ -411,8 +407,8 @@ gen_k (unsigned nbits)
unsigned char *buffer;
if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k))
BUG ();
log_debug("ephemeral scalar MPI #0: %s\n", buffer);
gcry_free( buffer );
log_debug ("ephemeral scalar MPI #0: %s\n", buffer);
gcry_free (buffer);
}
return k;

View File

@ -1081,7 +1081,107 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
}
static gpg_error_t
ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
{
gpg_error_t err;
gcry_sexp_t list, l2;
char *curve;
int i;
const char *oidstr;
unsigned int nbits;
array[0] = NULL;
array[1] = NULL;
array[2] = NULL;
list = gcry_sexp_find_token (sexp, "public-key", 0);
if (!list)
return gpg_error (GPG_ERR_INV_OBJ);
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
if (!list)
return gpg_error (GPG_ERR_NO_OBJ);
l2 = gcry_sexp_find_token (list, "curve", 0);
if (!l2)
{
err = gpg_error (GPG_ERR_NO_OBJ);
goto leave;
}
curve = gcry_sexp_nth_string (l2, 1);
if (!curve)
{
err = gpg_error (GPG_ERR_NO_OBJ);
goto leave;
}
gcry_sexp_release (l2);
if (!strcmp (curve, "NIST P-256"))
{
oidstr = "1.2.840.10045.3.1.7";
nbits = 256;
}
else if (!strcmp (curve, "NIST P-384"))
{
oidstr = "1.3.132.0.34";
nbits = 384;
}
else if (!strcmp (curve, "NIST P-521"))
{
oidstr = "1.3.132.0.35";
nbits = 521;
}
else
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
err = openpgp_oid_from_str (oidstr, &array[0]);
if (err)
goto leave;
l2 = gcry_sexp_find_token (list, "q", 0);
if (!l2)
{
err = gpg_error (GPG_ERR_NO_OBJ);
goto leave;
}
array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
if (!array[1])
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
gcry_sexp_release (list);
if (algo == PUBKEY_ALGO_ECDH)
{
array[2] = pk_ecdh_default_params (nbits);
if (!array[2])
{
err = gpg_error_from_syserror ();
goto leave;
}
}
leave:
if (err)
{
for (i=0; i < 3; i++)
{
gcry_mpi_release (array[i]);
array[i] = NULL;
}
}
return 0;
}
/* Extract key parameters from SEXP and store them in ARRAY. ELEMS is
a string where each character denotes a parameter name. TOPNAME is
the name of the top element above the elements. */
static int
key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp,
const char *topname, const char *elems)
@ -1165,7 +1265,10 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
pk->expiredate = pk->timestamp + expireval;
pk->pubkey_algo = algo;
err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
err = ecckey_from_sexp (pk->pkey, s_key, algo);
else
err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
if (err)
{
log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
@ -1173,7 +1276,6 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
free_public_key (pk);
return err;
}
gcry_sexp_release (s_key);
pkt = xtrycalloc (1, sizeof *pkt);
if (!pkt)
@ -1329,126 +1431,45 @@ gen_dsa (unsigned int nbits, KBNODE pub_root,
/* Create an S-expression string out of QBITS, ALGO and the TRANSIENT
flag. On success a malloced string is returned, on failure NULL
and ERRNO is set. */
static char *
pk_ecc_build_key_params (int qbits, int algo, int transient)
{
byte *kek_params = NULL;
size_t kek_params_size;
char qbitsstr[35];
char *result;
size_t n;
/* KEK parameters are only needed for long term key generation. */
if (!transient && algo == PUBKEY_ALGO_ECDH)
{
kek_params = pk_ecdh_default_params (qbits, &kek_params_size);
if (!kek_params)
return NULL;
}
else
kek_params = NULL;
snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits);
if (algo == PUBKEY_ALGO_ECDSA || !kek_params)
{
result = xtryasprintf ("(genkey(%s(nbits %zu:%s)"
/**/ "(qbits %zu:%s)"
/**/ "(transient-key 1:%d)))",
algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
strlen (qbitsstr), qbitsstr,
strlen (qbitsstr), qbitsstr,
transient);
}
else
{
char *tmpstr;
assert (kek_params);
tmpstr = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)"
/**/ "(qbits %zu:%s)"
/**/ "(transient-key 1:%d)"
/**/ "(kek-params %zu:",
strlen (qbitsstr), qbitsstr,
strlen (qbitsstr), qbitsstr,
transient,
kek_params_size);
if (!tmpstr)
{
xfree (kek_params);
return NULL;
}
/* Append the binary KEK parmas. */
n = strlen (tmpstr);
result = xtryrealloc (tmpstr, n + kek_params_size + 4);
if (!result)
{
xfree (tmpstr);
xfree (kek_params);
return NULL;
}
memcpy (result + n, kek_params, kek_params_size);
strcpy (result + n + kek_params_size, ")))");
}
xfree (kek_params);
return result;
}
/*
* Generate an ECC key
*/
static gpg_error_t
gen_ecc (int algo, unsigned int nbits, KBNODE pub_root,
gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root,
u32 timestamp, u32 expireval, int is_subkey,
int keygen_flags, char **cache_nonce_addr)
{
int err;
unsigned int qbits;
gpg_error_t err;
const char *curve;
char *keyparms;
assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH);
if (pubkey_get_npkey (PUBKEY_ALGO_ECDSA) != 2
|| pubkey_get_nskey (PUBKEY_ALGO_ECDSA) != 3
|| pubkey_get_npkey (PUBKEY_ALGO_ECDH) != 3
|| pubkey_get_nskey (PUBKEY_ALGO_ECDH) != 4)
{
log_error ("broken version of Libgcrypt\n");
return gpg_error (GPG_ERR_INTERNAL); /* ABI silently changed. */
}
/* For now we may only use one of the 3 NISY curves. */
if (nbits <= 256)
curve = "NIST P-256";
else if (nbits <= 384)
curve = "NIST P-384";
else
curve = "NIST P-521";
if (nbits != 256 && nbits != 384 && nbits != 521)
{
log_info (_("keysize invalid; using %u bits\n"), 256);
/* FIXME: Where do we set it to 256? */
}
/* Figure out a Q size based on the key size. See gen_dsa for more
details. Due to 8-bit rounding we may get 528 here instead of 521. */
nbits = qbits = (nbits < 521 ? nbits : 521 );
keyparms = pk_ecc_build_key_params
(qbits, algo, !!( (keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
&& (keygen_flags & KEYGEN_FLAG_NO_PROTECTION)) );
keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))",
algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
strlen (curve), curve,
((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
&& (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
"(transient-key)" : "" );
if (!keyparms)
{
err = gpg_error_from_syserror ();
log_error ("ecc pk_ecc_build_key_params failed: %s\n",
gpg_strerror (err));
}
err = gpg_error_from_syserror ();
else
{
err = common_gen (keyparms, algo,
algo == PUBKEY_ALGO_ECDSA? "cq" : "cqp",
err = common_gen (keyparms, algo, "",
pub_root, timestamp, expireval, is_subkey,
keygen_flags, cache_nonce_addr);
xfree (keyparms);
}
return 0;
return err;
}
@ -2428,7 +2449,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key,
|| !strcmp (r->u.value, "ELG"))
i = GCRY_PK_ELG_E;
else
i = gcry_pk_map_name (r->u.value);
i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value));
if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S)
i = 0; /* we don't want to allow generation of these algorithms */

View File

@ -724,17 +724,20 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_ECDH:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecc(c%m)(q%m)))",
pk->pkey[0], pk->pkey[1]);
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
if (!curve)
err = gpg_error_from_syserror ();
else
{
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecc(curve%s)(q%m)))",
curve, pk->pkey[1]);
xfree (curve);
}
}
break;
/* case PUBKEY_ALGO_ECDH: */
/* err = gcry_sexp_build (&s_pkey, NULL, */
/* "(public-key(ecdh(c%m)(q%m)(p%m)))", */
/* pk->pkey[0], pk->pkey[1], pk->pkey[2]); */
/* break; */
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;

View File

@ -97,6 +97,7 @@ int openpgp_cipher_blocklen (int algo);
int openpgp_cipher_test_algo( int algo );
const char *openpgp_cipher_algo_name (int algo);
int map_pk_openpgp_to_gcry (int algo);
int map_pk_gcry_to_openpgp (enum gcry_pk_algos algo);
int openpgp_pk_test_algo( int algo );
int openpgp_pk_test_algo2 ( int algo, unsigned int use );
int openpgp_pk_algo_usage ( int algo );
@ -154,15 +155,21 @@ int is_valid_mailbox (const char *name);
const char *get_libexecdir (void);
int path_access(const char *file,int mode);
/* Temporary helpers. */
int pubkey_get_npkey( int algo );
int pubkey_get_nskey( int algo );
int pubkey_get_nsig( int algo );
int pubkey_get_nenc( int algo );
/* Temporary helpers. */
unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey );
int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
unsigned int ecdsa_qbits_from_Q (unsigned int qbits);
/* Other stuff */
gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
char *openpgp_oid_to_str (gcry_mpi_t a);
/*-- status.c --*/
void set_status_fd ( int fd );
int is_status_enabled ( void );
@ -300,7 +307,7 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
int export_seckeys (ctrl_t ctrl, strlist_t users);
int export_secsubkeys (ctrl_t ctrl, strlist_t users);
/* dearmor.c --*/
/*-- dearmor.c --*/
int dearmor_file( const char *fname );
int enarmor_file( const char *fname );

View File

@ -379,6 +379,19 @@ map_pk_openpgp_to_gcry (int algo)
}
}
/* Map Gcrypt public key algorithm numbers to those used by
OpenPGP. */
int
map_pk_gcry_to_openpgp (enum gcry_pk_algos algo)
{
switch (algo)
{
case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH;
default: return algo < 110 ? algo : 0;
}
}
/* Return the block length of an OpenPGP cipher algorithm. */
int
@ -1347,35 +1360,44 @@ path_access(const char *file,int mode)
/* Temporary helper. */
/* Return the number of public key parameters as used by OpenPGP. */
int
pubkey_get_npkey( int algo )
pubkey_get_npkey (int algo)
{
size_t n;
/* ECC is special. */
if (algo == PUBKEY_ALGO_ECDSA)
return 2;
else if (algo == PUBKEY_ALGO_ECDH)
return 3;
/* All other algorithms match those of Libgcrypt. */
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
else if (algo == PUBKEY_ALGO_ECDSA)
algo = GCRY_PK_ECDSA;
else if (algo == PUBKEY_ALGO_ECDH)
algo = GCRY_PK_ECDH;
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n))
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n))
n = 0;
return n;
}
/* Temporary helper. */
/* Return the number of secret key parameters as used by OpenPGP. */
int
pubkey_get_nskey( int algo )
pubkey_get_nskey (int algo)
{
size_t n;
/* ECC is special. */
if (algo == PUBKEY_ALGO_ECDSA)
return 3;
else if (algo == PUBKEY_ALGO_ECDH)
return 4;
/* All other algorithms match those of Libgcrypt. */
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
else if (algo == PUBKEY_ALGO_ECDSA)
algo = GCRY_PK_ECDSA;
else if (algo == PUBKEY_ALGO_ECDH)
algo = GCRY_PK_ECDH;
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n ))
n = 0;
return n;
@ -1383,33 +1405,40 @@ pubkey_get_nskey( int algo )
/* Temporary helper. */
int
pubkey_get_nsig( int algo )
pubkey_get_nsig (int algo)
{
size_t n;
/* ECC is special. */
if (algo == PUBKEY_ALGO_ECDSA)
return 2;
else if (algo == PUBKEY_ALGO_ECDH)
return 0;
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
else if (algo == PUBKEY_ALGO_ECDSA)
algo = GCRY_PK_ECDSA;
else if (algo == PUBKEY_ALGO_ECDH)
algo = GCRY_PK_ECDH;
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n))
n = 0;
return n;
}
/* Temporary helper. */
int
pubkey_get_nenc( int algo )
pubkey_get_nenc (int algo)
{
size_t n;
/* ECC is special. */
if (algo == PUBKEY_ALGO_ECDSA)
return 0;
else if (algo == PUBKEY_ALGO_ECDH)
return 2;
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
else if (algo == PUBKEY_ALGO_ECDSA)
algo = GCRY_PK_ECDSA;
else if (algo == PUBKEY_ALGO_ECDH)
algo = GCRY_PK_ECDH;
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n ))
n = 0;
return n;
@ -1442,9 +1471,16 @@ pubkey_nbits( int algo, gcry_mpi_t *key )
key[0], key[1] );
}
else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) {
rc = gcry_sexp_build ( &sexp, NULL,
"(public-key(ecc(c%m)(q%m)))",
key[0], key[1] /* not affecting the size calculation, so use 'ecc' == 'ecdsa' */ );
char *curve = openpgp_oid_to_str (key[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
{
rc = gcry_sexp_build (&sexp, NULL,
"(public-key(ecc(curve%s)(q%m)))",
curve, key[1]);
xfree (curve);
}
}
else
return 0;
@ -1472,6 +1508,19 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode)
n1 = gcry_mpi_get_nbits(a);
n += es_fprintf (fp, "[%u bits]", n1);
}
else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
unsigned char *p = gcry_mpi_get_opaque (a, &nbits);
if (!p)
n += es_fprintf (fp, "[invalid opaque value]");
else
{
nbits = (nbits + 7)/8;
for (; nbits; nbits--, p++)
n += es_fprintf (fp, "%02X", *p);
}
}
else
{
unsigned char *buffer;
@ -1501,3 +1550,206 @@ ecdsa_qbits_from_Q (unsigned int qbits)
qbits /= 2;
return qbits;
}
/* Helper for openpgp_oid_from_str. */
static size_t
make_flagged_int (unsigned long value, char *buf, size_t buflen)
{
int more = 0;
int shift;
/* fixme: figure out the number of bits in an ulong and start with
that value as shift (after making it a multiple of 7) a more
straigtforward implementation is to do it in reverse order using
a temporary buffer - saves a lot of compares */
for (more=0, shift=28; shift > 0; shift -= 7)
{
if (more || value >= (1<<shift))
{
buf[buflen++] = 0x80 | (value >> shift);
value -= (value >> shift) << shift;
more = 1;
}
}
buf[buflen++] = value;
return buflen;
}
/* Convert the OID given in dotted decimal form in STRING to an DER
* encoding and store it as an opaque value at R_MPI. The format of
* the DER encoded is not a regular ASN.1 object but the modified
* format as used by OpenPGP for the ECC curve description. On error
* the function returns and error code an NULL is stored at R_BUG.
* Note that scanning STRING stops at the first white space
* character. */
gpg_error_t
openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi)
{
unsigned char *buf;
size_t buflen;
unsigned long val1, val;
const char *endp;
int arcno;
*r_mpi = NULL;
if (!string || !*string)
return gpg_error (GPG_ERR_INV_VALUE);
/* We can safely assume that the encoded OID is shorter than the string. */
buf = xtrymalloc (1 + strlen (string) + 2);
if (!buf)
return gpg_error_from_syserror ();
/* Save the first byte for the length. */
buflen = 1;
val1 = 0; /* Avoid compiler warning. */
arcno = 0;
do {
arcno++;
val = strtoul (string, (char**)&endp, 10);
if (!digitp (string) || !(*endp == '.' || !*endp))
{
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
if (*endp == '.')
string = endp+1;
if (arcno == 1)
{
if (val > 2)
break; /* Not allowed, error catched below. */
val1 = val;
}
else if (arcno == 2)
{ /* Need to combine the first two arcs in one octet. */
if (val1 < 2)
{
if (val > 39)
{
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
buf[buflen++] = val1*40 + val;
}
else
{
val += 80;
buflen = make_flagged_int (val, buf, buflen);
}
}
else
{
buflen = make_flagged_int (val, buf, buflen);
}
} while (*endp == '.');
if (arcno == 1 || buflen < 2 || buflen > 254 )
{ /* It is not possible to encode only the first arc. */
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
*buf = buflen - 1;
*r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8);
if (!*r_mpi)
{
xfree (buf);
return gpg_error_from_syserror ();
}
return 0;
}
/* Return a malloced string represenation of the OID in the opaque MPI
A. In case of an error NULL is returned and ERRNO is set. */
char *
openpgp_oid_to_str (gcry_mpi_t a)
{
const unsigned char *buf;
size_t length;
char *string, *p;
int n = 0;
unsigned long val, valmask;
valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1));
if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
gpg_err_set_errno (EINVAL);
return NULL;
}
buf = gcry_mpi_get_opaque (a, &length);
length = (length+7)/8;
/* The first bytes gives the length; check consistency. */
if (!length || buf[0] != length -1)
{
gpg_err_set_errno (EINVAL);
return NULL;
}
/* Skip length byte. */
length--;
buf++;
/* To calculate the length of the string we can safely assume an
upper limit of 3 decimal characters per byte. Two extra bytes
account for the special first octect */
string = p = xtrymalloc (length*(1+3)+2+1);
if (!string)
return NULL;
if (!buf || !length)
{
*p = 0;
return string;
}
if (buf[0] < 40)
p += sprintf (p, "0.%d", buf[n]);
else if (buf[0] < 80)
p += sprintf (p, "1.%d", buf[n]-40);
else {
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
if ( (val & valmask) )
goto badoid; /* Overflow. */
val <<= 7;
val |= buf[n] & 0x7f;
}
val -= 80;
sprintf (p, "2.%lu", val);
p += strlen (p);
}
for (n++; n < length; n++)
{
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
if ( (val & valmask) )
goto badoid; /* Overflow. */
val <<= 7;
val |= buf[n] & 0x7f;
}
sprintf (p, ".%lu", val);
p += strlen (p);
}
*p = 0;
return string;
badoid:
/* Return a special OID (gnu.gnupg.badoid) to indicate the error
case. The OID is broken and thus we return one which can't do
any harm. Formally this does not need to be a bad OID but an OID
with an arc that can't be represented in a 32 bit word is more
than likely corrupt. */
xfree (string);
return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
}

View File

@ -741,51 +741,57 @@ read_rest (IOBUF inp, size_t pktlen, int partial)
}
/*
* Read a special size+body from inp into body[body_max_size] and
* return it in a buffer and as MPI. On success the number of
* consumed bytes will body[0]+1. The format of the content of the
* returned MPI is one byte LEN, following by LEN bytes. Caller is
* expected to pre-allocate fixed-size 255 byte buffer (or smaller
* when appropriate).
*/
static int
read_size_body (iobuf_t inp, byte *body, int body_max_size,
int pktlen, gcry_mpi_t *out )
/* Read a special size+body from INP. On success store an opaque MPI
with it at R_DATA. On error return an error code and store NULL at
R_DATA. Even in the error case store the number of read bytes at
R_NREAD. The caller shall pass the remaining size of the packet in
PKTLEN. */
static gpg_error_t
read_size_body (iobuf_t inp, int pktlen, size_t *r_nread,
gcry_mpi_t *r_data)
{
unsigned int n;
int rc;
gcry_mpi_t result;
char buffer[256];
char *tmpbuf;
int i, c, nbytes;
*out = NULL;
*r_nread = 0;
*r_data = NULL;
if( (n = iobuf_readbyte(inp)) == -1 )
{
return G10ERR_INVALID_PACKET;
}
if ( n >= body_max_size || n < 2)
{
log_error("invalid size+body field\n");
return G10ERR_INVALID_PACKET;
}
body[0] = n;
if ((n = iobuf_read(inp, body+1, n)) == -1)
{
log_error("invalid size+body field\n");
return G10ERR_INVALID_PACKET;
}
if (n+1 > pktlen)
{
log_error("size+body field is larger than the packet\n");
return G10ERR_INVALID_PACKET;
}
rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, body, n+1, NULL);
if (rc)
log_fatal ("mpi_scan failed: %s\n", gpg_strerror (rc));
if (!pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
c = iobuf_readbyte (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
pktlen--;
++*r_nread;
nbytes = c;
if (nbytes < 2 || nbytes > 254)
return gpg_error (GPG_ERR_INV_PACKET);
if (nbytes > pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
*out = result;
buffer[0] = nbytes;
return rc;
for (i = 0; i < nbytes; i++)
{
c = iobuf_get (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
++*r_nread;
buffer[1+i] = c;
}
tmpbuf = xtrymalloc (1 + nbytes);
if (!tmpbuf)
return gpg_error_from_syserror ();
memcpy (tmpbuf, buffer, 1 + nbytes);
*r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes));
if (!*r_data)
{
xfree (tmpbuf);
return gpg_error_from_syserror ();
}
return 0;
}
@ -988,46 +994,29 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
}
else
{
if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
for (i = 0; i < ndata; i++)
{
byte encr_buf[255];
assert (ndata == 2);
n = pktlen;
k->data[0] = mpi_read (inp, &n, 0);
pktlen -= n;
rc = read_size_body (inp, encr_buf, sizeof(encr_buf),
pktlen, k->data+1);
if (rc)
goto leave;
if (list_mode)
if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[0], mpi_print_mode );
es_putc ('\n', listfp);
es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]+1);
mpi_print (listfp, k->data[1], mpi_print_mode );
es_putc ('\n', listfp);
rc = read_size_body (inp, pktlen, &n, k->data+i);
pktlen -= n;
}
pktlen -= (encr_buf[0]+1);
}
else
{
for (i = 0; i < ndata; i++)
else
{
n = pktlen;
k->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
if (rc)
goto leave;
if (list_mode)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
}
}
@ -1989,7 +1978,6 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
unknown_pubkey_warning (algorithm);
}
if (!npkey)
{
/* Unknown algorithm - put data into an opaque MPI. */
@ -2001,79 +1989,32 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
}
else
{
/* Fill in public key parameters. */
if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_ECDH)
for (i = 0; i < npkey; i++)
{
/* FIXME: The code in this function ignores the errors. */
byte name_oid[256];
err = read_size_body (inp, name_oid, sizeof(name_oid),
pktlen, pk->pkey+0);
if (err)
goto leave;
n = name_oid[0];
if (list_mode)
es_fprintf (listfp, "\tpkey[0]: curve OID [%d] ...%02x %02x\n",
n, name_oid[1+n-2], name_oid[1+n-1]);
pktlen -= (n+1);
/* Set item [1], which corresponds to the public key; these
two fields are all we need to uniquely define the key/ */
n = pktlen;
pk->pkey[1] = mpi_read( inp, &n, 0 );
pktlen -=n;
if (!pk->pkey[1])
err = gpg_error (GPG_ERR_INV_PACKET);
else if (list_mode)
if ((algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2))
{
es_fprintf (listfp, "\tpkey[1]: ");
mpi_print (listfp, pk->pkey[1], mpi_print_mode);
es_putc ('\n', listfp);
}
/* One more field for ECDH. */
if (algorithm == PUBKEY_ALGO_ECDH)
{
/* (NAMEOID holds the KEK params.) */
err = read_size_body (inp, name_oid, sizeof(name_oid),
pktlen, pk->pkey+2);
if (err)
goto leave;
n = name_oid[0];
if (name_oid[1] != 1)
{
log_error ("invalid ecdh KEK parameters field type in "
"private key: understand type 1, "
"but found 0x%02x\n", name_oid[1]);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if (list_mode)
es_fprintf (listfp, "\tpkey[2]: KEK params type=01 "
"hash:%d sym-algo:%d\n",
name_oid[1+n-2], name_oid[1+n-1]);
pktlen -= (n+1);
err = read_size_body (inp, pktlen, &n, pk->pkey+i);
pktlen -= n;
}
}
else
{
for (i = 0; i < npkey; i++)
else
{
n = pktlen;
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{
es_fprintf (listfp, "\tpkey[%d]: ", i);
mpi_print (listfp, pk->pkey[i], mpi_print_mode);
es_putc ('\n', listfp);
}
if (!pk->pkey[i])
err = gpg_error (GPG_ERR_INV_PACKET);
}
if (err)
goto leave;
if (list_mode)
{
es_fprintf (listfp, "\tpkey[%d]: ", i);
mpi_print (listfp, pk->pkey[i], mpi_print_mode);
es_putc ('\n', listfp);
}
}
if (err)
goto leave;
}
if (list_mode)
keyid_from_pk (pk, keyid);

View File

@ -79,8 +79,16 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
}
else if (pkalgo == GCRY_PK_ECDSA) /* Same as GCRY_PK_ECDH */
{
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecdsa(c%m)(q%m)))", pkey[0], pkey[1]);
char *curve = openpgp_oid_to_str (pkey[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
{
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecdsa(curve %s)(q%m)))",
curve, pkey[1]);
xfree (curve);
}
}
else
return GPG_ERR_PUBKEY_ALGO;
@ -174,18 +182,27 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
else if (algo == PUBKEY_ALGO_ECDH)
{
gcry_mpi_t k;
char *curve;
rc = pk_ecdh_generate_ephemeral_key (pkey, &k);
if (rc)
return rc;
/* Now use the ephemeral secret to compute the shared point. */
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecdh(c%m)(q%m)(p%m)))",
pkey[0], pkey[1], pkey[2]);
/* Put K into a simplified S-expression. */
if (rc || gcry_sexp_build (&s_data, NULL, "%m", k))
BUG ();
curve = openpgp_oid_to_str (pkey[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
{
/* Now use the ephemeral secret to compute the shared point. */
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecdh(curve%s)(q%m)))",
curve, pkey[1]);
xfree (curve);
/* FIXME: Take care of RC. */
/* Put K into a simplified S-expression. */
if (rc || gcry_sexp_build (&s_data, NULL, "%m", k))
BUG ();
}
}
else
return gpg_error (GPG_ERR_PUBKEY_ALGO);
@ -272,9 +289,16 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey)
}
else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH)
{
rc = gcry_sexp_build (&s_skey, NULL,
"(private-key(ecdsa(c%m)(q%m)(d%m)))",
skey[0], skey[1], skey[2] );
char *curve = openpgp_oid_to_str (skey[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
{
rc = gcry_sexp_build (&s_skey, NULL,
"(private-key(ecdsa(curve%s)(q%m)(d%m)))",
curve, skey[1], skey[2]);
xfree (curve);
}
}
else
return GPG_ERR_PUBKEY_ALGO;

View File

@ -32,7 +32,7 @@ int pk_check_secret_key (int algo, gcry_mpi_t *skey);
/*-- ecdh.c --*/
byte *pk_ecdh_default_params (unsigned int qbits, size_t *sizeout);
gcry_mpi_t pk_ecdh_default_params (unsigned int qbits);
gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k);
gpg_error_t pk_ecdh_encrypt_with_shared_point
/* */ (int is_encrypt, gcry_mpi_t shared_mpi,