gpg: Rework ECC support and add experimental support for Ed25519.

* agent/findkey.c (key_parms_from_sexp): Add algo name "ecc".
(agent_is_dsa_key): Ditto.
(agent_is_eddsa_key): New.  Not finished, though.
* agent/pksign.c (do_encode_eddsa): New.
(agent_pksign_do): Use gcry_log_debug functions.
* agent/protect.c (agent_protect): Parse a flags parameter.
* g10/keygen.c (gpg_curve_to_oid): Move to ...
* common/openpgp-oid.c (openpgp_curve_to_oid): here and rename.
(oid_ed25519): New.
(openpgp_oid_is_ed25519): New.
(openpgp_oid_to_curve): New.
* common/t-openpgp-oid.c (test_openpgp_oid_is_ed25519): New.
* g10/build-packet.c (gpg_mpi_write): Write the length header also for
opaque MPIs.
(gpg_mpi_write_nohdr): New.
(do_key): Use gpg_mpi_write_nohdr depending on algorithm.
(do_pubkey_enc): Ditto.
* g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Use
gpg_mpi_write_nohdr.
* g10/export.c (transfer_format_to_openpgp):
* g10/keygen.c (ecckey_from_sexp): Return the error.
(gen_ecc): Repalce arg NBITS by CURVE.
(read_parameter_file): Add keywords "Key-Curve" and "Subkey-Curve".
(ask_curve): New.
(generate_keypair, generate_subkeypair): Use ask_curve.
(do_generate_keypair): Also pass curve name.
* g10/keylist.c (list_keyblock_print, list_keyblock_colon): Print
curve name.
* g10/parse-packet.c (mpi_read): Remove workaround for
Libcgrypt < 1.5.
(parse_key): Fix ECC case.  Print the curve name.
* g10/pkglue.c (mpi_from_sexp): Rename to get_mpi_from_sexp.
(pk_verify, pk_check_secret_key): Add special case for Ed25519.
* g10/seskey.c (encode_md_value): Ditto.
* g10/sign.c (do_sign, hash_for, sign_file): Ditto.
--

Be warned that this code is subject to further changes and that the
format will very likely change before a release.  There are also known
bugs and missing code.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2013-11-15 08:59:45 +01:00
parent 9ae48b173c
commit 402aa0f948
20 changed files with 574 additions and 139 deletions

View File

@ -324,6 +324,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
gcry_sexp_t *result);
int agent_is_dsa_key (gcry_sexp_t s_key);
int agent_is_eddsa_key (gcry_sexp_t s_key);
int agent_key_available (const unsigned char *grip);
gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
int *r_keytype,

View File

@ -729,6 +729,11 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
algoname = "dsa";
elems = "pqgy";
}
else if (n==3 && !memcmp (name, "ecc", 3))
{
algoname = "ecc";
elems = "pabgnq";
}
else if (n==5 && !memcmp (name, "ecdsa", 5))
{
algoname = "ecdsa";
@ -788,6 +793,8 @@ agent_is_dsa_key (gcry_sexp_t s_key)
if (!strcmp (algoname, "dsa"))
return GCRY_PK_DSA;
else if (!strcmp (algoname, "ecc"))
return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag. */
else if (!strcmp (algoname, "ecdsa"))
return GCRY_PK_ECDSA;
else
@ -795,6 +802,28 @@ agent_is_dsa_key (gcry_sexp_t s_key)
}
/* Return true if S_KEY is an EdDSA key as used with curve Ed25519. */
int
agent_is_eddsa_key (gcry_sexp_t s_key)
{
char algoname[6];
if (!s_key)
return 0;
if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
return 0; /* Error - assume it is not an DSA key. */
if (!strcmp (algoname, "dsa"))
return GCRY_PK_DSA;
else if (!strcmp (algoname, "ecc"))
return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag. */
else if (!strcmp (algoname, "ecdsa"))
return GCRY_PK_ECDSA;
else
return 0;
}
/* Return the key for the keygrip GRIP. The result is stored at
RESULT. This function extracts the key from the private key

View File

@ -131,6 +131,24 @@ rfc6979_hash_algo_string (size_t mdlen)
}
/* Encode a message digest for use with the EdDSA algorithm
(i.e. curve Ed25519). */
static gpg_error_t
do_encode_eddsa (const byte *md, size_t mdlen, gcry_sexp_t *r_hash)
{
gpg_error_t err;
gcry_sexp_t hash;
*r_hash = NULL;
err = gcry_sexp_build (&hash, NULL,
"(data(flags eddsa)(hash-algo sha512)(value %b))",
(int)mdlen, md);
if (!err)
*r_hash = hash;
return err;
}
/* Encode a message digest for use with an DSA algorithm. */
static gpg_error_t
do_encode_dsa (const byte *md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey,
@ -400,7 +418,11 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
int dsaalgo;
/* Put the hash into a sexp */
if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
if (agent_is_eddsa_key (s_skey))
rc = do_encode_eddsa (ctrl->digest.value,
ctrl->digest.valuelen,
&s_hash);
else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
rc = do_encode_raw_pkcs1 (ctrl->digest.value,
ctrl->digest.valuelen,
gcry_pk_get_nbits (s_skey),
@ -421,10 +443,8 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
if (DBG_CRYPTO)
{
log_debug ("skey:\n");
gcry_sexp_dump (s_skey);
log_debug ("hash:\n");
gcry_sexp_dump (s_hash);
gcry_log_debugsxp ("skey", s_skey);
gcry_log_debugsxp ("hash", s_hash);
}
/* sign */
@ -437,10 +457,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
}
if (DBG_CRYPTO)
{
log_debug ("result:\n");
gcry_sexp_dump (s_sig);
}
gcry_log_debugsxp ("rslt", s_sig);
}
leave:

View File

@ -467,6 +467,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
int depth = 0;
unsigned char *p;
gcry_md_hd_t md;
int have_curve = 0;
/* Create an S-expression with the protected-at timestamp. */
memcpy (timestamp_exp, "(12:protected-at15:", 19);
@ -499,6 +500,11 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
if (!protect_info[infidx].algo)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
/* The parser below is a complete mess: To make it robust for ECC
use we should reorder the s-expression to include only what we
really need and thus guarantee the right order for saving stuff.
This should be done before calling this function and maybe with
the help of the new gcry_sexp_extract_param. */
parmlist = protect_info[infidx].parmlist;
prot_from_idx = protect_info[infidx].prot_from;
prot_to_idx = protect_info[infidx].prot_to;
@ -522,10 +528,19 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
/* This is a private ECC key but the first parameter is
the name of the curve. We change the parameter list
here to the one we expect in this case. */
have_curve = 1;
parmlist = "?qd";
prot_from_idx = 2;
prot_to_idx = 2;
}
else if (n == 5 && !memcmp (s, "flags", 5)
&& i == 1 && have_curve)
{
/* "curve" followed by "flags": Change again. */
parmlist = "??qd";
prot_from_idx = 3;
prot_to_idx = 3;
}
else
return gpg_error (GPG_ERR_INV_SEXP);
}

View File

@ -1,5 +1,6 @@
/* openpgp-oids.c - OID helper for OpenPGP
* Copyright (C) 2011 Free Software Foundation, Inc.
* Copyright (C) 2013 Werner Koch
*
* This file is part of GnuPG.
*
@ -36,6 +37,11 @@
#include "util.h"
/* The OID for Curve Ed25519 in OpenPGP format. */
static const char oid_ed25519[] =
{ 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 };
/* Helper for openpgp_oid_from_str. */
static size_t
make_flagged_int (unsigned long value, char *buf, size_t buflen)
@ -236,3 +242,88 @@ openpgp_oid_to_str (gcry_mpi_t a)
xfree (string);
return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
}
/* Return true if A represents the OID for Ed25519. */
int
openpgp_oid_is_ed25519 (gcry_mpi_t a)
{
const unsigned char *buf;
unsigned int nbits;
size_t n;
if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
return 0;
buf = gcry_mpi_get_opaque (a, &nbits);
n = (nbits+7)/8;
return (n == DIM (oid_ed25519)
&& !memcmp (buf, oid_ed25519, DIM (oid_ed25519)));
}
/* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL
store the bit size of the curve there. Returns NULL for unknown
curve names. */
const char *
openpgp_curve_to_oid (const char *name, unsigned int *r_nbits)
{
unsigned int nbits = 0;
const char *oidstr;
if (!name)
oidstr = NULL;
else if (!strcmp (name, "Ed25519"))
{
oidstr = "1.3.6.1.4.1.3029.1.5.1";
nbits = 255;
}
else if (!strcmp (name, "nistp256"))
{
oidstr = "1.2.840.10045.3.1.7";
nbits = 256;
}
else if (!strcmp (name, "nistp384"))
{
oidstr = "1.3.132.0.34";
nbits = 384;
}
else if (!strcmp (name, "nistp521"))
{
oidstr = "1.3.132.0.35";
nbits = 521;
}
else
oidstr = NULL;
if (r_nbits)
*r_nbits = nbits;
return oidstr;
}
/* Map an OpenPGP OID to the Libgcrypt curve NAME. If R_NBITS is not
NULL store the bit size of the curve there. Returns "?" for
unknown curve names. */
const char *
openpgp_oid_to_curve (const char *oid)
{
const char *name;
if (!oid)
name = "";
else if (!strcmp (oid, "1.3.6.1.4.1.3029.1.5.1"))
name = "Ed25519";
else if (!strcmp (oid, "1.2.840.10045.3.1.7"))
name = "NIST P-256";
else if (!strcmp (oid, "1.3.132.0.34"))
name = "NIST P-384";
else if (!strcmp (oid, "1.3.132.0.35"))
name = "NIST P-521";
else /* FIXME: Lookup via Libgcrypt. */
name = "?";
return name;
}

