1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-29 02:22:45 +02:00

gpg: Finish experimental support for Ed25519.

* agent/cvt-openpgp.c (try_do_unprotect_arg_s): Add field "curve".
(get_keygrip): Add and use arg CURVE.
(convert_secret_key): Ditto.
(convert_transfer_key): Ditto.
(get_npkey_nskey): New.
(prepare_unprotect): Replace gcrypt functions by
get_npkey_nskey.  Allow opaque MPIs.
(do_unprotect): Use CURVE instead of parameters.
(convert_from_openpgp_main): Ditto.
(convert_to_openpgp):  Simplify.
* g10/import.c (one_mpi_from_pkey): Remove.
(transfer_secret_keys): Rewrite to use the curve instead of the
parameters.
* g10/parse-packet.c (parse_key): Mark protected MPIs with USER1 flag.

* common/openpgp-oid.c (openpgp_curve_to_oid): Allow the use of
 "NIST P-256" et al.
* g10/keygen.c (ask_curve): Add arg ALGO.
(generate_keypair): Rewrite the ECC key logic.

* tests/openpgp/ecc.test: Provide the "ecc" passphrase.
This commit is contained in:
Werner Koch 2014-05-07 13:16:32 +02:00
parent bdb9c2b314
commit 8fee6c1ce6
7 changed files with 231 additions and 229 deletions

View File

