mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +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:
parent
bdb9c2b314
commit
8fee6c1ce6
7 changed files with 231 additions and 229 deletions
138
g10/import.c
138
g10/import.c
|
@ -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
|
||||
function prints diagnostics and returns an error code. */
|
||||
static gpg_error_t
|
||||
|
@ -1174,18 +1143,15 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
|
|||
int nskey;
|
||||
membuf_t mbuf;
|
||||
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];
|
||||
gcry_sexp_t skey, prot, tmpsexp;
|
||||
gcry_sexp_t curve = NULL;
|
||||
unsigned char *transferkey = NULL;
|
||||
size_t transferkeylen;
|
||||
gcry_cipher_hd_t cipherhd = NULL;
|
||||
unsigned char *wrappedkey = NULL;
|
||||
size_t wrappedkeylen;
|
||||
char *cache_nonce = NULL;
|
||||
gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL};
|
||||
|
||||
/* Get the current KEK. */
|
||||
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_ECDH)
|
||||
{
|
||||
/* We need special treatment for ECC algorithms. OpenPGP
|
||||
stores only the curve name but the agent expects a full
|
||||
key. This is so that we can keep all curve name
|
||||
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)
|
||||
/* The ECC case. */
|
||||
char *curvestr = openpgp_oid_to_str (pk->pkey[0]);
|
||||
if (!curvestr)
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
{
|
||||
gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECC, curve);
|
||||
|
||||
xfree (curve);
|
||||
if (!cparam)
|
||||
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
||||
else
|
||||
err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvestr);
|
||||
xfree (curvestr);
|
||||
if (!err)
|
||||
{
|
||||
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. */
|
||||
for (i=j=0; !err && *(s = "pabgn"+i); i++)
|
||||
{
|
||||
ecc_params[i] = one_mpi_from_pkey (cparam, s, 1);
|
||||
if (!ecc_params[i])
|
||||
err = gpg_error (GPG_ERR_INV_CURVE);
|
||||
else
|
||||
{
|
||||
put_membuf_str (&mbuf, " _ %m");
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* Append the secret key element D. For ECDH we
|
||||
skip PKEY[2] because this holds the KEK which is
|
||||
not needed by gpg-agent. */
|
||||
i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
|
||||
if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
|
||||
put_membuf_str (&mbuf, " e %m");
|
||||
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++)
|
||||
{
|
||||
if (!pk->pkey[i])
|
||||
; /* Protected keys only have NPKEY+1 elements. */
|
||||
else 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;
|
||||
}
|
||||
continue; /* Protected keys only have NPKEY+1 elements. */
|
||||
|
||||
if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
|
||||
put_membuf_str (&mbuf, " e %m");
|
||||
else
|
||||
{
|
||||
put_membuf_str (&mbuf, " _ %m");
|
||||
format_args[j++] = pk->pkey + i;
|
||||
}
|
||||
put_membuf_str (&mbuf, " _ %m");
|
||||
format_args[j++] = pk->pkey + i;
|
||||
}
|
||||
}
|
||||
put_membuf_str (&mbuf, ")\n");
|
||||
put_membuf_str (&mbuf, ")");
|
||||
put_membuf (&mbuf, "", 1);
|
||||
if (err)
|
||||
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"
|
||||
" (version %d)\n"
|
||||
" (algo %s)\n"
|
||||
" %S\n"
|
||||
" %S%S\n"
|
||||
" (csum %d)\n"
|
||||
" %S)\n",
|
||||
pk->version,
|
||||
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 (prot);
|
||||
if (!err)
|
||||
|
@ -1463,8 +1388,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
|
|||
}
|
||||
|
||||
leave:
|
||||
for (i=0; i < DIM (ecc_params); i++)
|
||||
gcry_mpi_release (ecc_params[i]);
|
||||
gcry_sexp_release (curve);
|
||||
xfree (cache_nonce);
|
||||
xfree (wrappedkey);
|
||||
xfree (transferkey);
|
||||
|
|
116
g10/keygen.c
116
g10/keygen.c
|
@ -2086,29 +2086,30 @@ 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. */
|
||||
/* Ask for the curve. ALGO is the selected algorithm which this
|
||||
function may adjust. Returns a malloced string with the name of
|
||||
the curve. */
|
||||
static char *
|
||||
ask_curve (void)
|
||||
ask_curve (int *algo)
|
||||
{
|
||||
struct {
|
||||
const char *name;
|
||||
int available;
|
||||
int expert_only;
|
||||
int fix_curve;
|
||||
const char *pretty_name;
|
||||
} curves[] = {
|
||||
#if GPG_USE_EDDSA
|
||||
{ "Ed25519", 0, 0, "Curve 25519" },
|
||||
{ "Curve25519", 0, 0, 1, "Curve 25519" },
|
||||
#endif
|
||||
#if GPG_USE_ECDSA || GPG_USE_ECDH
|
||||
{ "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" },
|
||||
{ "secp256k1", 0, 1 },
|
||||
{ "NIST P-256", 0, 1, 0, },
|
||||
{ "NIST P-384", 0, 0, 0, },
|
||||
{ "NIST P-521", 0, 1, 0, },
|
||||
{ "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" },
|
||||
{ "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" },
|
||||
{ "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" },
|
||||
{ "secp256k1", 0, 1, 0 },
|
||||
#endif
|
||||
};
|
||||
int idx;
|
||||
|
@ -2127,9 +2128,14 @@ ask_curve (void)
|
|||
if (!opt.expert && curves[idx].expert_only)
|
||||
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);
|
||||
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)
|
||||
continue;
|
||||
if (!gcry_pk_get_curve (keyparms, 0, NULL))
|
||||
|
@ -2171,7 +2177,22 @@ ask_curve (void)
|
|||
tty_printf (_("Invalid selection.\n"));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -3459,16 +3480,16 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
|
|||
{
|
||||
/* Create primary and subkey at once. */
|
||||
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
|
||||
|| algo == PUBKEY_ALGO_EDDSA
|
||||
|| 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;
|
||||
r = xmalloc_clear (sizeof *r + strlen (curve));
|
||||
r->key = pKEYCURVE;
|
||||
|
@ -3478,6 +3499,11 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
|
|||
}
|
||||
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);
|
||||
r = xmalloc_clear( sizeof *r + 20 );
|
||||
r->key = pKEYLENGTH;
|
||||
|
@ -3501,9 +3527,43 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
|
|||
strcpy( r->u.value, "encrypt" );
|
||||
r->next = para;
|
||||
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->key = pKEYTYPE;
|
||||
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_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;
|
||||
/* The curve has already been set. */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4031,11 +4085,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
|
|||
else if (algo == PUBKEY_ALGO_ECDSA
|
||||
|| algo == PUBKEY_ALGO_EDDSA
|
||||
|| algo == PUBKEY_ALGO_ECDH)
|
||||
{
|
||||
curve = ask_curve ();
|
||||
if (curve && !strcmp (curve, "Ed25519"))
|
||||
algo = PUBKEY_ALGO_EDDSA;
|
||||
}
|
||||
curve = ask_curve (&algo);
|
||||
else
|
||||
nbits = ask_keysize (algo, 0);
|
||||
|
||||
|
|
|
@ -2240,6 +2240,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
|
|||
pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
|
||||
read_rest (inp, pktlen),
|
||||
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;
|
||||
if (list_mode)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue