gpg: New option --include-key-block.

* common/openpgpdefs.h (SIGSUBPKT_KEY_BLOCK): New.
* g10/gpg.c (oIncludeKeyBlock): New.
(opts): New option --include-key-block.
(main): Implement.
* g10/options.h (opt): New flag include_key_block.
* g10/parse-packet.c (dump_sig_subpkt): Support SIGSUBPKT_KEY_BLOCK.
(parse_one_sig_subpkt): Ditto.
(can_handle_critical): Ditto.
* g10/sign.c (mk_sig_subpkt_key_block): New.
(write_signature_packets): Call it for data signatures.
--

This patch adds support for a to be proposed OpenPGP ferature:

  Introduce the Key Block subpacket to align OpenPGP with CMS.

  This new subpacket may be used similar to the CertificateSet of
  CMS (RFC-5652) and thus allows to start encrypted communication
  after having received a signed message.  In practice a stripped down
  version of the key should be including having only the key material
  and the self-signatures which are really useful and shall be used by
  the recipient to reply encrypted.

  #### Key Block

  (1 octet with value 0, N octets of key data)

  This subpacket MAY be used to convey key data along with a signature
  of class 0x00, 0x01, or 0x02.  It MUST contain the key used to create
  the signature; either as the primary key or as a subkey.  The key
  SHOULD contain a primary or subkey capable of encryption and the
  entire key must be a valid OpenPGP key including at least one User ID
  packet and the corresponding self-signatures.

  Implementations MUST ignore this subpacket if the first octet does not
  have a value of zero or if the key data does not represent a valid
  transferable public key.

GnuPG-bug-id: 4856
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-03-13 13:28:35 +01:00
parent 32493ce50a
commit 865d485180
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
6 changed files with 145 additions and 11 deletions

View File

@ -120,6 +120,7 @@ typedef enum
SIGSUBPKT_PREF_AEAD = 34, /* Preferred AEAD algorithms. */
SIGSUBPKT_ATTST_SIGS = 37, /* Attested Certifications. */
SIGSUBPKT_KEY_BLOCK = 38, /* Entire key used. */
SIGSUBPKT_FLAG_CRITICAL = 128
}

View File

@ -2700,6 +2700,16 @@ As of now this is only done if the signing key has been specified with
information can be helpful for verifier to locate the key; see option
@option{--auto-key-retrieve}.
@item --include-key-block
@opindex include-key-block
This option is used to embed the actual signing key into a data
signature. The embedded key is stripped down to a single user id and
includes only the signing subkey used to create the signature as well
as as valid encryption subkeys. All other info is removed from the
key to keep it and thus the signature small. This option is the
OpenPGP counterpart to the @command{gpgsm} option
@option{--include-certs}.
@item --personal-cipher-preferences @var{string}
@opindex personal-cipher-preferences
Set the list of personal cipher preferences to @var{string}. Use

View File

@ -433,6 +433,7 @@ enum cmd_and_opt_values
oNoSymkeyCache,
oUseOnlyOpenPGPCard,
oFullTimestrings,
oIncludeKeyBlock,
oNoop
};
@ -748,7 +749,9 @@ static gpgrt_opt_t opts[] = {
N_("|N|set compress level to N (0 disables)")),
ARGPARSE_s_i (oCompressLevel, "compress-level", "@"),
ARGPARSE_s_i (oBZ2CompressLevel, "bzip2-compress-level", "@"),
ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"),
ARGPARSE_s_n (oIncludeKeyBlock, "include-key-block",
N_("include the public key in the signature")),
ARGPARSE_header ("ImportExport",
N_("Options controlling key import and export")),
@ -826,7 +829,6 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"),
ARGPARSE_s_s (oOverrideSessionKey, "override-session-key", "@"),
ARGPARSE_s_i (oOverrideSessionKeyFD, "override-session-key-fd", "@"),
ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"),
ARGPARSE_header ("Security", N_("Options controlling the security")),
@ -3032,6 +3034,7 @@ main (int argc, char **argv)
case oForceAEAD: opt.force_aead = 1; break;
case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break;
case oIncludeKeyBlock: opt.flags.include_key_block = 1; break;
case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break;
case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break;

View File

@ -241,6 +241,7 @@ struct
unsigned int allow_weak_key_signatures:1;
unsigned int large_rsa:1;
unsigned int disable_signer_uid:1;
unsigned int include_key_block:1;
/* Flag to enable experimental features from RFC4880bis. */
unsigned int rfc4880bis:1;
/* Hack: --output is not given but OUTFILE was temporary set to "-". */

View File