@ -36,6 +36,7 @@ struct try_do_unprotect_arg_s
int is_v4; int is_v4;
int is_protected; int is_protected;
int pubkey_algo; int pubkey_algo;
const char *curve;
int protect_algo; int protect_algo;
char *iv; char *iv;
int ivlen; int ivlen;
@ -54,7 +55,8 @@ struct try_do_unprotect_arg_s
/* Compute the keygrip from the public key and store it at GRIP. */ /* Compute the keygrip from the public key and store it at GRIP. */
static gpg_error_t static gpg_error_t
get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
unsigned char *grip)
{ {
gpg_error_t err; gpg_error_t err;
gcry_sexp_t s_pkey = NULL; gcry_sexp_t s_pkey = NULL;
@ -80,9 +82,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
case GCRY_PK_ECC: case GCRY_PK_ECC:
err = gcry_sexp_build (&s_pkey, NULL, err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))", "(public-key(ecc(curve %s)(q%m)))",
pkey[0], pkey[1], pkey[2], pkey[3], pkey[4], curve, pkey[0]);
pkey[5]);
break; break;
default: default:
@ -102,7 +103,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
parameters into our s-expression based format. Note that parameters into our s-expression based format. Note that
PUBKEY_ALGO has an gcrypt algorithm number. */ PUBKEY_ALGO has an gcrypt algorithm number. */
static gpg_error_t static gpg_error_t
convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
const char *curve)
{ {
gpg_error_t err; gpg_error_t err;
gcry_sexp_t s_skey = NULL; gcry_sexp_t s_skey = NULL;
@ -135,11 +137,12 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
break; break;
case GCRY_PK_ECC: case GCRY_PK_ECC:
err = gcry_sexp_build (&s_skey, NULL, if (!curve)
"(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)" err = gpg_error (GPG_ERR_BAD_SECKEY);
"(d%m)))", else
skey[0], skey[1], skey[2], skey[3], skey[4], err = gcry_sexp_build (&s_skey, NULL,
skey[5], skey[6]); "(private-key(ecc(curve%s)(q%m)(d%m)))",
curve, skey[0], skey[1]);
break; break;
default: default:
@ -160,7 +163,7 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */
static gpg_error_t static gpg_error_t
convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
gcry_sexp_t transfer_key) const char *curve, gcry_sexp_t transfer_key)
{ {
gpg_error_t err; gpg_error_t err;
gcry_sexp_t s_skey = NULL; gcry_sexp_t s_skey = NULL;
@ -197,9 +200,9 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
case GCRY_PK_ECC: case GCRY_PK_ECC:
err = gcry_sexp_build err = gcry_sexp_build
(&s_skey, NULL, (&s_skey, NULL,
"(protected-private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)" "(protected-private-key(ecc(curve%s)(q%m)"
"(protected openpgp-native%S)))", "(protected openpgp-native%S)))",
skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key); curve, skey[0], transfer_key);
break; break;
default: default:
@ -254,6 +257,22 @@ checksum (const unsigned char *p, unsigned int n)
} }
/* Return the number of expected key parameters. */
static void
get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey)
{
switch (pubkey_algo)
{
case GCRY_PK_RSA: *npkey = 2; *nskey = 6; break;
case GCRY_PK_ELG: *npkey = 3; *nskey = 4; break;
case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break;
case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break;
case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break;
default: *npkey = 0; *nskey = 0; break;
}
}
/* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number. /* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number.
On success R_NPKEY and R_NSKEY receive the number or parameters for On success R_NPKEY and R_NSKEY receive the number or parameters for
the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
@ -264,7 +283,6 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
unsigned int *r_npkey, unsigned int *r_nskey, unsigned int *r_npkey, unsigned int *r_nskey,
unsigned int *r_skeylen) unsigned int *r_skeylen)
{ {
gpg_error_t err;
size_t npkey, nskey, skeylen; size_t npkey, nskey, skeylen;
int i; int i;
@ -293,12 +311,8 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
/* Get properties of the public key algorithm and do some /* Get properties of the public key algorithm and do some
consistency checks. Note that we need at least NPKEY+1 elements consistency checks. Note that we need at least NPKEY+1 elements
in the SKEY array. */ in the SKEY array. */
if ( (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, get_npkey_nskey (pubkey_algo, &npkey, &nskey);
NULL, &npkey)) if (!npkey || !nskey || npkey >= nskey)
|| (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY,
NULL, &nskey)))
return err;
if (!npkey || npkey >= nskey)
return gpg_error (GPG_ERR_INTERNAL); return gpg_error (GPG_ERR_INTERNAL);
if (skeylen <= npkey) if (skeylen <= npkey)
return gpg_error (GPG_ERR_MISSING_VALUE); return gpg_error (GPG_ERR_MISSING_VALUE);
@ -309,7 +323,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
encrypted. */ encrypted. */
for (i=0; i < npkey; i++) for (i=0; i < npkey; i++)
{ {
if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
return gpg_error (GPG_ERR_BAD_SECKEY); return gpg_error (GPG_ERR_BAD_SECKEY);
} }
@ -329,7 +343,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
static int static int
do_unprotect (const char *passphrase, do_unprotect (const char *passphrase,
int pkt_version, int pubkey_algo, int is_protected, int pkt_version, int pubkey_algo, int is_protected,
gcry_mpi_t *skey, size_t skeysize, const char *curve, gcry_mpi_t *skey, size_t skeysize,
int protect_algo, void *protect_iv, size_t protect_ivlen, int protect_algo, void *protect_iv, size_t protect_ivlen,
int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
u16 desired_csum, gcry_sexp_t *r_key) u16 desired_csum, gcry_sexp_t *r_key)
@ -353,23 +367,26 @@ do_unprotect (const char *passphrase,
merely verify the checksum. */ merely verify the checksum. */
if (!is_protected) if (!is_protected)
{ {
unsigned char *buffer;
actual_csum = 0; actual_csum = 0;
for (i=npkey; i < nskey; i++) for (i=npkey; i < nskey; i++)
{ {
if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
return gpg_error (GPG_ERR_BAD_SECKEY); return gpg_error (GPG_ERR_BAD_SECKEY);
err = gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, skey[i]); if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
if (!err)
{ {
buffer = (gcry_is_secure (skey[i])? unsigned int nbits;
xtrymalloc_secure (nbytes) : xtrymalloc (nbytes)); const unsigned char *buffer;
if (!buffer) buffer = gcry_mpi_get_opaque (skey[i], &nbits);
return gpg_error_from_syserror (); nbytes = (nbits+7)/8;
err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, actual_csum += checksum (buffer, nbytes);
NULL, skey[i]); }
else
{
unsigned char *buffer;
err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes,
skey[i]);
if (!err) if (!err)
actual_csum += checksum (buffer, nbytes); actual_csum += checksum (buffer, nbytes);
xfree (buffer); xfree (buffer);
@ -428,7 +445,8 @@ do_unprotect (const char *passphrase,
{ {
int ndata; int ndata;
unsigned int ndatabits; unsigned int ndatabits;
unsigned char *p, *data; const unsigned char *p;
unsigned char *data;
u16 csum_pgp7 = 0; u16 csum_pgp7 = 0;
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE )) if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE ))
@ -527,7 +545,7 @@ do_unprotect (const char *passphrase,
for (i = npkey; i < nskey; i++) for (i = npkey; i < nskey; i++)
{ {
unsigned char *p; const unsigned char *p;
size_t ndata; size_t ndata;
unsigned int ndatabits; unsigned int ndatabits;
@ -580,7 +598,7 @@ do_unprotect (const char *passphrase,
if (nskey != skeylen) if (nskey != skeylen)
err = gpg_error (GPG_ERR_BAD_SECKEY); err = gpg_error (GPG_ERR_BAD_SECKEY);
else else
err = convert_secret_key (r_key, pubkey_algo, skey); err = convert_secret_key (r_key, pubkey_algo, skey, curve);
if (err) if (err)
return err; return err;
@ -608,6 +626,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
err = do_unprotect (pi->pin, err = do_unprotect (pi->pin,
arg->is_v4? 4:3, arg->is_v4? 4:3,
arg->pubkey_algo, arg->is_protected, arg->pubkey_algo, arg->is_protected,
arg->curve,
arg->skey, arg->skeysize, arg->skey, arg->skeysize,
arg->protect_algo, arg->iv, arg->ivlen, arg->protect_algo, arg->iv, arg->ivlen,
arg->s2k_mode, arg->s2k_algo, arg->s2k_mode, arg->s2k_algo,
@ -651,6 +670,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
u32 s2k_count = 0; u32 s2k_count = 0;
size_t npkey, nskey; size_t npkey, nskey;
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
char *curve = NULL;
u16 desired_csum; u16 desired_csum;
int skeyidx = 0; int skeyidx = 0;
gcry_sexp_t s_skey = NULL; gcry_sexp_t s_skey = NULL;
@ -695,8 +715,6 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
if (!string) if (!string)
goto bad_seckey; goto bad_seckey;
protect_algo = gcry_cipher_map_name (string); protect_algo = gcry_cipher_map_name (string);
if (!protect_algo && !!strcmp (string, "IDEA"))
protect_algo = GCRY_CIPHER_IDEA;
xfree (string); xfree (string);
value = gcry_sexp_nth_data (list, 3, &valuelen); value = gcry_sexp_nth_data (list, 3, &valuelen);
@ -739,11 +757,21 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
pubkey_algo = gcry_pk_map_name (string); pubkey_algo = gcry_pk_map_name (string);
xfree (string); xfree (string);
if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey) get_npkey_nskey (pubkey_algo, &npkey, &nskey);
|| gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey) if (!npkey || !nskey || npkey >= nskey)
|| !npkey || npkey >= nskey)
goto bad_seckey; goto bad_seckey;
if (npkey == 1) /* This is ECC */
{
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "curve", 0);
if (!list)
goto bad_seckey;
curve = gcry_sexp_nth_string (list, 1);
if (!curve)
goto bad_seckey;
}
gcry_sexp_release (list); gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "skey", 0); list = gcry_sexp_find_token (top_list, "skey", 0);
if (!list) if (!list)
@ -770,15 +798,15 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
value = gcry_sexp_nth_data (list, ++idx, &valuelen); value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value || !valuelen) if (!value || !valuelen)
goto bad_seckey; goto bad_seckey;
if (is_enc) if (is_enc || curve)
{ {
void *p = xtrymalloc (valuelen); /* Encrypted parameters and ECC parameters need or can be
if (!p) stored as opaque. */
goto outofmem; skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
memcpy (p, value, valuelen);
skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
if (!skey[skeyidx]) if (!skey[skeyidx])
goto outofmem; goto outofmem;
if (is_enc)
gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
} }
else else
{ {
@ -807,33 +835,24 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
gcry_sexp_release (list); list = NULL; gcry_sexp_release (list); list = NULL;
gcry_sexp_release (top_list); top_list = NULL; gcry_sexp_release (top_list); top_list = NULL;
/* log_debug ("XXX is_v4=%d\n", is_v4); */ #if 0
/* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */ log_debug ("XXX is_v4=%d\n", is_v4);
/* log_debug ("XXX is_protected=%d\n", is_protected); */ log_debug ("XXX pubkey_algo=%d\n", pubkey_algo);
/* log_debug ("XXX protect_algo=%d\n", protect_algo); */ log_debug ("XXX is_protected=%d\n", is_protected);
/* log_printhex ("XXX iv", iv, ivlen); */ log_debug ("XXX protect_algo=%d\n", protect_algo);
/* log_debug ("XXX ivlen=%d\n", ivlen); */ log_printhex ("XXX iv", iv, ivlen);
/* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */ log_debug ("XXX ivlen=%d\n", ivlen);
/* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */ log_debug ("XXX s2k_mode=%d\n", s2k_mode);
/* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */ log_debug ("XXX s2k_algo=%d\n", s2k_algo);
/* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */ log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt);
/* for (idx=0; skey[idx]; idx++) */ log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count);
/* { */ log_debug ("XXX curve='%s'\n", curve);
/* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */ for (idx=0; skey[idx]; idx++)
/* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */ gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1)
/* if (is_enc) */ ? "skey(e)" : "skey(_)", skey[idx]);
/* { */ #endif /*0*/
/* void *p; */
/* unsigned int nbits; */
/* p = gcry_mpi_get_opaque (skey[idx], &nbits); */
/* log_printhex (NULL, p, (nbits+7)/8); */
/* } */
/* else */
/* gcry_mpi_dump (skey[idx]); */
/* log_printf ("\n"); */
/* } */
err = get_keygrip (pubkey_algo, skey, grip); err = get_keygrip (pubkey_algo, curve, skey, grip);
if (err) if (err)
goto leave; goto leave;
@ -850,7 +869,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
if (err) if (err)
goto leave; goto leave;
err = convert_transfer_key (&s_skey, pubkey_algo, skey, s_pgp); err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
if (err) if (err)
goto leave; goto leave;
} }
@ -871,6 +890,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
pi_arg.is_v4 = is_v4; pi_arg.is_v4 = is_v4;
pi_arg.is_protected = is_protected; pi_arg.is_protected = is_protected;
pi_arg.pubkey_algo = pubkey_algo; pi_arg.pubkey_algo = pubkey_algo;
pi_arg.curve = curve;
pi_arg.protect_algo = protect_algo; pi_arg.protect_algo = protect_algo;
pi_arg.iv = iv; pi_arg.iv = iv;
pi_arg.ivlen = ivlen; pi_arg.ivlen = ivlen;
@ -929,6 +949,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
err = make_canon_sexp_pad (s_skey, 1, r_key, NULL); err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
leave: leave:
xfree (curve);
gcry_sexp_release (s_skey); gcry_sexp_release (s_skey);
gcry_sexp_release (list); gcry_sexp_release (list);
gcry_sexp_release (top_list); gcry_sexp_release (top_list);
@ -1223,11 +1244,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
{ {
char countbuf[35]; char countbuf[35];
membuf_t mbuf; membuf_t mbuf;
void *format_args_buf_ptr[1];
int format_args_buf_int[1];
void *format_args[10+2]; void *format_args[10+2];
unsigned int n; gcry_sexp_t tmpkey;
gcry_sexp_t tmpkey, tmpsexp = NULL; gcry_sexp_t tmpsexp = NULL;
snprintf (countbuf, sizeof countbuf, "%lu", s2k_count); snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
@ -1238,11 +1257,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
put_membuf_str (&mbuf, " _ %m"); put_membuf_str (&mbuf, " _ %m");
format_args[j++] = array + i; format_args[j++] = array + i;
} }
put_membuf_str (&mbuf, " e %b"); put_membuf_str (&mbuf, " e %m");
format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n); format_args[j++] = array + npkey;
format_args_buf_int[0] = (n+7)/8;
format_args[j++] = format_args_buf_int;
format_args[j++] = format_args_buf_ptr;
put_membuf_str (&mbuf, ")\n"); put_membuf_str (&mbuf, ")\n");
put_membuf (&mbuf, "", 1); put_membuf (&mbuf, "", 1);