View File

@ -35,7 +35,7 @@
static void
test_openpgp_oid_from_str (void)
{
static char *sample_oids[] =
static char *sample_oids[] =
{
"0.0",
"1.0",
@ -134,6 +134,41 @@ test_openpgp_oid_to_str (void)
}
static void
test_openpgp_oid_is_ed25519 (void)
{
static struct
{
int yes;
const char *oidstr;
} samples[] = {
{ 0, "0.0" },
{ 0, "1.3.132.0.35" },
{ 0, "1.3.6.1.4.1.3029.1.5.0" },
{ 1, "1.3.6.1.4.1.3029.1.5.1" },
{ 0, "1.3.6.1.4.1.3029.1.5.2" },
{ 0, "1.3.6.1.4.1.3029.1.5.1.0" },
{ 0, "1.3.6.1.4.1.3029.1.5" },
{ 0, NULL },
};
gpg_error_t err;
gcry_mpi_t a;
int idx;
for (idx=0; samples[idx].oidstr; idx++)
{
err = openpgp_oid_from_str (samples[idx].oidstr, &a);
if (err)
fail (idx, err);
if (openpgp_oid_is_ed25519 (a) != samples[idx].yes)
fail (idx, 0);
gcry_mpi_release (a);
}
}
int
main (int argc, char **argv)
@ -143,6 +178,7 @@ main (int argc, char **argv)
test_openpgp_oid_from_str ();
test_openpgp_oid_to_str ();
test_openpgp_oid_is_ed25519 ();
return 0;
}

View File

@ -215,6 +215,9 @@ size_t percent_unescape_inplace (char *string, int nulrepl);
/*-- openpgp-oid.c --*/
gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
char *openpgp_oid_to_str (gcry_mpi_t a);
int openpgp_oid_is_ed25519 (gcry_mpi_t a);
const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits);
const char *openpgp_oid_to_curve (const char *oid);

View File

@ -32,8 +32,8 @@ fpr:::::::::AB059359A3B81F410FCFF97F5CE086B5B5A18FF4:
#+end_example
The double =--with-fingerprint= prints the fingerprint for the subkeys
too. Old versions of gpg used a lighly different format and required
the use of the option =--fixed-list-mode= to conform to format
too. Old versions of gpg used a slighrly different format and required
the use of the option =--fixed-list-mode= to conform to the format
described here.
** Description of the fields
@ -201,6 +201,11 @@ described here.
For sig records, this is the used hash algorithm. For example:
2 = SHA-1, 8 = SHA-256.
*** Field 17 - Curve name
For pub, sub, sec, and sbb records this field is used for the ECC
curve name.
** Special fields
*** PKD - Public key data

View File

@ -166,9 +166,14 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
{
unsigned int nbits;
const void *p;
unsigned int lenhdr[2];
p = gcry_mpi_get_opaque (a, &nbits);
rc = iobuf_write (out, p, (nbits+7)/8);
lenhdr[0] = nbits >> 8;
lenhdr[1] = nbits;
rc = iobuf_write (out, lenhdr, 2);
if (!rc)
rc = iobuf_write (out, p, (nbits+7)/8);
}
else
{
@ -191,6 +196,29 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
}
/*
* Write an opaque MPI to the output stream without length info.
*/
gpg_error_t
gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a)
{
int rc;
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
const void *p;
p = gcry_mpi_get_opaque (a, &nbits);
rc = iobuf_write (out, p, (nbits+7)/8);
}
else
rc = gpg_error (GPG_ERR_BAD_MPI);
return rc;
}
/* Calculate the length of a packet described by PKT. */
u32
calc_packet_length( PACKET *pkt )
@ -302,7 +330,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
for (i=0; i < npkey; i++ )
{
err = gpg_mpi_write (a, pk->pkey[i]);
if ((pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0))
|| (pk->pubkey_algo == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2))
err = gpg_mpi_write_nohdr (a, pk->pkey[i]);
else
err = gpg_mpi_write (a, pk->pkey[i]);
if (err)
goto leave;
}
@ -473,7 +505,12 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
write_fake_data( a, enc->data[0] );
for (i=0; i < n && !rc ; i++ )
rc = gpg_mpi_write (a, enc->data[i]);
{
if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
rc = gpg_mpi_write_nohdr (a, enc->data[i]);
else
rc = gpg_mpi_write (a, enc->data[i]);
}
if (!rc)
{

View File

@ -197,11 +197,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi,
obuf = iobuf_temp();
/* variable-length field 1, curve name OID */
err = gpg_mpi_write (obuf, pkey[0]);
err = gpg_mpi_write_nohdr (obuf, pkey[0]);
/* fixed-length field 2 */
iobuf_put (obuf, PUBKEY_ALGO_ECDH);
/* variable-length field 3, KDF params */
err = (err ? err : gpg_mpi_write (obuf, pkey[2]));
err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2]));
/* fixed-length field 4 */
iobuf_write (obuf, "Anonymous Sender ", 20);
/* fixed-length field 5, recipient fp */

View File