@ -1615,6 +1615,23 @@ dump_sig_subpkt (int hashed, int type, int critical,
}
break;
case SIGSUBPKT_KEY_BLOCK:
es_fputs ("key-block: ", listfp);
if (length && buffer[0])
p = "[unknown reserved octet]";
else if (length < 50) /* 50 is an arbitrary min. length. */
p = "[invalid subpacket]";
else
{
/* estream_t fp; */
/* fp = es_fopen ("a.key-block", "wb"); */
/* log_assert (fp); */
/* es_fwrite ( buffer+1, length-1, 1, fp); */
/* es_fclose (fp); */
es_fprintf (listfp, "[%u octets]", (unsigned int)length-1);
}
break;
default:
if (type >= 100 && type <= 110)
@ -1692,6 +1709,12 @@ parse_one_sig_subpkt (const byte * buffer, size_t n, int type)
if (n != 2)
break;
return 0;
case SIGSUBPKT_KEY_BLOCK:
if (n && buffer[0])
return -1; /* Unknown version - ignore. */
if (n < 50)
break; /* Definitely too short to carry a key block. */
return 0;
default:
return 0;
}
@ -1760,6 +1783,12 @@ can_handle_critical (const byte * buffer, size_t n, int type)
case SIGSUBPKT_REVOC_REASON: /* At least we know about it. */
return 1;
case SIGSUBPKT_KEY_BLOCK:
if (n && !buffer[0])
return 1;
else
return 0;
default:
return 0;
}

View File

@ -202,6 +202,91 @@ mk_notation_policy_etc (ctrl_t ctrl, PKT_signature *sig,
}
/*
* Put the Key Block subpakcet into SIG for key PKSK. Returns an
* error code on failure.
*/
static gpg_error_t
mk_sig_subpkt_key_block (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pksk)
{
gpg_error_t err;
char *mbox;
char *filterexp = NULL;
int save_opt_armor = opt.armor;
int save_opt_verbose = opt.verbose;
char hexfpr[2*MAX_FINGERPRINT_LEN + 1];
void *data = NULL;
size_t datalen;
kbnode_t keyblock = NULL;
push_export_filters ();
opt.armor = 0;
hexfingerprint (pksk, hexfpr, sizeof hexfpr);
/* Get the user id so that we know which one to insert into the
* key. */
if (pksk->user_id
&& (mbox = mailbox_from_userid (pksk->user_id->name, 0)))
{
if (DBG_LOOKUP)
log_debug ("including key with UID '%s' (specified)\n", mbox);
filterexp = xasprintf ("keep-uid= -- mbox = %s", mbox);
xfree (mbox);
}
else if (opt.sender_list)
{
/* If --sender was given we use the first one from that list. */
if (DBG_LOOKUP)
log_debug ("including key with UID '%s' (--sender)\n",
opt.sender_list->d);
filterexp = xasprintf ("keep-uid= -- mbox = %s", opt.sender_list->d);
}
else /* Use the primary user id. */
{
if (DBG_LOOKUP)
log_debug ("including key with primary UID\n");
filterexp = xstrdup ("keep-uid= primary -t");
}
if (DBG_LOOKUP)
log_debug ("export filter expression: %s\n", filterexp);
err = parse_and_set_export_filter (filterexp);
if (err)
goto leave;
xfree (filterexp);
filterexp = xasprintf ("drop-subkey= fpr <> %s && usage !~ e", hexfpr);
if (DBG_LOOKUP)
log_debug ("export filter expression: %s\n", filterexp);
err = parse_and_set_export_filter (filterexp);
if (err)
goto leave;
opt.verbose = 0;
err = export_pubkey_buffer (ctrl, hexfpr, EXPORT_MINIMAL|EXPORT_CLEAN,
"", 1, /* Prefix with the reserved byte. */
NULL, &keyblock, &data, &datalen);
opt.verbose = save_opt_verbose;
if (err)
{
log_error ("failed to get to be included key: %s\n", gpg_strerror (err));
goto leave;
}
build_sig_subpkt (sig, SIGSUBPKT_KEY_BLOCK, data, datalen);
leave:
xfree (data);
release_kbnode (keyblock);
xfree (filterexp);
opt.armor = save_opt_armor;
pop_export_filters ();
return err;
}
/*
* Helper to hash a user ID packet.
*/
@ -835,7 +920,7 @@ write_signature_packets (ctrl_t ctrl,
PKT_public_key *pk;
PKT_signature *sig;
gcry_md_hd_t md;
int rc;
gpg_error_t err;
pk = sk_rover->pk;
@ -865,12 +950,17 @@ write_signature_packets (ctrl_t ctrl,
build_sig_subpkt_from_sig (sig, pk);
mk_notation_policy_etc (ctrl, sig, NULL, pk);
if (opt.flags.include_key_block && IS_SIG (sig))
err = mk_sig_subpkt_key_block (ctrl, sig, pk);
else
err = 0;
hash_sigversion_to_magic (md, sig, extrahash);
gcry_md_final (md);
rc = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce, 0);
if (!err)
err = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce, 0);
gcry_md_close (md);
if (!rc)
if (!err)
{
/* Write the packet. */
PACKET pkt;
@ -878,19 +968,19 @@ write_signature_packets (ctrl_t ctrl,
init_packet (&pkt);
pkt.pkttype = PKT_SIGNATURE;
pkt.pkt.signature = sig;
rc = build_packet (out, &pkt);
if (!rc && is_status_enabled())
err = build_packet (out, &pkt);
if (!err && is_status_enabled())
print_status_sig_created (pk, sig, status_letter);
free_packet (&pkt, NULL);
if (rc)
if (err)
log_error ("build signature packet failed: %s\n",
gpg_strerror (rc));
gpg_strerror (err));
}
else
free_seckey_enc (sig);
if (rc)
return rc;
if (err)
return err;
}
return 0;