View File

@ -211,6 +211,7 @@ This format is used to transfer keys between gpg and gpg-agent.
(openpgp-private-key (openpgp-private-key
(version V) (version V)
(algo PUBKEYALGO) (algo PUBKEYALGO)
(curve CURVENAME)
(skey _ P1 _ P2 _ P3 ... e PN) (skey _ P1 _ P2 _ P3 ... e PN)
(csum n) (csum n)
(protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)) (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT))
@ -218,6 +219,7 @@ This format is used to transfer keys between gpg and gpg-agent.
* V is the packet version number (3 or 4). * V is the packet version number (3 or 4).
* PUBKEYALGO is a Libgcrypt algo name * PUBKEYALGO is a Libgcrypt algo name
* CURVENAME is the name of the curve - only used with ECC.
* P1 .. PN are the parameters; the public parameters are never encrypted * P1 .. PN are the parameters; the public parameters are never encrypted
the secrect key parameters are encrypted if the "protection" list is the secrect key parameters are encrypted if the "protection" list is
given. To make this more explicit each parameter is preceded by a given. To make this more explicit each parameter is preceded by a

View File

@ -280,17 +280,20 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits)
oidstr = "1.3.6.1.4.1.11591.15.1"; oidstr = "1.3.6.1.4.1.11591.15.1";
nbits = 255; nbits = 255;
} }
else if (!strcmp (name, "nistp256")) else if (!strcmp (name, "nistp256") || !strcmp (name, "NIST P-256"))
{ {
/* Libgcrypt uses "NIST P-256" as standard name for this curve
and thus the key generation returns this value. Thus we
allow both strings. */
oidstr = "1.2.840.10045.3.1.7"; oidstr = "1.2.840.10045.3.1.7";
nbits = 256; nbits = 256;
} }
else if (!strcmp (name, "nistp384")) else if (!strcmp (name, "nistp384") || !strcmp (name, "NIST P-384"))
{ {
oidstr = "1.3.132.0.34"; oidstr = "1.3.132.0.34";
nbits = 384; nbits = 384;
} }
else if (!strcmp (name, "nistp521")) else if (!strcmp (name, "nistp521") || !strcmp (name, "NIST P-521"))
{ {
oidstr = "1.3.132.0.35"; oidstr = "1.3.132.0.35";
nbits = 521; nbits = 521;

View File

@ -1128,37 +1128,6 @@ import_one (ctrl_t ctrl,
} }
/* Extract one MPI value from the S-expression PKEY which is expected
to hold a "public-key". Returns NULL on error. */
static gcry_mpi_t
one_mpi_from_pkey (gcry_sexp_t pkey, const char *name, size_t namelen)
{
gcry_sexp_t list, l2;
gcry_mpi_t a;
list = gcry_sexp_find_token (pkey, "public-key", 0);
if (!list)
return NULL;
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
if (!list)
return NULL;
l2 = gcry_sexp_find_token (list, name, namelen);
if (!l2)
{
gcry_sexp_release (list);
return NULL;
}
a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
gcry_sexp_release (list);
return a;
}
/* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The
function prints diagnostics and returns an error code. */ function prints diagnostics and returns an error code. */
static gpg_error_t static gpg_error_t
@ -1174,18 +1143,15 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
int nskey; int nskey;
membuf_t mbuf; membuf_t mbuf;
int i, j; int i, j;
unsigned int n;
void *format_args_buf_ptr[PUBKEY_MAX_NSKEY];
int format_args_buf_int[PUBKEY_MAX_NSKEY];
void *format_args[2*PUBKEY_MAX_NSKEY]; void *format_args[2*PUBKEY_MAX_NSKEY];
gcry_sexp_t skey, prot, tmpsexp; gcry_sexp_t skey, prot, tmpsexp;
gcry_sexp_t curve = NULL;
unsigned char *transferkey = NULL; unsigned char *transferkey = NULL;
size_t transferkeylen; size_t transferkeylen;
gcry_cipher_hd_t cipherhd = NULL; gcry_cipher_hd_t cipherhd = NULL;
unsigned char *wrappedkey = NULL; unsigned char *wrappedkey = NULL;
size_t wrappedkeylen; size_t wrappedkeylen;
char *cache_nonce = NULL; char *cache_nonce = NULL;
gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL};
/* Get the current KEK. */ /* Get the current KEK. */
err = agent_keywrap_key (ctrl, 0, &kek, &keklen); err = agent_keywrap_key (ctrl, 0, &kek, &keklen);
@ -1263,65 +1229,30 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH) || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{ {
/* We need special treatment for ECC algorithms. OpenPGP /* The ECC case. */
stores only the curve name but the agent expects a full char *curvestr = openpgp_oid_to_str (pk->pkey[0]);
key. This is so that we can keep all curve name if (!curvestr)
validation code out of gpg-agent. */
#if PUBKEY_MAX_NSKEY < 7
#error PUBKEY_MAX_NSKEY too low for ECC
#endif
char *curve = openpgp_oid_to_str (pk->pkey[0]);
if (!curve)
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
else else
{ {
gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECC, curve); err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvestr);
xfree (curvestr);
xfree (curve); if (!err)
if (!cparam)
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
else
{ {
const char *s; j = 0;
/* Append the public key element Q. */
put_membuf_str (&mbuf, " _ %m");
format_args[j++] = pk->pkey + 1;
/* Append the curve parameters P, A, B, G and N. */ /* Append the secret key element D. For ECDH we
for (i=j=0; !err && *(s = "pabgn"+i); i++) skip PKEY[2] because this holds the KEK which is
{ not needed by gpg-agent. */
ecc_params[i] = one_mpi_from_pkey (cparam, s, 1); i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
if (!ecc_params[i]) if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
err = gpg_error (GPG_ERR_INV_CURVE); put_membuf_str (&mbuf, " e %m");
else else
{ put_membuf_str (&mbuf, " _ %m");
put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i;
format_args[j++] = ecc_params+i;
}
}
gcry_sexp_release (cparam);
if (!err)
{
/* Append the public key element Q. */
put_membuf_str (&mbuf, " _ %m");
format_args[j++] = pk->pkey + 1;
/* Append the secret key element D. Note that
for ECDH we need to skip PKEY[2] because this
holds the KEK which is not needed. */
i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
{
put_membuf_str (&mbuf, " e %b");
format_args_buf_ptr[i]
= gcry_mpi_get_opaque (pk->pkey[i],&n);
format_args_buf_int[i] = (n+7)/8;
format_args[j++] = format_args_buf_int + i;
format_args[j++] = format_args_buf_ptr + i;
}
else
{
put_membuf_str (&mbuf, " _ %m");
format_args[j++] = pk->pkey + i;
}
}
} }
} }
} }
@ -1331,23 +1262,16 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
for (i=j=0; i < nskey; i++) for (i=j=0; i < nskey; i++)
{ {
if (!pk->pkey[i]) if (!pk->pkey[i])
; /* Protected keys only have NPKEY+1 elements. */ continue; /* Protected keys only have NPKEY+1 elements. */
else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
{ if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
put_membuf_str (&mbuf, " e %b"); put_membuf_str (&mbuf, " e %m");
format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i],&n);
format_args_buf_int[i] = (n+7)/8;
format_args[j++] = format_args_buf_int + i;
format_args[j++] = format_args_buf_ptr + i;
}
else else
{ put_membuf_str (&mbuf, " _ %m");
put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i;
format_args[j++] = pk->pkey + i;
}
} }
} }
put_membuf_str (&mbuf, ")\n"); put_membuf_str (&mbuf, ")");
put_membuf (&mbuf, "", 1); put_membuf (&mbuf, "", 1);
if (err) if (err)
xfree (get_membuf (&mbuf, NULL)); xfree (get_membuf (&mbuf, NULL));
@ -1398,12 +1322,13 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
"(openpgp-private-key\n" "(openpgp-private-key\n"
" (version %d)\n" " (version %d)\n"
" (algo %s)\n" " (algo %s)\n"
" %S\n" " %S%S\n"
" (csum %d)\n" " (csum %d)\n"
" %S)\n", " %S)\n",
pk->version, pk->version,
openpgp_pk_algo_name (pk->pubkey_algo), openpgp_pk_algo_name (pk->pubkey_algo),
skey, (int)(unsigned long)ski->csum, prot); curve, skey,
(int)(unsigned long)ski->csum, prot);
gcry_sexp_release (skey); gcry_sexp_release (skey);
gcry_sexp_release (prot); gcry_sexp_release (prot);
if (!err) if (!err)
@ -1463,8 +1388,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
} }
leave: leave:
for (i=0; i < DIM (ecc_params); i++) gcry_sexp_release (curve);
gcry_mpi_release (ecc_params[i]);
xfree (cache_nonce); xfree (cache_nonce);
xfree (wrappedkey); xfree (wrappedkey);
xfree (transferkey); xfree (transferkey);