@ -583,7 +583,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
goto leave;
curvename = gcry_pk_get_curve (s_pubkey, 0, NULL);
gcry_sexp_release (s_pubkey);
curveoidstr = gpg_curve_to_oid (curvename, NULL);
curveoidstr = openpgp_curve_to_oid (curvename, NULL);
if (!curveoidstr)
{
log_error ("no OID known for curve '%s'\n", curvename);

View File

@ -60,9 +60,11 @@
enum para_name {
pKEYTYPE,
pKEYLENGTH,
pKEYCURVE,
pKEYUSAGE,
pSUBKEYTYPE,
pSUBKEYLENGTH,
pSUBKEYCURVE,
pSUBKEYUSAGE,
pAUTHKEYTYPE,
pNAMEREAL,
@ -1071,40 +1073,6 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
return err;
}
/* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL
store the bit size of the curve there. Returns NULL for unknown
curve names. */
const char *
gpg_curve_to_oid (const char *name, unsigned int *r_nbits)
{
unsigned int nbits = 0;
const char *oidstr;
if (!name)
oidstr = NULL;
else if (!strcmp (name, "NIST P-256"))
{
oidstr = "1.2.840.10045.3.1.7";
nbits = 256;
}
else if (!strcmp (name, "NIST P-384"))
{
oidstr = "1.3.132.0.34";
nbits = 384;
}
else if (!strcmp (name, "NIST P-521"))
{
oidstr = "1.3.132.0.35";
nbits = 521;
}
else
oidstr = NULL;
if (r_nbits)
*r_nbits = nbits;
return oidstr;
}
static gpg_error_t
ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
@ -1142,7 +1110,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
goto leave;
}
gcry_sexp_release (l2);
oidstr = gpg_curve_to_oid (curve, &nbits);
oidstr = openpgp_curve_to_oid (curve, &nbits);
if (!oidstr)
{
/* That can't happen because we used one of the curves
@ -1188,7 +1156,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
array[i] = NULL;
}
}
return 0;
return err;
}
@ -1534,31 +1502,24 @@ gen_dsa (unsigned int nbits, KBNODE pub_root,
* Generate an ECC key
*/
static gpg_error_t
gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root,
gen_ecc (int algo, const char *curve, kbnode_t pub_root,
u32 timestamp, u32 expireval, int is_subkey,
int keygen_flags, char **cache_nonce_addr)
{
gpg_error_t err;
const char *curve;
char *keyparms;
assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH);
/* For now we may only use one of the 3 NIST curves. See also
gpg_curve_to_oid. */
if (nbits <= 256)
curve = "NIST P-256";
else if (nbits <= 384)
curve = "NIST P-384";
else
curve = "NIST P-521";
if (!curve || !*curve)
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))",
algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s%s)))",
strlen (curve), curve,
((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
&& (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
"(transient-key)" : "" );
(((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
&& (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
" transient-key" : ""),
(!strcmp (curve, "Ed25519")? " eddsa":""));
if (!keyparms)
err = gpg_error_from_syserror ();
else
@ -2082,6 +2043,98 @@ ask_keysize (int algo, unsigned int primary_keysize)
}
/* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE
is not 0, the function asks for the size of the encryption
subkey. */
static char *
ask_curve (void)
{
struct {
const char *name;
int available;
int expert_only;
const char *pretty_name;
} curves[] = {
{ "Ed25519", 0, 0, "Curve 25519" },
{ "NIST P-256", 0, 1, },
{ "NIST P-384", 0, 0, },
{ "NIST P-521", 0, 1, },
{ "brainpoolP256r1", 0, 1, "Brainpool P-256" },
{ "brainpoolP384r1", 0, 1, "Brainpool P-384" },
{ "brainpoolP512r1", 0, 1, "Brainpool P-512" },
};
int idx;
char *answer;
char *result = NULL;
gcry_sexp_t keyparms;
tty_printf (_("Please select which elliptic curve you want:\n"));
keyparms = NULL;
for (idx=0; idx < DIM(curves); idx++)
{
int rc;
curves[idx].available = 0;
if (!opt.expert && curves[idx].expert_only)
continue;
gcry_sexp_release (keyparms);
rc = gcry_sexp_build (&keyparms, NULL,
"(public-key(ecc(curve %s)))", curves[idx].name);
if (rc)
continue;
if (!gcry_pk_get_curve (keyparms, 0, NULL))
continue;
curves[idx].available = 1;
tty_printf (_(" (%d) %s\n"), idx + 1,
curves[idx].pretty_name?
curves[idx].pretty_name:curves[idx].name);
}
gcry_sexp_release (keyparms);
for (;;)
{
answer = cpr_get ("keygen.curve", _("Your selection? "));
cpr_kill_prompt ();
idx = *answer? atoi (answer) : 1;
if (*answer && !idx)
{
/* See whether the user entered the name of the curve. */
for (idx=0; idx < DIM(curves); idx++)
{
if (!opt.expert && curves[idx].expert_only)
continue;
if (!stricmp (curves[idx].name, answer)
|| (curves[idx].pretty_name
&& !stricmp (curves[idx].pretty_name, answer)))
break;
}
if (idx == DIM(curves))
idx = -1;
}
else
idx--;
xfree(answer);
answer = NULL;
if (idx < 0 || idx >= DIM (curves) || !curves[idx].available)
tty_printf (_("Invalid selection.\n"));
else
{
result = xstrdup (curves[idx].name);
break;
}
}
if (!result)
result = xstrdup (curves[0].name);
return result;
}
/****************
* Parse an expire string and return its value in seconds.
* Returns (u32)-1 on error.
@ -2539,7 +2592,7 @@ do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled)
/* Basic key generation. Here we divert to the actual generation
routines based on the requested algorithm. */
static int
do_create (int algo, unsigned int nbits, KBNODE pub_root,
do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root,
u32 timestamp, u32 expiredate, int is_subkey,
int keygen_flags, char **cache_nonce_addr)
{
@ -2561,7 +2614,7 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root,
err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey,
keygen_flags, cache_nonce_addr);
else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey,
keygen_flags, cache_nonce_addr);
else if (algo == PUBKEY_ALGO_RSA)
err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
@ -2974,7 +3027,6 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
* but because we do this always, why not here. */
STRING2KEY *s2k;
DEK *dek;
static int count;
s2k = xmalloc ( sizeof *s2k );
s2k->mode = opt.s2k_mode;
@ -3058,9 +3110,11 @@ read_parameter_file( const char *fname )
} keywords[] = {
{ "Key-Type", pKEYTYPE},
{ "Key-Length", pKEYLENGTH },
{ "Key-Curve", pKEYCURVE },
{ "Key-Usage", pKEYUSAGE },
{ "Subkey-Type", pSUBKEYTYPE },
{ "Subkey-Length", pSUBKEYLENGTH },
{ "Subkey-Curve", pSUBKEYCURVE },
{ "Subkey-Usage", pSUBKEYUSAGE },
{ "Name-Real", pNAMEREAL },
{ "Name-Email", pNAMEEMAIL },
@ -3340,6 +3394,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
else
{
int subkey_algo;
char *curve = NULL;
/* Fixme: To support creating a primary key by keygrip we better
also define the keyword for the parameter file. Note that
@ -3355,12 +3410,24 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
sprintf( r->u.value, "%d", algo );
r->next = para;
para = r;
nbits = ask_keysize (algo, 0);
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYLENGTH;
sprintf( r->u.value, "%u", nbits);
r->next = para;
para = r;
if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
{
curve = ask_curve ();
r = xmalloc_clear (sizeof *r + strlen (curve));
r->key = pKEYCURVE;
strcpy (r->u.value, curve);
r->next = para;
para = r;
}
else
{
nbits = ask_keysize (algo, 0);
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYLENGTH;
sprintf( r->u.value, "%u", nbits);
r->next = para;
para = r;
}
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYUSAGE;
strcpy( r->u.value, "sign" );
@ -3400,12 +3467,27 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
nbits = 0;
}
nbits = ask_keysize (both? subkey_algo : algo, nbits);
r = xmalloc_clear( sizeof *r + 20 );
r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
sprintf( r->u.value, "%u", nbits);
r->next = para;
para = r;
if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
{
if (!both)
curve = ask_curve ();
r = xmalloc_clear (sizeof *r + strlen (curve));
r->key = both? pSUBKEYCURVE : pKEYCURVE;
strcpy (r->u.value, curve);
r->next = para;
para = r;
}
else
{
nbits = ask_keysize (both? subkey_algo : algo, nbits);
r = xmalloc_clear( sizeof *r + 20 );
r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
sprintf( r->u.value, "%u", nbits);
r->next = para;
para = r;
}
xfree (curve);
}
expire = ask_expire_interval(0,NULL);
@ -3630,6 +3712,7 @@ do_generate_keypair (struct para_data_s *para,
if (!card)
err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
get_parameter_uint( para, pKEYLENGTH ),
get_parameter_value (para, pKEYCURVE),
pub_root,
timestamp,
get_parameter_u32( para, pKEYEXPIRE ), 0,
@ -3681,6 +3764,7 @@ do_generate_keypair (struct para_data_s *para,
{
err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL),
get_parameter_uint (para, pSUBKEYLENGTH),
get_parameter_value (para, pSUBKEYCURVE),
pub_root,
timestamp,
get_parameter_u32 (para, pSUBKEYEXPIRE), 1,
@ -3827,7 +3911,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
int algo;
unsigned int use;
u32 expire;
unsigned int nbits;
unsigned int nbits = 0;
char *curve = NULL;
u32 cur_time;
char *hexgrip = NULL;
char *serialno = NULL;
@ -3881,7 +3966,14 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
hexgrip = NULL;
algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
assert (algo);
nbits = hexgrip? 0 : ask_keysize (algo, 0);
if (hexgrip)
nbits = 0;
else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
curve = ask_curve ();
else
nbits = ask_keysize (algo, 0);
expire = ask_expire_interval (0, NULL);
if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
_("Really create? (y/N) ")))
@ -3894,7 +3986,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
err = do_create_from_keygrip (ctrl, algo, hexgrip,
keyblock, cur_time, expire, 1);
else
err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
err = do_create (algo, nbits, curve,
keyblock, cur_time, expire, 1, 0, NULL);
if (err)
goto leave;
@ -3911,6 +4004,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
write_status_text (STATUS_KEY_CREATED, "S");
leave:
xfree (curve);
xfree (hexgrip);
xfree (serialno);
if (err)

View File

@ -817,6 +817,17 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo),
keystr_from_pk (pk), datestr_from_pk (pk));
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fprintf (es_stdout, " %s", name);
xfree (curve);
}
if (pk->flags.revoked)
{
es_fprintf (es_stdout, " [");
@ -940,6 +951,18 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
s2k_char,
nbits_from_pk (pk2), pubkey_letter (pk2->pubkey_algo),
keystr_from_pk (pk2), datestr_from_pk (pk2));
if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk2->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fprintf (es_stdout, " %s", name);
xfree (curve);
}
if (pk2->flags.revoked)
{
es_fprintf (es_stdout, " [");
@ -1172,16 +1195,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
es_putc (':', es_stdout);
es_putc (':', es_stdout);
print_capabilities (pk, keyblock);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret)
{
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs(serialno, es_stdout);
es_putc (':', es_stdout); /* End of field 15. */
es_fputs (serialno, es_stdout);
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fputs (name, es_stdout);
xfree (curve);
}
es_putc (':', es_stdout); /* End of field 17. */
es_putc ('\n', es_stdout);
print_revokers (pk);
@ -1285,16 +1320,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
/* fixme: add LID and ownertrust here */
);
print_capabilities (pk2, NULL);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret)
{
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs (serialno, es_stdout);
es_putc (':', es_stdout); /* End of field 15. */
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fputs (name, es_stdout);
xfree (curve);
}
es_putc (':', es_stdout); /* End of field 17. */
es_putc ('\n', es_stdout);
if (fpr > 1)
print_fingerprint (pk2, 0);

View File

@ -230,7 +230,6 @@ void keyedit_passwd (ctrl_t ctrl, const char *username);
void show_basic_key_info (KBNODE keyblock);
/*-- keygen.c --*/
const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);

View File

@ -445,6 +445,7 @@ PACKET *create_gpg_control ( ctrlpkttype_t type,
/*-- build-packet.c --*/
int build_packet( iobuf_t inp, PACKET *pkt );
gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a);
gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a);
u32 calc_packet_length( PACKET *pkt );
void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
const byte *buffer, size_t buflen );

View File

@ -140,22 +140,13 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
nread++;
}
if (nread >= 2 && !(buf[0] << 8 | buf[1]))
{
/* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero)
MPIs. We fix this here. */
a = gcry_mpi_new (0);
}
else
{
if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
a = NULL;
}
if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
a = NULL;
leave:
gcry_free (buf);
if (nread > *ret_nread)
log_bug ("mpi larger than packet");
log_bug ("mpi larger than packet (%zu/%u)", nread, *ret_nread);
else
*ret_nread = nread;
return a;
@ -1999,8 +1990,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
{
for (i = 0; i < npkey; i++)
{
if ((algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2))
if ((algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
|| (algorithm == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2))
{
size_t n;
err = read_size_body (inp, pktlen, &n, pk->pkey+i);
@ -2020,6 +2011,14 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
{
es_fprintf (listfp, "\tpkey[%d]: ", i);
mpi_print (listfp, pk->pkey[i], mpi_print_mode);
if ((algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_ECDH) && i==0)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
es_fprintf (listfp, " %s (%s)",
openpgp_oid_to_curve (curve), curve);
xfree (curve);
}
es_putc ('\n', listfp);
}
}

View File

@ -33,14 +33,14 @@
/* FIXME: Better chnage the fucntion name because mpi_ is used by
gcrypt macros. */
gcry_mpi_t
mpi_from_sexp (gcry_sexp_t sexp, const char * item)
get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt)
{
gcry_sexp_t list;
gcry_mpi_t data;
list = gcry_sexp_find_token (sexp, item, 0);
assert (list);
data = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG);
data = gcry_sexp_nth_mpi (list, 1, mpifmt);
assert (data);
gcry_sexp_release (list);
return data;
@ -58,6 +58,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
gcry_sexp_t s_sig, s_hash, s_pkey;
int rc;
const int pkalgo = map_pk_openpgp_to_gcry (algo);
int is_ed25519 = 0;
/* Make a sexp from pkey. */
if (pkalgo == GCRY_PK_DSA)
@ -79,15 +80,24 @@ 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 */
{
char *curve = openpgp_oid_to_str (pkey[0]);
if (!curve)
rc = gpg_error_from_syserror ();
is_ed25519 = openpgp_oid_is_ed25519 (pkey[0]);
if (is_ed25519)
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecc(curve Ed25519)"
"(flags eddsa)(q%m)))",
pkey[1]);
else
{
rc = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecdsa(curve %s)(q%m)))",
curve, pkey[1]);
xfree (curve);
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
@ -97,8 +107,18 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
BUG (); /* gcry_sexp_build should never fail. */
/* Put hash into a S-Exp s_hash. */
if (gcry_sexp_build (&s_hash, NULL, "%m", hash))
BUG (); /* gcry_sexp_build should never fail. */
if (is_ed25519)
{
if (gcry_sexp_build (&s_hash, NULL,
"(data(flags eddsa)(hash-algo sha512)(value %m))",
hash))
BUG (); /* gcry_sexp_build should never fail. */
}
else
{
if (gcry_sexp_build (&s_hash, NULL, "%m", hash))
BUG (); /* gcry_sexp_build should never fail. */
}
/* Put data into a S-Exp s_sig. */
s_sig = NULL;
@ -114,6 +134,9 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
{
if (!data[0] || !data[1])
rc = gpg_error (GPG_ERR_BAD_MPI);
else if (is_ed25519)
rc = gcry_sexp_build (&s_sig, NULL,
"(sig-val(eddsa(r%M)(s%M)))", data[0], data[1]);
else
rc = gcry_sexp_build (&s_sig, NULL,
"(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]);
@ -223,8 +246,8 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
size_t fpn;
/* Get the shared point and the ephemeral public key. */
shared = mpi_from_sexp (s_ciph, "s");
public = mpi_from_sexp (s_ciph, "e");
shared = get_mpi_from_sexp (s_ciph, "s", GCRYMPI_FMT_USG);
public = get_mpi_from_sexp (s_ciph, "e", GCRYMPI_FMT_USG);
gcry_sexp_release (s_ciph);
s_ciph = NULL;
if (DBG_CIPHER)
@ -256,9 +279,9 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
else /* Elgamal or RSA case. */
{ /* Fixme: Add better error handling or make gnupg use
S-expressions directly. */
resarr[0] = mpi_from_sexp (s_ciph, "a");
resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG);
if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E)
resarr[1] = mpi_from_sexp (s_ciph, "b");
resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG);
}
gcry_sexp_release (s_ciph);
@ -296,15 +319,25 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey)
}
else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH)
{
char *curve = openpgp_oid_to_str (skey[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
if (openpgp_oid_is_ed25519 (skey[0]))
{
rc = gcry_sexp_build (&s_skey, NULL,
"(private-key(ecdsa(curve%s)(q%m)(d%m)))",
curve, skey[1], skey[2]);
xfree (curve);
"(private-key(ecc(curve Ed25519)"
"(flags eddsa)(q%m)(d%m)))",
skey[1], skey[2]);
}
else
{
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

View File

@ -23,7 +23,7 @@
#include "packet.h" /* For PKT_public_key. */
/*-- pkglue.c --*/
gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item);
gcry_mpi_t get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt);
int pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data,
gcry_mpi_t *pkey);

