aboutsummaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/all-tests.scm7
-rw-r--r--g10/build-packet.c24
-rw-r--r--g10/call-agent.c20
-rw-r--r--g10/call-agent.h2
-rw-r--r--g10/card-util.c1
-rw-r--r--g10/export.c431
-rw-r--r--g10/gpg.c7
-rw-r--r--g10/gpgv.c8
-rw-r--r--g10/import.c71
-rw-r--r--g10/keygen.c4
-rw-r--r--g10/keylist.c82
-rw-r--r--g10/main.h5
-rw-r--r--g10/mainproc.c2
-rw-r--r--g10/options.h2
-rw-r--r--g10/parse-packet.c48
-rw-r--r--g10/test-stubs.c8
16 files changed, 597 insertions, 125 deletions
diff --git a/g10/all-tests.scm b/g10/all-tests.scm
index 982220b28..02fcde7b5 100644
--- a/g10/all-tests.scm
+++ b/g10/all-tests.scm
@@ -27,9 +27,10 @@
(parse-makefile-expand filename expander key))
(map (lambda (name)
- (test::binary #f
- (path-join "g10" name)
- (path-join (getenv "objdir") "g10" name)))
+ (let ((name-ext (string-append name (getenv "EXEEXT"))))
+ (test::binary #f
+ (path-join "g10" name-ext)
+ (path-join (getenv "objdir") "g10" name-ext))))
(parse-makefile-expand (in-srcdir "g10" "Makefile.am")
(lambda (filename port key) (parse-makefile port key))
"module_tests")))
diff --git a/g10/build-packet.c b/g10/build-packet.c
index cc953557d..f33d156b3 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -674,7 +674,8 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
count += 8; /* Salt. */
if (ski->s2k.mode == 3)
count++; /* S2K.COUNT */
- if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002)
+ if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002
+ && ski->s2k.mode != 1003)
count += ski->ivlen;
iobuf_put (a, count);
@@ -704,8 +705,9 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
if (ski->s2k.mode == 3)
iobuf_put (a, ski->s2k.count);
- /* For our special modes 1001, 1002 we do not need an IV. */
- if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002)
+ /* For our special modes 1001..1003 we do not need an IV. */
+ if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002
+ && ski->s2k.mode != 1003)
iobuf_write (a, ski->iv, ski->ivlen);
}
@@ -733,6 +735,22 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
/* The serial number gets stored in the IV field. */
iobuf_write (a, ski->iv, ski->ivlen);
}
+ else if (ski->s2k.mode == 1003)
+ {
+ /* GnuPG extension - Store raw s-expression. */
+ byte *p;
+ unsigned int ndatabits;
+
+ log_assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE));
+
+ p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits);
+ /* For v5 keys we first write the number of octets of the
+ * following key material. */
+ if (is_v5)
+ write_32 (a, p? (ndatabits+7)/8 : 0);
+ if (p)
+ iobuf_write (a, p, (ndatabits+7)/8 );
+ }
else if (ski->is_protected)
{
/* The secret key is protected - write it out as it is. */
diff --git a/g10/call-agent.c b/g10/call-agent.c
index 27b5cacfb..66812e998 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -2997,13 +2997,15 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
keygrip, DESC a prompt to be displayed with the agent's passphrase
question (needs to be plus+percent escaped). if OPENPGP_PROTECTED
is not zero, ensure that the key material is returned in RFC
- 4880-compatible passphrased-protected form. If CACHE_NONCE_ADDR is
- not NULL the agent is advised to first try a passphrase associated
- with that nonce. On success the key is stored as a canonical
- S-expression at R_RESULT and R_RESULTLEN. */
+ 4880-compatible passphrased-protected form; if instead MODE1003 is
+ not zero the raw gpg-agent private key format is requested (either
+ protected or unprotected). If CACHE_NONCE_ADDR is not NULL the
+ agent is advised to first try a passphrase associated with that
+ nonce. On success the key is stored as a canonical S-expression at
+ R_RESULT and R_RESULTLEN. */
gpg_error_t
agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
- int openpgp_protected, char **cache_nonce_addr,
+ int openpgp_protected, int mode1003, char **cache_nonce_addr,
unsigned char **r_result, size_t *r_resultlen,
u32 *keyid, u32 *mainkeyid, int pubkey_algo)
{
@@ -3028,6 +3030,12 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
return err;
dfltparm.ctx = agent_ctx;
+ /* Check that the gpg-agent supports the --mode1003 option. */
+ if (mode1003 && assuan_transact (agent_ctx,
+ "GETINFO cmd_has_option EXPORT_KEY mode1003",
+ NULL, NULL, NULL, NULL, NULL, NULL))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
if (desc)
{
snprintf (line, DIM(line), "SETKEYDESC %s", desc);
@@ -3038,7 +3046,7 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
}
snprintf (line, DIM(line), "EXPORT_KEY %s%s%s %s",
- openpgp_protected ? "--openpgp ":"",
+ mode1003? "--mode1003" : openpgp_protected ? "--openpgp ":"",
cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"",
cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"",
hexkeygrip);
diff --git a/g10/call-agent.h b/g10/call-agent.h
index a4cbc3162..a3f234ade 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -231,7 +231,7 @@ gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc,
/* Receive a key from the agent. */
gpg_error_t agent_export_key (ctrl_t ctrl, const char *keygrip,
const char *desc, int openpgp_protected,
- char **cache_nonce_addr,
+ int mode1003, char **cache_nonce_addr,
unsigned char **r_result, size_t *r_resultlen,
u32 *keyid, u32 *mainkeyid, int pubkey_algo);
diff --git a/g10/card-util.c b/g10/card-util.c
index 339194fb5..02de241f2 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -843,7 +843,6 @@ change_name (void)
{
tty_printf (_("Error: Combined name too long "
"(limit is %d characters).\n"), 39);
- xfree (isoname);
rc = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
diff --git a/g10/export.c b/g10/export.c
index edf27bc4d..cab00d10c 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -2,6 +2,7 @@
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
* 2005, 2010 Free Software Foundation, Inc.
* Copyright (C) 1998-2016 Werner Koch
+ * Copyright (C) 2022 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -63,15 +64,17 @@ struct export_stats_s
};
-/* A global variable to store the selector created from
+/* Global variables to store the selectors created from
* --export-filter keep-uid=EXPR.
* --export-filter drop-subkey=EXPR.
+ * --export-filter select=EXPR.
*
* FIXME: We should put this into the CTRL object but that requires a
* lot more changes right now.
*/
static recsel_expr_t export_keep_uid;
static recsel_expr_t export_drop_subkey;
+static recsel_expr_t export_select_filter;
/* An object used for a linked list to implement the
@@ -81,6 +84,7 @@ struct export_filter_attic_s
struct export_filter_attic_s *next;
recsel_expr_t export_keep_uid;
recsel_expr_t export_drop_subkey;
+ recsel_expr_t export_select_filter;
};
static struct export_filter_attic_s *export_filter_attic;
@@ -105,6 +109,8 @@ cleanup_export_globals (void)
export_keep_uid = NULL;
recsel_release (export_drop_subkey);
export_drop_subkey = NULL;
+ recsel_release (export_select_filter);
+ export_select_filter = NULL;
}
@@ -128,10 +134,16 @@ parse_export_options(char *str,unsigned int *options,int noisy)
{"export-dane", EXPORT_DANE_FORMAT, NULL, NULL },
+ {"export-revocs", EXPORT_REVOCS, NULL,
+ N_("export only revocation certificates") },
+
{"backup", EXPORT_BACKUP, NULL,
N_("use the GnuPG key backup format")},
{"export-backup", EXPORT_BACKUP, NULL, NULL },
+ {"mode1003", EXPORT_MODE1003, NULL,
+ N_("export secret keys using the GnuPG format") },
+
/* Aliases for backward compatibility */
{"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL},
{"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL},
@@ -184,6 +196,8 @@ parse_export_options(char *str,unsigned int *options,int noisy)
*
* - secret :: 1 for a secret subkey, else 0.
* - key_algo :: Public key algorithm id
+ *
+ * - select :: The key is only exported if the filter returns true.
*/
gpg_error_t
parse_and_set_export_filter (const char *string)
@@ -197,6 +211,8 @@ parse_and_set_export_filter (const char *string)
err = recsel_parse_expr (&export_keep_uid, string+9);
else if (!strncmp (string, "drop-subkey=", 12))
err = recsel_parse_expr (&export_drop_subkey, string+12);
+ else if (!strncmp (string, "select=", 7))
+ err = recsel_parse_expr (&export_select_filter, string+7);
else
err = gpg_error (GPG_ERR_INV_NAME);
@@ -217,6 +233,8 @@ push_export_filters (void)
export_keep_uid = NULL;
item->export_drop_subkey = export_drop_subkey;
export_drop_subkey = NULL;
+ item->export_select_filter = export_select_filter;
+ export_select_filter = NULL;
item->next = export_filter_attic;
export_filter_attic = item;
}
@@ -235,6 +253,7 @@ pop_export_filters (void)
cleanup_export_globals ();
export_keep_uid = item->export_keep_uid;
export_drop_subkey = item->export_drop_subkey;
+ export_select_filter = item->export_select_filter;
}
@@ -624,6 +643,183 @@ canon_pk_algo (enum gcry_pk_algos algo)
}
+/* Take an s-expression wit the public and private key and change the
+ * parameter array in PK to include the secret parameters. */
+static gpg_error_t
+secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
+{
+ gpg_error_t err;
+ gcry_sexp_t list = NULL;
+ gcry_sexp_t l2;
+ enum gcry_pk_algos pk_algo;
+ struct seckey_info *ski;
+ int idx;
+ char *string;
+ size_t npkey, nskey;
+ gcry_mpi_t pub_params[10] = { NULL };
+
+ /* We look for a private-key, then the first element in it tells us
+ the type */
+ list = gcry_sexp_find_token (s_key, "protected-private-key", 0);
+ if (!list)
+ list = gcry_sexp_find_token (s_key, "private-key", 0);
+ if (!list)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ log_assert (!pk->seckey_info);
+
+ /* Parse the gcrypt PK algo and check that it is okay. */
+ l2 = gcry_sexp_cadr (list);
+ if (!l2)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ gcry_sexp_release (list);
+ list = l2;
+ string = gcry_sexp_nth_string (list, 0);
+ if (!string)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ pk_algo = gcry_pk_map_name (string);
+ xfree (string); string = NULL;
+ if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
+ || gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
+ || !npkey || npkey >= nskey)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ /* Check that the pubkey algo and the received parameters matches
+ * those from the public key. */
+ switch (canon_pk_algo (pk_algo))
+ {
+ case GCRY_PK_RSA:
+ if (!is_RSA (pk->pubkey_algo) || npkey != 2)
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */
+ else
+ err = gcry_sexp_extract_param (list, NULL, "ne",
+ &pub_params[0],
+ &pub_params[1],
+ NULL);
+ break;
+
+ case GCRY_PK_DSA:
+ if (!is_DSA (pk->pubkey_algo) || npkey != 4)
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */
+ else
+ err = gcry_sexp_extract_param (list, NULL, "pqgy",
+ &pub_params[0],
+ &pub_params[1],
+ &pub_params[2],
+ &pub_params[3],
+ NULL);
+ break;
+
+ case GCRY_PK_ELG:
+ if (!is_ELGAMAL (pk->pubkey_algo) || npkey != 3)
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */
+ else
+ err = gcry_sexp_extract_param (list, NULL, "pgy",
+ &pub_params[0],
+ &pub_params[1],
+ &pub_params[2],
+ NULL);
+ break;
+
+ case GCRY_PK_ECC:
+ err = 0;
+ if (!(pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+ || pk->pubkey_algo == PUBKEY_ALGO_ECDH
+ || pk->pubkey_algo == PUBKEY_ALGO_EDDSA))
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */
+ goto leave;
+ }
+ npkey = 2;
+ if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ npkey++;
+ /* Dedicated check of the curve. */
+ pub_params[0] = NULL;
+ err = match_curve_skey_pk (list, pk);
+ if (err)
+ goto leave;
+ /* ... and of the Q parameter. */
+ err = sexp_extract_param_sos (list, "q", &pub_params[1]);
+ if (!err && (gcry_mpi_cmp (pk->pkey[1], pub_params[1])))
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ break;
+
+ default:
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Unknown. */
+ break;
+ }
+ if (err)
+ goto leave;
+
+ nskey = npkey + 1; /* We only have one skey param. */
+ if (nskey > PUBKEY_MAX_NSKEY)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ /* Check that the public key parameters match. For ECC we already
+ * did this in the switch above. */
+ if (canon_pk_algo (pk_algo) != GCRY_PK_ECC)
+ {
+ for (idx=0; idx < npkey; idx++)
+ if (gcry_mpi_cmp (pk->pkey[idx], pub_params[idx]))
+ {
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ goto leave;
+ }
+ }
+
+ /* Store the maybe protected secret key as an s-expression. */
+ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+ if (!ski)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+ if (!ski)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ ski->is_protected = 1;
+ ski->s2k.mode = 1003;
+
+ {
+ unsigned char *buf;
+ size_t buflen;
+
+ err = make_canon_sexp (s_key, &buf, &buflen);
+ if (err)
+ goto leave;
+ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, buf, buflen*8);
+ for (idx=npkey+1; idx < PUBKEY_MAX_NSKEY; idx++)
+ pk->pkey[idx] = NULL;
+ }
+
+ leave:
+ gcry_sexp_release (list);
+ for (idx=0; idx < DIM(pub_params); idx++)
+ gcry_mpi_release (pub_params[idx]);
+ return err;
+}
+
+
/* Take a cleartext dump of a secret key in PK and change the
* parameter array in PK to include the secret parameters. */
static gpg_error_t
@@ -1233,29 +1429,51 @@ print_status_exported (PKT_public_key *pk)
* passphrase-protected. Otherwise, store secret key material in the
* clear.
*
+ * If MODE1003 is set, the key is requested in raw GnuPG format from
+ * the agent. This usually does not require a passphrase unless the
+ * gpg-agent has not yet used the key and needs to convert it to its
+ * internal format first.
+ *
* CACHE_NONCE_ADDR is used to share nonce for multiple key retrievals.
+ *
+ * If PK is NULL, the raw key is returned (e.g. for ssh export) at
+ * R_KEY. CLEARTEXT and CACHE_NONCE_ADDR ared ignored in this case.
*/
gpg_error_t
receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
- int cleartext,
+ int cleartext, int mode1003,
char **cache_nonce_addr, const char *hexgrip,
- PKT_public_key *pk)
+ PKT_public_key *pk, gcry_sexp_t *r_key)
{
gpg_error_t err = 0;
unsigned char *wrappedkey = NULL;
size_t wrappedkeylen;
unsigned char *key = NULL;
size_t keylen, realkeylen;
- gcry_sexp_t s_skey;
+ gcry_sexp_t s_skey = NULL;
char *prompt;
+ if (r_key)
+ *r_key = NULL;
if (opt.verbose)
log_info ("key %s: asking agent for the secret parts\n", hexgrip);
- prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT,1);
- err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr,
- &wrappedkey, &wrappedkeylen,
- pk->keyid, pk->main_keyid, pk->pubkey_algo);
+ if (pk)
+ {
+ prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT, 1);
+ err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, mode1003,
+ cache_nonce_addr,
+ &wrappedkey, &wrappedkeylen,
+ pk->keyid, pk->main_keyid, pk->pubkey_algo);
+ }
+ else
+ {
+ prompt = gpg_format_keydesc (ctrl, NULL, FORMAT_KEYDESC_KEYGRIP, 1);
+ err = agent_export_key (ctrl, hexgrip, prompt, 0, 0,
+ NULL,
+ &wrappedkey, &wrappedkeylen,
+ NULL, NULL, 0);
+ }
xfree (prompt);
if (err)
@@ -1282,14 +1500,21 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen);
if (!err)
{
- if (cleartext)
+ if (pk && mode1003)
+ err = secret_key_to_mode1003 (s_skey, pk);
+ else if (pk && cleartext)
err = cleartext_secret_key_to_openpgp (s_skey, pk);
- else
+ else if (pk)
err = transfer_format_to_openpgp (s_skey, pk);
- gcry_sexp_release (s_skey);
+ else if (r_key)
+ {
+ *r_key = s_skey;
+ s_skey = NULL;
+ }
}
unwraperror:
+ gcry_sexp_release (s_skey);
xfree (key);
xfree (wrappedkey);
if (err)
@@ -1795,8 +2020,10 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
else if (!err)
{
err = receive_seckey_from_agent (ctrl, cipherhd,
- cleartext, &cache_nonce,
- hexgrip, pk);
+ cleartext,
+ !!(options & EXPORT_MODE1003),
+ &cache_nonce,
+ hexgrip, pk, NULL);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
@@ -1872,6 +2099,78 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
}
+/* Helper for do_export_stream which writes the own revocations
+ * certificates (if any) from KEYBLOCK to OUT. */
+static gpg_error_t
+do_export_revocs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
+ iobuf_t out, unsigned int options, int *any)
+{
+ gpg_error_t err = 0;
+ kbnode_t kbctx, node;
+ PKT_signature *sig;
+
+ (void)ctrl;
+
+ /* NB: walk_kbnode skips packets marked as deleted. */
+ for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
+ {
+ if (node->pkt->pkttype != PKT_SIGNATURE)
+ continue;
+ sig = node->pkt->pkt.signature;
+
+ /* We are only interested in revocation certifcates. */
+ if (!(IS_KEY_REV (sig) || IS_UID_REV (sig) || IS_SUBKEY_REV (sig)))
+ continue;
+
+ if (!(sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1]))
+ continue; /* Not a self-signature. */
+
+ /* Do not export signature packets which are marked as not
+ * exportable. */
+ if (!(options & EXPORT_LOCAL_SIGS)
+ && !sig->flags.exportable)
+ continue; /* not exportable */
+
+ /* Do not export packets with a "sensitive" revocation key
+ * unless the user wants us to. */
+ if (!(options & EXPORT_SENSITIVE_REVKEYS)
+ && sig->revkey)
+ {
+ int i;
+
+ for (i = 0; i < sig->numrevkeys; i++)
+ if ((sig->revkey[i].class & 0x40))
+ break;
+ if (i < sig->numrevkeys)
+ continue;
+ }
+
+ if (!sig->flags.checked)
+ {
+ log_info ("signature not marked as checked - ignored\n");
+ continue;
+ }
+ if (!sig->flags.valid)
+ {
+ log_info ("signature not not valid - ignored\n");
+ continue;
+ }
+
+ err = build_packet (out, node->pkt);
+ if (err)
+ {
+ log_error ("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, gpg_strerror (err));
+ goto leave;
+ }
+ *any = 1;
+ }
+
+ leave:
+ return err;
+}
+
+
/* For secret key export we need to setup a decryption context.
* Returns 0 and the context at r_cipherhd. */
static gpg_error_t
@@ -2066,13 +2365,33 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
NULL, NULL);
commit_kbnode (&keyblock);
}
- else if (export_keep_uid || export_drop_subkey)
+ else if (export_keep_uid || export_drop_subkey || export_select_filter)
{
/* Need to merge so that for example the "usage" property
* has been setup. */
merge_keys_and_selfsig (ctrl, keyblock);
}
+
+ if (export_select_filter)
+ {
+ int selected = 0;
+ struct impex_filter_parm_s parm;
+ parm.ctrl = ctrl;
+
+ for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
+ {
+ if (recsel_select (export_select_filter,
+ impex_filter_getval, &parm))
+ {
+ selected = 1;
+ break;
+ }
+ }
+ if (!selected)
+ continue; /* Skip this keyblock. */
+ }
+
if (export_keep_uid)
{
commit_kbnode (&keyblock);
@@ -2088,10 +2407,15 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
}
/* And write it. */
- err = do_export_one_keyblock (ctrl, keyblock, keyid,
- out_help? out_help : out,
- secret, options, stats, any,
- desc, ndesc, descindex, cipherhd);
+ if ((options & EXPORT_REVOCS))
+ err = do_export_revocs (ctrl, keyblock, keyid,
+ out_help? out_help : out,
+ options, any);
+ else
+ err = do_export_one_keyblock (ctrl, keyblock, keyid,
+ out_help? out_help : out,
+ secret, options, stats, any,
+ desc, ndesc, descindex, cipherhd);
if (err)
break;
@@ -2602,74 +2926,6 @@ export_ssh_key (ctrl_t ctrl, const char *userid)
}
-/* Simplified version of receive_seckey_from_agent used to get the raw
- * key. */
-gpg_error_t
-receive_raw_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
- const char *hexgrip, gcry_sexp_t *r_key)
-{
- gpg_error_t err = 0;
- unsigned char *wrappedkey = NULL;
- size_t wrappedkeylen;
- unsigned char *key = NULL;
- size_t keylen, realkeylen;
- gcry_sexp_t s_skey = NULL;
-
- *r_key = NULL;
- if (opt.verbose)
- log_info ("key %s: asking agent for the secret parts\n", hexgrip);
-
- {
- char * prompt = gpg_format_keydesc (ctrl, NULL, FORMAT_KEYDESC_KEYGRIP, 1);
- err = agent_export_key (ctrl, hexgrip, prompt, 0, NULL,
- &wrappedkey, &wrappedkeylen,
- NULL, NULL, 0);
- xfree (prompt);
- }
- if (err)
- goto leave;
-
- if (wrappedkeylen < 24)
- {
- err = gpg_error (GPG_ERR_INV_LENGTH);
- goto leave;
- }
- keylen = wrappedkeylen - 8;
- key = xtrymalloc_secure (keylen);
- if (!key)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen);
- if (err)
- goto leave;
- realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
- if (!realkeylen)
- goto leave; /* Invalid csexp. */
-
- err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen);
- if (!err)
- {
- gcry_log_debugsxp ("skey", s_skey);
- *r_key = s_skey;
- s_skey = NULL;
- }
-
- leave:
- gcry_sexp_release (s_skey);
- xfree (key);
- xfree (wrappedkey);
- if (err)
- {
- log_error ("key %s: error receiving key from agent:"
- " %s%s\n", hexgrip, gpg_strerror (err),
- "");
- }
- return err;
-}
-
-
/* Export the key identified by USERID in the SSH secret key format.
* The USERID must be given in keygrip format (prefixed with a '&')
* and thus no OpenPGP key is required. The exported key is not
@@ -2715,7 +2971,8 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid)
if ((err = get_keywrap_key (ctrl, &cipherhd)))
goto leave;
- err = receive_raw_seckey_from_agent (ctrl, cipherhd, hexgrip, &skey);
+ err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, NULL, hexgrip, NULL,
+ &skey);
if (err)
goto leave;
diff --git a/g10/gpg.c b/g10/gpg.c
index 1514254b9..68c0454ee 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -327,6 +327,7 @@ enum cmd_and_opt_values
oExportOptions,
oExportFilter,
oListOptions,
+ oListFilter,
oVerifyOptions,
oTempDir,
oExecPath,
@@ -794,6 +795,7 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_header ("Keylist", N_("Options controlling key listings")),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
+ ARGPARSE_s_s (oListFilter, "list-filter", "@"),
ARGPARSE_s_n (oFullTimestrings, "full-timestrings", "@"),
ARGPARSE_s_n (oShowPhotos, "show-photos", "@"),
ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"),
@@ -3357,6 +3359,11 @@ main (int argc, char **argv)
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
+ case oListFilter:
+ rc = parse_and_set_list_filter (pargs.r.ret_str);
+ if (rc)
+ log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
+ break;
case oListOptions:
if(!parse_list_options(pargs.r.ret_str))
{
diff --git a/g10/gpgv.c b/g10/gpgv.c
index 3bb99dc6c..ceded4af9 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -812,3 +812,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason,
*r_comment = NULL;
return 0;
}
+
+const char *
+impex_filter_getval (void *cookie, const char *propname)
+{
+ (void)cookie;
+ (void)propname;
+ return NULL;
+}
diff --git a/g10/import.c b/g10/import.c
index c3ef89323..9fab46ca6 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1430,7 +1430,8 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock)
}
-/* Helper for apply_*_filter in import.c and export.c. */
+/* Helper for apply_*_filter in import.c and export.c and also used by
+ * keylist.c. */
const char *
impex_filter_getval (void *cookie, const char *propname)
{
@@ -1440,11 +1441,32 @@ impex_filter_getval (void *cookie, const char *propname)
kbnode_t node = parm->node;
static char numbuf[20];
const char *result;
+ const char *s;
+ enum { scpNone = 0, scpPub, scpSub, scpUid, scpSig} scope = 0;
log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC);
- if (node->pkt->pkttype == PKT_USER_ID
- || node->pkt->pkttype == PKT_ATTRIBUTE)
+ /* We allow a prefix delimited by a slash to limit the scope of the
+ * keyword. Note that "pub" also includes "sec" and "sub" includes
+ * "ssb". */
+ if ((s=strchr (propname, '/')) && s != propname)
+ {
+ size_t n = s - propname;
+ if (!strncmp (propname, "pub", n))
+ scope = scpPub;
+ else if (!strncmp (propname, "sub", n))
+ scope = scpSub;
+ else if (!strncmp (propname, "uid", n))
+ scope = scpUid;
+ else if (!strncmp (propname, "sig", n))
+ scope = scpSig;
+
+ propname = s + 1;
+ }
+
+ if ((node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_ATTRIBUTE)
+ && (!scope || scope == scpUid))
{
PKT_user_id *uid = node->pkt->pkt.user_id;
@@ -1473,7 +1495,8 @@ impex_filter_getval (void *cookie, const char *propname)
else
result = NULL;
}
- else if (node->pkt->pkttype == PKT_SIGNATURE)
+ else if (node->pkt->pkttype == PKT_SIGNATURE
+ && (!scope || scope == scpSig))
{
PKT_signature *sig = node->pkt->pkt.signature;
@@ -1503,10 +1526,12 @@ impex_filter_getval (void *cookie, const char *propname)
else
result = NULL;
}
- else if (node->pkt->pkttype == PKT_PUBLIC_KEY
- || node->pkt->pkttype == PKT_SECRET_KEY
- || node->pkt->pkttype == PKT_PUBLIC_SUBKEY
- || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ else if (((node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_SECRET_KEY)
+ && (!scope || scope == scpPub))
+ || ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ && (!scope || scope == scpSub)))
{
PKT_public_key *pk = node->pkt->pkt.public_key;
@@ -1520,6 +1545,16 @@ impex_filter_getval (void *cookie, const char *propname)
snprintf (numbuf, sizeof numbuf, "%d", pk->pubkey_algo);
result = numbuf;
}
+ else if (!strcmp (propname, "key_size"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%u", nbits_from_pk (pk));
+ result = numbuf;
+ }
+ else if (!strcmp (propname, "algostr"))
+ {
+ pubkey_string (pk, parm->hexfpr, sizeof parm->hexfpr);
+ result = parm->hexfpr;
+ }
else if (!strcmp (propname, "key_created"))
{
snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->timestamp);
@@ -1556,6 +1591,26 @@ impex_filter_getval (void *cookie, const char *propname)
hexfingerprint (pk, parm->hexfpr, sizeof parm->hexfpr);
result = parm->hexfpr;
}
+ else if (!strcmp (propname, "origin"))
+ {
+ result = key_origin_string (pk->keyorg);
+ }
+ else if (!strcmp (propname, "lastupd"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->keyupdate);
+ result = numbuf;
+ }
+ else if (!strcmp (propname, "url"))
+ {
+ if (pk->updateurl && *pk->updateurl)
+ {
+ /* Fixme: This might get truncated. */
+ mem2str (parm->hexfpr, pk->updateurl, sizeof parm->hexfpr);
+ result = parm->hexfpr;
+ }
+ else
+ result = "";
+ }
else
result = NULL;
}
diff --git a/g10/keygen.c b/g10/keygen.c
index c2f2dcc9d..4dcf7a494 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -5286,8 +5286,8 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
goto leave;
}
- err = receive_seckey_from_agent (ctrl, cipherhd, 0,
- &cache_nonce, hexgrip, sk);
+ err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0,
+ &cache_nonce, hexgrip, sk, NULL);
if (err)
{
log_error ("error getting secret key from agent: %s\n",
diff --git a/g10/keylist.c b/g10/keylist.c
index f8e4ff32a..1ced732a4 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -44,6 +44,8 @@
#include "../common/mbox-util.h"
#include "../common/zb32.h"
#include "tofu.h"
+#include "../common/init.h"
+#include "../common/recsel.h"
#include "../common/compliance.h"
#include "../common/pkscreening.h"
@@ -64,16 +66,26 @@ struct keylist_context
int no_validity; /* Do not show validity. */
};
-
-static void list_keyblock (ctrl_t ctrl,
- kbnode_t keyblock, int secret, int has_secret,
- int fpr, struct keylist_context *listctx);
+/* An object and a global instance to store selectors created from
+ * --list-filter select=EXPR.
+ */
+struct list_filter_s
+{
+ recsel_expr_t selkey;
+};
+struct list_filter_s list_filter;
/* The stream used to write attribute packets to. */
static estream_t attrib_fp;
+
+
+static void list_keyblock (ctrl_t ctrl,
+ kbnode_t keyblock, int secret, int has_secret,
+ int fpr, struct keylist_context *listctx);
+
/* Release resources from a keylist context. */
static void
keylist_context_release (struct keylist_context *listctx)
@@ -82,6 +94,49 @@ keylist_context_release (struct keylist_context *listctx)
}
+static void
+release_list_filter (struct list_filter_s *filt)
+{
+ recsel_release (filt->selkey);
+ filt->selkey = NULL;
+}
+
+
+static void
+cleanup_keylist_globals (void)
+{
+ release_list_filter (&list_filter);
+}
+
+
+/* Parse and set an list filter from string. STRING has the format
+ * "NAME=EXPR" with NAME being the name of the filter. Spaces before
+ * and after NAME are not allowed. If this function is all called
+ * several times all expressions for the same NAME are concatenated.
+ * Supported filter names are:
+ *
+ * - select :: If the expression evaluates to true for a certain key
+ * this key will be listed. The expression may use any
+ * variable defined for the export and import filters.
+ *
+ */
+gpg_error_t
+parse_and_set_list_filter (const char *string)
+{
+ gpg_error_t err;
+
+ /* Auto register the cleanup function. */
+ register_mem_cleanup_func (cleanup_keylist_globals);
+
+ if (!strncmp (string, "select=", 7))
+ err = recsel_parse_expr (&list_filter.selkey, string+7);
+ else
+ err = gpg_error (GPG_ERR_INV_NAME);
+
+ return err;
+}
+
+
/* List the keys. If list is NULL, all available keys are listed.
* With LOCATE_MODE set the locate algorithm is used to find a key; if
* in addition NO_LOCAL is set the locate does not look into the local
@@ -2163,6 +2218,7 @@ reorder_keyblock (KBNODE keyblock)
do_reorder_keyblock (keyblock, 0);
}
+
static void
list_keyblock (ctrl_t ctrl,
KBNODE keyblock, int secret, int has_secret, int fpr,
@@ -2170,6 +2226,24 @@ list_keyblock (ctrl_t ctrl,
{
reorder_keyblock (keyblock);
+ if (list_filter.selkey)
+ {
+ int selected = 0;
+ struct impex_filter_parm_s parm;
+ parm.ctrl = ctrl;
+
+ for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
+ {
+ if (recsel_select (list_filter.selkey, impex_filter_getval, &parm))
+ {
+ selected = 1;
+ break;
+ }
+ }
+ if (!selected)
+ return; /* Skip this one. */
+ }
+
if (opt.with_colons)
list_keyblock_colon (ctrl, keyblock, secret, has_secret);
else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
diff --git a/g10/main.h b/g10/main.h
index 2f14f374b..62d2651be 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -431,10 +431,10 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
void **r_data, size_t *r_datalen);
gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
- int cleartext,
+ int cleartext, int mode1003,
char **cache_nonce_addr,
const char *hexgrip,
- PKT_public_key *pk);
+ PKT_public_key *pk, gcry_sexp_t *r_key);
gpg_error_t write_keyblock_to_output (kbnode_t keyblock,
int with_armor, unsigned int options);
@@ -464,6 +464,7 @@ void release_revocation_reason_info (struct revocation_reason_info *reason);
void public_key_list (ctrl_t ctrl, strlist_t list,
int locate_mode, int no_local);
void secret_key_list (ctrl_t ctrl, strlist_t list );
+gpg_error_t parse_and_set_list_filter (const char *string);
void print_subpackets_colon(PKT_signature *sig);
void reorder_keyblock (KBNODE keyblock);
void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret,
diff --git a/g10/mainproc.c b/g10/mainproc.c
index f8f3c15bc..330ad10c5 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -726,7 +726,7 @@ proc_encrypted (CTX c, PACKET *pkt)
}
/* Compute compliance with CO_DE_VS. */
- if (!result && is_status_enabled ()
+ if (!result && (is_status_enabled () || opt.flags.require_compliance)
/* Overriding session key voids compliance. */
&& !opt.override_session_key
/* Check symmetric cipher. */
diff --git a/g10/options.h b/g10/options.h
index b439ef120..c10862687 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -406,6 +406,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
#define EXPORT_CLEAN (1<<5)
#define EXPORT_DANE_FORMAT (1<<7)
#define EXPORT_BACKUP (1<<10)
+#define EXPORT_REVOCS (1<<11)
+#define EXPORT_MODE1003 (1<<12)
#define LIST_SHOW_PHOTOS (1<<0)
#define LIST_SHOW_POLICY_URLS (1<<1)
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index b6aebbb69..a033732ec 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -2752,11 +2752,15 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
break;
case 1001:
if (list_mode)
- es_fprintf (listfp, "\tgnu-dummy S2K");
+ es_fprintf (listfp, "\tgnu-dummy");
break;
case 1002:
if (list_mode)
- es_fprintf (listfp, "\tgnu-divert-to-card S2K");
+ es_fprintf (listfp, "\tgnu-divert-to-card");
+ break;
+ case 1003:
+ if (list_mode)
+ es_fprintf (listfp, "\tgnu-mode1003");
break;
default:
if (list_mode)
@@ -2768,7 +2772,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
}
/* Print some info. */
- if (list_mode)
+ if (list_mode && ski->s2k.mode != 1003)
{
es_fprintf (listfp, ", algo: %d,%s hash: %d",
ski->algo,
@@ -2779,8 +2783,9 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
es_fprintf (listfp, ", salt: ");
es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL);
}
- es_putc ('\n', listfp);
- }
+ }
+ if (list_mode)
+ es_putc ('\n', listfp);
/* Read remaining protection parameters. */
if (ski->s2k.mode == 3)
@@ -2838,7 +2843,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
ski->ivlen = openpgp_cipher_blocklen (ski->algo);
log_assert (ski->ivlen <= sizeof (temp));
- if (ski->s2k.mode == 1001)
+ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1003)
ski->ivlen = 0;
else if (ski->s2k.mode == 1002)
ski->ivlen = snlen < 16 ? snlen : 16;
@@ -2850,7 +2855,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
}
for (i = 0; i < ski->ivlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
- if (list_mode)
+ if (list_mode && ski->s2k.mode != 1003)
{
es_fprintf (listfp,
ski->s2k.mode == 1002 ? "\tserial-number: "
@@ -2888,6 +2893,35 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
10 * 8);
pktlen = 0;
}
+ else if (ski->s2k.mode == 1003)
+ {
+ void *tmpp;
+
+ if (pktlen < 2) /* At least two bytes for parenthesis. */
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+
+ tmpp = read_rest (inp, pktlen);
+ if (list_mode)
+ {
+ if (mpi_print_mode)
+ {
+ char *tmpsxp = canon_sexp_to_string (tmpp, pktlen);
+ es_fprintf (listfp, "\tskey[%d]: %s\n", npkey,
+ tmpsxp? trim_trailing_spaces (tmpsxp)
+ /* */: "[invalid S-expression]");
+ xfree (tmpsxp);
+ }
+ else
+ es_fprintf (listfp, "\tskey[%d]: [s-expression %lu octets]\n",
+ npkey, pktlen);
+ }
+ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
+ tmpp, tmpp? pktlen * 8 : 0);
+ pktlen = 0;
+ }
else if (ski->is_protected)
{
void *tmpp;
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index cfe33b1d0..6ae0f4eb7 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -572,3 +572,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason,
*r_comment = NULL;
return 0;
}
+
+const char *
+impex_filter_getval (void *cookie, const char *propname)
+{
+ (void)cookie;
+ (void)propname;
+ return NULL;
+}