View File

@ -2086,29 +2086,30 @@ ask_keysize (int algo, unsigned int primary_keysize)
} }
/* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE /* Ask for the curve. ALGO is the selected algorithm which this
is not 0, the function asks for the size of the encryption function may adjust. Returns a malloced string with the name of
subkey. */ the curve. */
static char * static char *
ask_curve (void) ask_curve (int *algo)
{ {
struct { struct {
const char *name; const char *name;
int available; int available;
int expert_only; int expert_only;
int fix_curve;
const char *pretty_name; const char *pretty_name;
} curves[] = { } curves[] = {
#if GPG_USE_EDDSA #if GPG_USE_EDDSA
{ "Ed25519", 0, 0, "Curve 25519" }, { "Curve25519", 0, 0, 1, "Curve 25519" },
#endif #endif
#if GPG_USE_ECDSA || GPG_USE_ECDH #if GPG_USE_ECDSA || GPG_USE_ECDH
{ "NIST P-256", 0, 1, }, { "NIST P-256", 0, 1, 0, },
{ "NIST P-384", 0, 0, }, { "NIST P-384", 0, 0, 0, },
{ "NIST P-521", 0, 1, }, { "NIST P-521", 0, 1, 0, },
{ "brainpoolP256r1", 0, 1, "Brainpool P-256" }, { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" },
{ "brainpoolP384r1", 0, 1, "Brainpool P-384" }, { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" },
{ "brainpoolP512r1", 0, 1, "Brainpool P-512" }, { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" },
{ "secp256k1", 0, 1 }, { "secp256k1", 0, 1, 0 },
#endif #endif
}; };
int idx; int idx;
@ -2127,9 +2128,14 @@ ask_curve (void)
if (!opt.expert && curves[idx].expert_only) if (!opt.expert && curves[idx].expert_only)
continue; continue;
/* FIXME: The strcmp below is a temporary hack during
development. It shall be removed as soon as we have proper
Curve25519 support in Libgcrypt. */
gcry_sexp_release (keyparms); gcry_sexp_release (keyparms);
rc = gcry_sexp_build (&keyparms, NULL, rc = gcry_sexp_build (&keyparms, NULL,
"(public-key(ecc(curve %s)))", curves[idx].name); "(public-key(ecc(curve %s)))",
(!strcmp (curves[idx].name, "Curve25519")
? "Ed25519" : curves[idx].name));
if (rc) if (rc)
continue; continue;
if (!gcry_pk_get_curve (keyparms, 0, NULL)) if (!gcry_pk_get_curve (keyparms, 0, NULL))
@ -2171,7 +2177,22 @@ ask_curve (void)
tty_printf (_("Invalid selection.\n")); tty_printf (_("Invalid selection.\n"));
else else
{ {
result = xstrdup (curves[idx].name); if (curves[idx].fix_curve)
log_info ("WARNING: Curve25519 is an experimental algorithm and"
" not yet specified by OpenPGP. The current"
" implementation may change with the next GnuPG release"
" and thus rendering the key unusable!\n");
/* If the user selected a signing algorithm and Curve25519
we need to update the algo and and the curve name. */
if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA)
&& curves[idx].fix_curve)
{
*algo = PUBKEY_ALGO_EDDSA;
result = xstrdup ("Ed25519");
}
else
result = xstrdup (curves[idx].name);
break; break;
} }
} }
@ -3459,16 +3480,16 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
{ {
/* Create primary and subkey at once. */ /* Create primary and subkey at once. */
both = 1; both = 1;
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYTYPE;
sprintf( r->u.value, "%d", algo );
r->next = para;
para = r;
if (algo == PUBKEY_ALGO_ECDSA if (algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_EDDSA
|| algo == PUBKEY_ALGO_ECDH) || algo == PUBKEY_ALGO_ECDH)
{ {
curve = ask_curve (); curve = ask_curve (&algo);
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYTYPE;
sprintf( r->u.value, "%d", algo);
r->next = para;
para = r;
nbits = 0; nbits = 0;
r = xmalloc_clear (sizeof *r + strlen (curve)); r = xmalloc_clear (sizeof *r + strlen (curve));
r->key = pKEYCURVE; r->key = pKEYCURVE;
@ -3478,6 +3499,11 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
} }
else else
{ {
r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYTYPE;
sprintf( r->u.value, "%d", algo);
r->next = para;
para = r;
nbits = ask_keysize (algo, 0); nbits = ask_keysize (algo, 0);
r = xmalloc_clear( sizeof *r + 20 ); r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYLENGTH; r->key = pKEYLENGTH;
@ -3501,9 +3527,43 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
strcpy( r->u.value, "encrypt" ); strcpy( r->u.value, "encrypt" );
r->next = para; r->next = para;
para = r; para = r;
if (algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA
|| algo == PUBKEY_ALGO_ECDH)
{
if (algo == PUBKEY_ALGO_EDDSA
&& subkey_algo == PUBKEY_ALGO_ECDH)
{
/* Need to switch to a different curve for the
encryption key. */
xfree (curve);
curve = xstrdup ("Curve25519");
}
r = xmalloc_clear (sizeof *r + strlen (curve));
r->key = pSUBKEYCURVE;
strcpy (r->u.value, curve);
r->next = para;
para = r;
}
} }
else else /* Create only a single key. */
{ {
/* For ECC we need to ask for the curve before storing the
algo becuase ask_curve may change the algo. */
if (algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA
|| algo == PUBKEY_ALGO_ECDH)
{
curve = ask_curve (&algo);
nbits = 0;
r = xmalloc_clear (sizeof *r + strlen (curve));
r->key = pKEYCURVE;
strcpy (r->u.value, curve);
r->next = para;
para = r;
}
r = xmalloc_clear( sizeof *r + 20 ); r = xmalloc_clear( sizeof *r + 20 );
r->key = pKEYTYPE; r->key = pKEYTYPE;
sprintf( r->u.value, "%d", algo ); sprintf( r->u.value, "%d", algo );
@ -3528,13 +3588,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
|| algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_EDDSA
|| algo == PUBKEY_ALGO_ECDH) || algo == PUBKEY_ALGO_ECDH)
{ {
if (!both) /* The curve has already been set. */
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 else
{ {
@ -4031,11 +4085,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
else if (algo == PUBKEY_ALGO_ECDSA else if (algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_EDDSA
|| algo == PUBKEY_ALGO_ECDH) || algo == PUBKEY_ALGO_ECDH)
{ curve = ask_curve (&algo);
curve = ask_curve ();
if (curve && !strcmp (curve, "Ed25519"))
algo = PUBKEY_ALGO_EDDSA;
}
else else
nbits = ask_keysize (algo, 0); nbits = ask_keysize (algo, 0);

View File

@ -2240,6 +2240,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
read_rest (inp, pktlen), read_rest (inp, pktlen),
pktlen * 8); pktlen * 8);
/* Mark that MPI as protected - we need this information for
importing a key. The OPAQUE flag can't be used because
we also store public EdDSA values in opaque MPIs. */
if (pk->pkey[npkey])
gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
pktlen = 0; pktlen = 0;
if (list_mode) if (list_mode)
es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey); es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
@ -2252,6 +2257,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
if (ski->is_protected) if (ski->is_protected)
{ {
pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen); pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen);
if (pk->pkey[i])
gcry_mpi_set_flag (pk->pkey[i], GCRYMPI_FLAG_USER1);
if (list_mode) if (list_mode)
es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i); es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i);
} }