View File

@ -264,7 +264,12 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo)
pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo);
if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
if (pkalgo == GCRY_PK_ECDSA && openpgp_oid_is_ed25519 (pk->pkey[0]))
{
frame = gcry_mpi_set_opaque_copy (NULL, gcry_md_read (md, hash_algo),
8*gcry_md_get_algo_dlen (hash_algo));
}
else if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
{
/* It's a DSA signature, so find out the size of q. */

View File

@ -281,11 +281,16 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig,
;
else if (pksk->pubkey_algo == GCRY_PK_RSA
|| pksk->pubkey_algo == GCRY_PK_RSA_S)
sig->data[0] = mpi_from_sexp (s_sigval, "s");
sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
else if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
{
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE);
}
else
{
sig->data[0] = mpi_from_sexp (s_sigval, "r");
sig->data[1] = mpi_from_sexp (s_sigval, "s");
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
}
gcry_sexp_release (s_sigval);
@ -422,6 +427,10 @@ match_dsa_hash (unsigned int qbytes)
usable for the pubkey algorithm. If --preferred-digest-prefs isn't
set, then take the OpenPGP default (i.e. SHA-1).
Note that Ed25519+EdDSA takes an input of arbitrary length and thus
we don't enforce any particular algorithm like we do for standard
ECDSA. However, we use SHA256 as the default algorithm.
Possible improvement: Use the highest-ranked usable algorithm from
the signing key prefs either before or after using the personal
list?
@ -437,6 +446,14 @@ hash_for (PKT_public_key *pk)
{
return recipient_digest_algo;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
&& openpgp_oid_is_ed25519 (pk->pkey[0]))
{
if (opt.personal_digest_prefs)
return opt.personal_digest_prefs[0].value;
else
return DIGEST_ALGO_SHA256;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_DSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
@ -927,7 +944,8 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
|| sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
|| (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA
&& !openpgp_oid_is_ed25519 (sk_rover->pk->pkey[1])))
{
int temp_hashlen = (gcry_mpi_get_nbits
(sk_rover->pk->pkey[1]));
@ -1492,8 +1510,13 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA)
digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8);
else if(pksk->pubkey_algo == PUBKEY_ALGO_ECDSA )
digest_algo = match_dsa_hash (ecdsa_qbits_from_Q
(gcry_mpi_get_nbits (pksk->pkey[1]))/8);
{
if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
digest_algo = DIGEST_ALGO_SHA256;
else
digest_algo = match_dsa_hash
(ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8);
}
else
digest_algo = DIGEST_ALGO_SHA1;
}