View File

@ -188,7 +188,7 @@ echo 'This is one line' >z
for msg in $tests; do for msg in $tests; do
info "checking: $msg" info "checking: $msg"
eval "(IFS=; echo \"\$$msg\")" >x eval "(IFS=; echo \"\$$msg\")" >x
$GPG -o y --yes x || error "decryption of $msg failed" PINENTRY_USER_DATA=ecc $GPG -o y --yes x || error "decryption of $msg failed"
cmp y z || error "$msg: mismatch" cmp y z || error "$msg: mismatch"
done done
@ -204,7 +204,7 @@ for i in $plain_files $data_files ; do
for k in $mainkeyids ; do for k in $mainkeyids ; do
info "file: $i key: $k" info "file: $i key: $k"
$GPG ${opt_always} -e -o x --yes -r $k $i $GPG ${opt_always} -e -o x --yes -r $k $i
$GPG -o y --yes x PINENTRY_USER_DATA=ecc $GPG -o y --yes x
cmp $i y || error "$i,$k: mismatch" cmp $i y || error "$i,$k: mismatch"
done done
done done
@ -217,7 +217,7 @@ info "Checking ECC signing and verifiction."
for i in $plain_files $data_files ; do for i in $plain_files $data_files ; do
for k in $mainkeyids ; do for k in $mainkeyids ; do
info "file: $i key: $k" info "file: $i key: $k"
$GPG -s -o x --yes -u $k $i PINENTRY_USER_DATA=ecc $GPG -s -o x --yes -u $k $i
$GPG -o y --yes x || error "verify of $i,$k failed" $GPG -o y --yes x || error "verify of $i,$k failed"
cmp $i y || error "$i,$k: mismatch" cmp $i y || error "$i,$k: mismatch"
done done