Page MenuHome GnuPG

No OneTemporary

diff --git a/g10/cipher.c b/g10/cipher.c
index f10ce486b..f577c97db 100644
--- a/g10/cipher.c
+++ b/g10/cipher.c
@@ -1,186 +1,187 @@
/* cipher.c - En-/De-ciphering filter
* Copyright (C) 1998-2003, 2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 1998-2003, 2006, 2009, 2017 Werner koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "../common/util.h"
#include "filter.h"
#include "packet.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#include "../common/status.h"
#define MIN_PARTIAL_SIZE 512
static void
write_header (cipher_filter_context_t *cfx, iobuf_t a)
{
gcry_error_t err;
PACKET pkt;
PKT_encrypted ed;
byte temp[18];
unsigned int blocksize;
unsigned int nprefix;
blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo);
if ( blocksize < 8 || blocksize > 16 )
log_fatal ("unsupported blocksize %u\n", blocksize);
memset (&ed, 0, sizeof ed);
ed.len = cfx->datalen;
ed.extralen = blocksize + 2;
ed.new_ctb = !ed.len;
if (cfx->dek->use_mdc)
{
ed.mdc_method = DIGEST_ALGO_SHA1;
gcry_md_open (&cfx->mdc_hash, DIGEST_ALGO_SHA1, 0);
if (DBG_HASHING)
gcry_md_debug (cfx->mdc_hash, "creatmdc");
}
else
{
log_info (_("WARNING: "
"encrypting without integrity protection is dangerous\n"));
log_info (_("Hint: Do not use option %s\n"), "--rfc2440");
}
write_status_printf (STATUS_BEGIN_ENCRYPTION, "%d %d",
ed.mdc_method, cfx->dek->algo);
init_packet (&pkt);
pkt.pkttype = cfx->dek->use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED;
pkt.pkt.encrypted = &ed;
if (build_packet( a, &pkt))
log_bug ("build_packet(ENCR_DATA) failed\n");
nprefix = blocksize;
gcry_randomize (temp, nprefix, GCRY_STRONG_RANDOM );
temp[nprefix] = temp[nprefix-2];
temp[nprefix+1] = temp[nprefix-1];
print_cipher_algo_note (cfx->dek->algo);
err = openpgp_cipher_open (&cfx->cipher_hd,
cfx->dek->algo,
GCRY_CIPHER_MODE_CFB,
(GCRY_CIPHER_SECURE
| ((cfx->dek->use_mdc || cfx->dek->algo >= 100)?
0 : GCRY_CIPHER_ENABLE_SYNC)));
if (err)
{
/* We should never get an error here cause we already checked,
* that the algorithm is available. */
BUG();
}
/* log_hexdump ("thekey", cfx->dek->key, cfx->dek->keylen); */
gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen);
gcry_cipher_setiv (cfx->cipher_hd, NULL, 0);
/* log_hexdump ("prefix", temp, nprefix+2); */
if (cfx->mdc_hash) /* Hash the "IV". */
gcry_md_write (cfx->mdc_hash, temp, nprefix+2 );
gcry_cipher_encrypt (cfx->cipher_hd, temp, nprefix+2, NULL, 0);
gcry_cipher_sync (cfx->cipher_hd);
iobuf_write (a, temp, nprefix+2);
cfx->short_blklen_warn = (blocksize < 16);
cfx->short_blklen_count = nprefix+2;
cfx->wrote_header = 1;
}
/*
* This filter is used to en/de-cipher data with a symmetric algorithm
*/
int
-cipher_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len)
+cipher_filter_cfb (void *opaque, int control,
+ iobuf_t a, byte *buf, size_t *ret_len)
{
cipher_filter_context_t *cfx = opaque;
size_t size = *ret_len;
int rc = 0;
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{
rc = -1; /* not yet used */
}
else if (control == IOBUFCTRL_FLUSH) /* encrypt */
{
log_assert (a);
if (!cfx->wrote_header)
write_header (cfx, a);
if (cfx->mdc_hash)
gcry_md_write (cfx->mdc_hash, buf, size);
gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0);
if (cfx->short_blklen_warn)
{
cfx->short_blklen_count += size;
if (cfx->short_blklen_count > (150 * 1024 * 1024))
{
log_info ("WARNING: encrypting more than %d MiB with algorithm "
"%s should be avoided\n", 150,
openpgp_cipher_algo_name (cfx->dek->algo));
cfx->short_blklen_warn = 0; /* Don't show again. */
}
}
rc = iobuf_write (a, buf, size);
}
else if (control == IOBUFCTRL_FREE)
{
if (cfx->mdc_hash)
{
byte *hash;
int hashlen = gcry_md_get_algo_dlen (gcry_md_get_algo(cfx->mdc_hash));
byte temp[22];
log_assert (hashlen == 20);
/* We must hash the prefix of the MDC packet here. */
temp[0] = 0xd3;
temp[1] = 0x14;
gcry_md_putc (cfx->mdc_hash, temp[0]);
gcry_md_putc (cfx->mdc_hash, temp[1]);
gcry_md_final (cfx->mdc_hash);
hash = gcry_md_read (cfx->mdc_hash, 0);
memcpy(temp+2, hash, 20);
gcry_cipher_encrypt (cfx->cipher_hd, temp, 22, NULL, 0);
gcry_md_close (cfx->mdc_hash); cfx->mdc_hash = NULL;
if (iobuf_write( a, temp, 22))
log_error ("writing MDC packet failed\n");
}
gcry_cipher_close (cfx->cipher_hd);
}
else if (control == IOBUFCTRL_DESC)
{
- mem2str (buf, "cipher_filter", *ret_len);
+ mem2str (buf, "cipher_filter_cfb", *ret_len);
}
return rc;
}
diff --git a/g10/encrypt.c b/g10/encrypt.c
index e3e044f23..9cf8abdd7 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -1,1051 +1,1051 @@
/* encrypt.c - Main encryption driver
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "filter.h"
#include "trustdb.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "pkglue.h"
#include "../common/compliance.h"
static int encrypt_simple( const char *filename, int mode, int use_seskey );
static int write_pubkey_enc_from_list (ctrl_t ctrl,
PK_LIST pk_list, DEK *dek, iobuf_t out);
/****************
* Encrypt FILENAME with only the symmetric cipher. Take input from
* stdin if FILENAME is NULL.
*/
int
encrypt_symmetric (const char *filename)
{
return encrypt_simple( filename, 1, 0 );
}
/****************
* Encrypt FILENAME as a literal data packet only. Take input from
* stdin if FILENAME is NULL.
*/
int
encrypt_store (const char *filename)
{
return encrypt_simple( filename, 0, 0 );
}
/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and
the algorithm that will be used to encrypt the contents of the SED
packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random
session key that is appropriate for DEK->ALGO is generated and
stored there.
Encrypt that session key using DEK and store the result in ENCKEY,
which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */
void
encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey)
{
gcry_cipher_hd_t hd;
byte buf[33];
log_assert ( dek->keylen <= 32 );
if (!*seskey)
{
*seskey=xmalloc_clear(sizeof(DEK));
(*seskey)->algo=dek->algo;
make_session_key(*seskey);
/*log_hexdump( "thekey", c->key, c->keylen );*/
}
/* The encrypted session key is prefixed with a one-octet algorithm id. */
buf[0] = (*seskey)->algo;
memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
/* We only pass already checked values to the following function,
thus we consider any failure as fatal. */
if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
BUG ();
if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
BUG ();
gcry_cipher_setiv (hd, NULL, 0);
gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
gcry_cipher_close (hd);
memcpy( enckey, buf, (*seskey)->keylen + 1 );
wipememory( buf, sizeof buf ); /* burn key */
}
/* Shall we use the MDC? Yes - unless rfc-2440 compatibility is
* requested. Must return 1 or 0. */
int
use_mdc (pk_list_t pk_list,int algo)
{
(void)pk_list;
(void)algo;
/* RFC-2440 don't has MDC - this is the only way to create a legacy
* non-MDC encryption packet. */
if (RFC2440)
return 0;
return 1; /* In all other cases we use the MDC */
}
/* We don't want to use use_seskey yet because older gnupg versions
can't handle it, and there isn't really any point unless we're
making a message that can be decrypted by a public key or
passphrase. */
static int
encrypt_simple (const char *filename, int mode, int use_seskey)
{
iobuf_t inp, out;
PACKET pkt;
PKT_plaintext *pt = NULL;
STRING2KEY *s2k = NULL;
byte enckey[33];
int rc = 0;
int seskeylen = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
int do_compress = !!default_compress_algo();
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", rc);
return rc;
}
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
/* Prepare iobufs. */
inp = iobuf_open(filename);
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]",
strerror(errno) );
release_progress_context (pfx);
return rc;
}
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter( inp, text_filter, &tfx );
cfx.dek = NULL;
if ( mode )
{
rc = setup_symkey (&s2k, &cfx.dek);
if (rc)
{
iobuf_close (inp);
if (gpg_err_code (rc) == GPG_ERR_CIPHER_ALGO
|| gpg_err_code (rc) == GPG_ERR_DIGEST_ALGO)
; /* Error has already been printed. */
else
log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc));
release_progress_context (pfx);
return rc;
}
if (use_seskey && s2k->mode != 1 && s2k->mode != 3)
{
use_seskey = 0;
log_info (_("can't use a symmetric ESK packet "
"due to the S2K mode\n"));
}
if ( use_seskey )
{
DEK *dek = NULL;
seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ());
encrypt_seskey( cfx.dek, &dek, enckey );
xfree( cfx.dek ); cfx.dek = dek;
}
if (opt.verbose)
log_info(_("using cipher %s\n"),
openpgp_cipher_algo_name (cfx.dek->algo));
cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
}
if (do_compress && cfx.dek && cfx.dek->use_mdc
&& is_file_compressed(filename, &rc))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename);
do_compress = 0;
}
if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out )))
{
iobuf_cancel (inp);
xfree (cfx.dek);
xfree (s2k);
release_progress_context (pfx);
return rc;
}
if ( opt.armor )
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
if ( s2k )
{
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
enc->version = 4;
enc->cipher_algo = cfx.dek->algo;
enc->s2k = *s2k;
if ( use_seskey && seskeylen )
{
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy (enc->seskey, enckey, seskeylen + 1 );
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc = build_packet( out, &pkt )))
log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
xfree (enc);
}
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Note that PGP 5 has problems decrypting symmetrically encrypted
data if the file length is in the inner packet. It works when
only partial length headers are use. In the past, we always used
partial body length here, but since PGP 2, PGP 6, and PGP 7 need
the file length, and nobody should be using PGP 5 nowadays
anyway, this is now set to the file length. Note also that this
only applies to the RFC-1991 style symmetric messages, and not
the RFC-2440 style. PGP 6 and 7 work with either partial length
or fixed length with the new style messages. */
if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the
size of a file is larger than 2^32 minus some bytes for
packet headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
/* Note that PT has been initialized above in !no_literal mode. */
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
}
else
{
cfx.datalen = filesize && !do_compress ? filesize : 0;
pkt.pkttype = 0;
pkt.pkt.generic = NULL;
}
/* Register the cipher filter. */
if (mode)
- iobuf_push_filter ( out, cipher_filter, &cfx );
+ iobuf_push_filter ( out, cipher_filter_cfb, &cfx );
/* Register the compress filter. */
if ( do_compress )
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter (out, &zfx, default_compress_algo());
}
/* Do the work. */
if (!opt.no_literal)
{
if ( (rc = build_packet( out, &pkt )) )
log_error("build_packet failed: %s\n", gpg_strerror (rc) );
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc) );
break;
}
wipememory (copy_buffer, 4096); /* burn buffer */
}
/* Finish the stuff. */
iobuf_close (inp);
if (rc)
iobuf_cancel(out);
else
{
iobuf_close (out); /* fixme: check returncode */
if (mode)
write_status ( STATUS_END_ENCRYPTION );
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (cfx.dek);
xfree (s2k);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
gpg_error_t
setup_symkey (STRING2KEY **symkey_s2k, DEK **symkey_dek)
{
int canceled;
int defcipher;
int s2kdigest;
defcipher = default_cipher_algo ();
if (!gnupg_cipher_is_allowed (opt.compliance, 1, defcipher,
GCRY_CIPHER_MODE_CFB))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (defcipher),
gnupg_compliance_option_string (opt.compliance));
return gpg_error (GPG_ERR_CIPHER_ALGO);
}
s2kdigest = S2K_DIGEST_ALGO;
if (!gnupg_digest_is_allowed (opt.compliance, 1, s2kdigest))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (s2kdigest),
gnupg_compliance_option_string (opt.compliance));
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
*symkey_s2k = xmalloc_clear (sizeof **symkey_s2k);
(*symkey_s2k)->mode = opt.s2k_mode;
(*symkey_s2k)->hash_algo = s2kdigest;
*symkey_dek = passphrase_to_dek (defcipher,
*symkey_s2k, 1, 0, NULL, 0, &canceled);
if (!*symkey_dek || !(*symkey_dek)->keylen)
{
xfree(*symkey_dek);
xfree(*symkey_s2k);
return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE);
}
return 0;
}
static int
write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
iobuf_t out)
{
int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo);
PKT_symkey_enc *enc;
byte enckey[33];
PACKET pkt;
enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
encrypt_seskey(symkey_dek,&dek,enckey);
enc->version = 4;
enc->cipher_algo = opt.s2k_cipher_algo;
enc->s2k = *symkey_s2k;
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy( enc->seskey, enckey, seskeylen + 1 );
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc=build_packet(out,&pkt)))
log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc));
xfree(enc);
return rc;
}
/*
* Encrypt the file with the given userids (or ask if none is
* supplied). Either FILENAME or FILEFD must be given, but not both.
* The caller may provide a checked list of public keys in
* PROVIDED_PKS; if not the function builds a list of keys on its own.
*
* Note that FILEFD is currently only used by cmd_encrypt in the
* not yet finished server.c.
*/
int
encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
int outputfd)
{
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
PKT_plaintext *pt = NULL;
DEK *symkey_dek = NULL;
STRING2KEY *symkey_s2k = NULL;
int rc = 0, rc2 = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
PK_LIST pk_list;
int do_compress;
int compliant;
if (filefd != -1 && filename)
return gpg_error (GPG_ERR_INV_ARG); /* Both given. */
do_compress = !!opt.compress_algo;
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
if (use_symkey
&& (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
{
release_progress_context (pfx);
return rc;
}
if (provided_keys)
pk_list = provided_keys;
else
{
if ((rc = build_pk_list (ctrl, remusr, &pk_list)))
{
release_progress_context (pfx);
return rc;
}
}
/* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
if (filefd == -1)
inp = iobuf_open (filename);
else
{
inp = NULL;
gpg_err_set_errno (ENOSYS);
}
#else
if (filefd == GNUPG_INVALID_FD)
inp = iobuf_open (filename);
else
inp = iobuf_fdopen_nc (FD2INT(filefd), "rb");
#endif
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
char xname[64];
rc = gpg_error_from_syserror ();
if (filefd != -1)
snprintf (xname, sizeof xname, "[fd %d]", filefd);
else if (!filename)
strcpy (xname, "[stdin]");
else
*xname = 0;
log_error (_("can't open '%s': %s\n"),
*xname? xname : filename, gpg_strerror (rc) );
goto leave;
}
if (opt.verbose)
log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out);
if (rc)
goto leave;
if (opt.armor)
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
/* Create a session key. */
cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
if (!opt.def_cipher_algo)
{
/* Try to get it from the prefs. */
cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL);
/* The only way select_algo_from_prefs can fail here is when
mixing v3 and v4 keys, as v4 keys have an implicit preference
entry for 3DES, and the pk_list cannot be empty. In this
case, use 3DES anyway as it's the safest choice - perhaps the
v3 key is being used in an OpenPGP implementation and we know
that the implementation behind any v4 key can handle 3DES. */
if (cfx.dek->algo == -1)
{
cfx.dek->algo = CIPHER_ALGO_3DES;
}
/* In case 3DES has been selected, print a warning if any key
does not have a preference for AES. This should help to
indentify why encrypting to several recipients falls back to
3DES. */
if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES)
warn_missing_aes_from_pklist (pk_list);
}
else
{
if (!opt.expert
&& (select_algo_from_prefs (pk_list, PREFTYPE_SYM,
opt.def_cipher_algo, NULL)
!= opt.def_cipher_algo))
{
log_info(_("WARNING: forcing symmetric cipher %s (%d)"
" violates recipient preferences\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
opt.def_cipher_algo);
}
cfx.dek->algo = opt.def_cipher_algo;
}
/* Check compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo,
GCRY_CIPHER_MODE_CFB))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (cfx.dek->algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", rc);
goto leave;
}
compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo,
GCRY_CIPHER_MODE_CFB);
{
pk_list_t pkr;
for (pkr = pk_list; pkr; pkr = pkr->next)
{
PKT_public_key *pk = pkr->pk;
unsigned int nbits = nbits_from_pk (pk);
if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits, NULL))
log_info (_("WARNING: key %s is not suitable for encryption"
" in %s mode\n"),
keystr_from_pk (pk),
gnupg_compliance_option_string (opt.compliance));
if (compliant
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits, NULL))
compliant = 0;
}
}
if (compliant)
write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS
&& !compliant)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
rc = gpg_error (GPG_ERR_FORBIDDEN);
g10_errors_seen = 1;
goto leave;
}
cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo);
/* Only do the is-file-already-compressed check if we are using a
MDC. This forces compressed files to be re-compressed if we do
not have a MDC to give some protection against chosen ciphertext
attacks. */
if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename);
do_compress = 0;
}
if (rc2)
{
rc = rc2;
goto leave;
}
make_session_key (cfx.dek);
if (DBG_CRYPTO)
log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
if (rc)
goto leave;
/* We put the passphrase (if any) after any public keys as this
seems to be the most useful on the recipient side - there is no
point in prompting a user for a passphrase if they have the
secret key needed to decrypt. */
if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
goto leave;
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Get the size of the file if possible, i.e., if it is a real file. */
if (filename && *filename
&& !iobuf_is_pipe_filename (filename) && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size
of a file is larger than 2^32 minus some bytes for packet
headers, we switch to partial length encoding. */
if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode ? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
}
else
cfx.datalen = filesize && !do_compress ? filesize : 0;
/* Register the cipher filter. */
- iobuf_push_filter (out, cipher_filter, &cfx);
+ iobuf_push_filter (out, cipher_filter_cfb, &cfx);
/* Register the compress filter. */
if (do_compress)
{
int compr_algo = opt.compress_algo;
if (compr_algo == -1)
{
compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL);
if (compr_algo == -1)
compr_algo = DEFAULT_COMPRESS_ALGO;
/* Theoretically impossible to get here since uncompressed
is implicit. */
}
else if (!opt.expert
&& select_algo_from_prefs(pk_list, PREFTYPE_ZIP,
compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo), compr_algo);
}
/* Algo 0 means no compression. */
if (compr_algo)
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter (out,&zfx,compr_algo);
}
}
/* Do the work. */
if (!opt.no_literal)
{
if ((rc = build_packet( out, &pkt )))
log_error ("build_packet failed: %s\n", gpg_strerror (rc));
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1)
{
rc = iobuf_write (out, copy_buffer, bytes_copied);
if (rc)
{
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc));
break;
}
}
wipememory (copy_buffer, 4096); /* Burn the buffer. */
}
/* Finish the stuff. */
leave:
iobuf_close (inp);
if (rc)
iobuf_cancel (out);
else
{
iobuf_close (out); /* fixme: check returncode */
write_status (STATUS_END_ENCRYPTION);
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (cfx.dek);
xfree (symkey_dek);
xfree (symkey_s2k);
if (!provided_keys)
release_pk_list (pk_list);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
/*
* Filter to do a complete public key encryption.
*/
int
encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
encrypt_filter_context_t *efx = opaque;
int rc = 0;
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{
BUG(); /* not used */
}
else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */
{
if ( !efx->header_okay )
{
efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek );
if ( !opt.def_cipher_algo )
{
/* Try to get it from the prefs. */
efx->cfx.dek->algo =
select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL);
if (efx->cfx.dek->algo == -1 )
{
/* Because 3DES is implicitly in the prefs, this can
only happen if we do not have any public keys in
the list. */
efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO;
}
/* In case 3DES has been selected, print a warning if
any key does not have a preference for AES. This
should help to indentify why encrypting to several
recipients falls back to 3DES. */
if (opt.verbose
&& efx->cfx.dek->algo == CIPHER_ALGO_3DES)
warn_missing_aes_from_pklist (efx->pk_list);
}
else
{
if (!opt.expert
&& select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM,
opt.def_cipher_algo,
NULL) != opt.def_cipher_algo)
log_info(_("forcing symmetric cipher %s (%d) "
"violates recipient preferences\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
opt.def_cipher_algo);
efx->cfx.dek->algo = opt.def_cipher_algo;
}
efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo);
make_session_key ( efx->cfx.dek );
if (DBG_CRYPTO)
log_printhex (efx->cfx.dek->key, efx->cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (efx->ctrl,
efx->pk_list, efx->cfx.dek, a);
if (rc)
return rc;
if(efx->symkey_s2k && efx->symkey_dek)
{
rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
efx->cfx.dek,a);
if(rc)
return rc;
}
- iobuf_push_filter (a, cipher_filter, &efx->cfx);
+ iobuf_push_filter (a, cipher_filter_cfb, &efx->cfx);
efx->header_okay = 1;
}
rc = iobuf_write (a, buf, size);
}
else if (control == IOBUFCTRL_FREE)
{
xfree (efx->symkey_dek);
xfree (efx->symkey_s2k);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "encrypt_filter", *ret_len);
}
return rc;
}
/*
* Write a pubkey-enc packet for the public key PK to OUT.
*/
int
write_pubkey_enc (ctrl_t ctrl,
PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out)
{
PACKET pkt;
PKT_pubkey_enc *enc;
int rc;
gcry_mpi_t frame;
print_pubkey_algo_note ( pk->pubkey_algo );
enc = xmalloc_clear ( sizeof *enc );
enc->pubkey_algo = pk->pubkey_algo;
keyid_from_pk( pk, enc->keyid );
enc->throw_keyid = throw_keyid;
/* Okay, what's going on: We have the session key somewhere in
* the structure DEK and want to encode this session key in an
* integer value of n bits. pubkey_nbits gives us the number of
* bits we have to use. We then encode the session key in some
* way and we get it back in the big intger value FRAME. Then
* we use FRAME, the public key PK->PKEY and the algorithm
* number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which
* returns the encrypted value in the array ENC->DATA. This
* array has a size which depends on the used algorithm (e.g. 2
* for Elgamal). We don't need frame anymore because we have
* everything now in enc->data which is the passed to
* build_packet(). */
frame = encode_session_key (pk->pubkey_algo, dek,
pubkey_nbits (pk->pubkey_algo, pk->pkey));
rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey);
gcry_mpi_release (frame);
if (rc)
log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
else
{
if ( opt.verbose )
{
char *ustr = get_user_id_string_native (ctrl, enc->keyid);
log_info (_("%s/%s encrypted for: \"%s\"\n"),
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
ustr );
xfree (ustr);
}
/* And write it. */
init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet (out, &pkt);
if (rc)
log_error ("build_packet(pubkey_enc) failed: %s\n",
gpg_strerror (rc));
}
free_pubkey_enc(enc);
return rc;
}
/*
* Write pubkey-enc packets from the list of PKs to OUT.
*/
static int
write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
{
if (opt.throw_keyids && (PGP6 || PGP7 || PGP8))
{
log_info(_("option '%s' may not be used in %s mode\n"),
"--throw-keyids",
gnupg_compliance_option_string (opt.compliance));
compliance_failure();
}
for ( ; pk_list; pk_list = pk_list->next )
{
PKT_public_key *pk = pk_list->pk;
int throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
if (rc)
return rc;
}
return 0;
}
void
encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr)
{
int rc = 0;
if (opt.outfile)
{
log_error(_("--output doesn't work for this command\n"));
return;
}
if (!nfiles)
{
char line[2048];
unsigned int lno = 0;
while ( fgets(line, DIM(line), stdin) )
{
lno++;
if (!*line || line[strlen(line)-1] != '\n')
{
log_error("input line %u too long or missing LF\n", lno);
return;
}
line[strlen(line)-1] = '\0';
print_file_status(STATUS_FILE_START, line, 2);
rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1);
if (rc)
log_error ("encryption of '%s' failed: %s\n",
print_fname_stdin(line), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
}
}
else
{
while (nfiles--)
{
print_file_status(STATUS_FILE_START, *files, 2);
if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) )
log_error("encryption of '%s' failed: %s\n",
print_fname_stdin(*files), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
files++;
}
}
}
diff --git a/g10/filter.h b/g10/filter.h
index 2c1e513ce..d2f6c3f0f 100644
--- a/g10/filter.h
+++ b/g10/filter.h
@@ -1,164 +1,164 @@
/* filter.h
* Copyright (C) 1998, 1999, 2000, 2001, 2003,
* 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#ifndef G10_FILTER_H
#define G10_FILTER_H
#include "../common/types.h"
#include "dek.h"
typedef struct {
gcry_md_hd_t md; /* catch all */
gcry_md_hd_t md2; /* if we want to calculate an alternate hash */
size_t maxbuf_size;
} md_filter_context_t;
typedef struct {
int refcount; /* Initialized to 1. */
/* these fields may be initialized */
int what; /* what kind of armor headers to write */
int only_keyblocks; /* skip all headers but ".... key block" */
const char *hdrlines; /* write these headerlines */
/* these fields must be initialized to zero */
int no_openpgp_data; /* output flag: "No valid OpenPGP data found" */
/* the following fields must be initialized to zero */
int inp_checked; /* set if the input has been checked */
int inp_bypass; /* set if the input is not armored */
int in_cleartext; /* clear text message */
int not_dash_escaped; /* clear text is not dash escaped */
int hashes; /* detected hash algorithms */
int faked; /* we are faking a literal data packet */
int truncated; /* number of truncated lines */
int qp_detected;
byte eol[3]; /* The end of line characters as a
zero-terminated string. Defaults
(eol[0]=='\0') to whatever the local
platform uses. */
byte *buffer; /* malloced buffer */
unsigned buffer_size; /* and size of this buffer */
unsigned buffer_len; /* used length of the buffer */
unsigned buffer_pos; /* read position */
byte radbuf[4];
int idx, idx2;
u32 crc;
int status; /* an internal state flag */
int cancel;
int any_data; /* any valid armored data seen */
int pending_lf; /* used together with faked */
} armor_filter_context_t;
struct unarmor_pump_s;
typedef struct unarmor_pump_s *UnarmorPump;
struct compress_filter_context_s {
int status;
void *opaque; /* (used for z_stream) */
byte *inbuf;
unsigned inbufsize;
byte *outbuf;
unsigned outbufsize;
int algo; /* compress algo */
int algo1hack;
int new_ctb;
void (*release)(struct compress_filter_context_s*);
};
typedef struct compress_filter_context_s compress_filter_context_t;
typedef struct {
DEK *dek;
u32 datalen;
gcry_cipher_hd_t cipher_hd;
unsigned int wrote_header : 1;
unsigned int short_blklen_warn : 1;
unsigned long short_blklen_count;
gcry_md_hd_t mdc_hash;
byte enchash[20];
} cipher_filter_context_t;
typedef struct {
byte *buffer; /* malloced buffer */
unsigned buffer_size; /* and size of this buffer */
unsigned buffer_len; /* used length of the buffer */
unsigned buffer_pos; /* read position */
int truncated; /* number of truncated lines */
int not_dash_escaped;
int escape_from;
gcry_md_hd_t md;
int pending_lf;
int pending_esc;
} text_filter_context_t;
typedef struct {
char *what; /* description */
u32 last_time; /* last time reported */
unsigned long last; /* last amount reported */
unsigned long offset; /* current amount */
unsigned long total; /* total amount */
int refcount;
} progress_filter_context_t;
/* encrypt_filter_context_t defined in main.h */
/*-- mdfilter.c --*/
int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len);
void free_md_filter_context( md_filter_context_t *mfx );
/*-- armor.c --*/
armor_filter_context_t *new_armor_context (void);
void release_armor_context (armor_filter_context_t *afx);
int push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf);
int use_armor_filter( iobuf_t a );
UnarmorPump unarmor_pump_new (void);
void unarmor_pump_release (UnarmorPump x);
int unarmor_pump (UnarmorPump x, int c);
/*-- compress.c --*/
gpg_error_t push_compress_filter (iobuf_t out, compress_filter_context_t *zfx,
int algo);
gpg_error_t push_compress_filter2 (iobuf_t out,compress_filter_context_t *zfx,
int algo, int rel);
/*-- cipher.c --*/
-int cipher_filter( void *opaque, int control,
- iobuf_t chain, byte *buf, size_t *ret_len);
+int cipher_filter_cfb (void *opaque, int control,
+ iobuf_t chain, byte *buf, size_t *ret_len);
/*-- textfilter.c --*/
int text_filter( void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
int copy_clearsig_text (iobuf_t out, iobuf_t inp, gcry_md_hd_t md,
int escape_dash, int escape_from);
/*-- progress.c --*/
progress_filter_context_t *new_progress_context (void);
void release_progress_context (progress_filter_context_t *pfx);
void handle_progress (progress_filter_context_t *pfx,
iobuf_t inp, const char *name);
#endif /*G10_FILTER_H*/
diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c
index 229a4e5ea..d82995d1d 100644
--- a/g10/gpgcompose.c
+++ b/g10/gpgcompose.c
@@ -1,3089 +1,3089 @@
/* gpgcompose.c - Maintainer tool to create OpenPGP messages by hand.
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <errno.h>
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include "packet.h"
#include "keydb.h"
#include "main.h"
#include "options.h"
static int do_debug;
#define debug(fmt, ...) \
do { if (do_debug) log_debug (fmt, ##__VA_ARGS__); } while (0)
/* --encryption, for instance, adds a filter in front of out. There
is an operator (--encryption-pop) to end this. We use the
following infrastructure to make it easy to pop the state. */
struct filter
{
void *func;
void *context;
int pkttype;
int partial_block_mode;
struct filter *next;
};
/* Hack to ass CTRL to some functions. */
static ctrl_t global_ctrl;
static struct filter *filters;
static void
filter_push (iobuf_t out, void *func, void *context,
int type, int partial_block_mode)
{
gpg_error_t err;
struct filter *f = xmalloc_clear (sizeof (*f));
f->next = filters;
f->func = func;
f->context = context;
f->pkttype = type;
f->partial_block_mode = partial_block_mode;
filters = f;
err = iobuf_push_filter (out, func, context);
if (err)
log_fatal ("Adding filter: %s\n", gpg_strerror (err));
}
static void
filter_pop (iobuf_t out, int expected_type)
{
gpg_error_t err;
struct filter *f = filters;
log_assert (f);
if (f->pkttype != expected_type)
log_fatal ("Attempted to pop a %s container, "
"but current container is a %s container.\n",
pkttype_str (f->pkttype), pkttype_str (expected_type));
if (f->pkttype == PKT_ENCRYPTED)
{
err = iobuf_pop_filter (out, f->func, f->context);
if (err)
log_fatal ("Popping encryption filter: %s\n", gpg_strerror (err));
}
else
log_fatal ("FILTERS appears to be corrupted.\n");
if (f->partial_block_mode)
iobuf_set_partial_body_length_mode (out, 0);
filters = f->next;
xfree (f);
}
/* Return if CIPHER_ID is a valid cipher. */
static int
valid_cipher (int cipher_id)
{
return (cipher_id == CIPHER_ALGO_IDEA
|| cipher_id == CIPHER_ALGO_3DES
|| cipher_id == CIPHER_ALGO_CAST5
|| cipher_id == CIPHER_ALGO_BLOWFISH
|| cipher_id == CIPHER_ALGO_AES
|| cipher_id == CIPHER_ALGO_AES192
|| cipher_id == CIPHER_ALGO_AES256
|| cipher_id == CIPHER_ALGO_TWOFISH
|| cipher_id == CIPHER_ALGO_CAMELLIA128
|| cipher_id == CIPHER_ALGO_CAMELLIA192
|| cipher_id == CIPHER_ALGO_CAMELLIA256);
}
/* Parse a session key encoded as a string of the form x:HEXDIGITS
where x is the algorithm id. (This is the format emitted by gpg
--show-session-key.) */
struct session_key
{
int algo;
int keylen;
char *key;
};
static struct session_key
parse_session_key (const char *option, char *p, int require_algo)
{
char *tail;
struct session_key sk;
memset (&sk, 0, sizeof (sk));
/* Check for the optional "cipher-id:" at the start of the
string. */
errno = 0;
sk.algo = strtol (p, &tail, 10);
if (! errno && tail && *tail == ':')
{
if (! valid_cipher (sk.algo))
log_info ("%s: %d is not a known cipher (but using anyways)\n",
option, sk.algo);
p = tail + 1;
}
else if (require_algo)
log_fatal ("%s: Session key must have the form algo:HEXCHARACTERS.\n",
option);
else
sk.algo = 0;
/* Ignore a leading 0x. */
if (p[0] == '0' && p[1] == 'x')
p += 2;
if (strlen (p) % 2 != 0)
log_fatal ("%s: session key must consist of an even number of hexadecimal characters.\n",
option);
sk.keylen = strlen (p) / 2;
sk.key = xmalloc (sk.keylen);
if (hex2bin (p, sk.key, sk.keylen) == -1)
log_fatal ("%s: Session key must only contain hexadecimal characters\n",
option);
return sk;
}
/* A callback.
OPTION_STR is the option that was matched. ARGC is the number of
arguments following the option and ARGV are those arguments.
(Thus, argv[0] is the first string following the option and
argv[-1] is the option.)
COOKIE is the opaque value passed to process_options. */
typedef int (*option_prcessor_t) (const char *option_str,
int argc, char *argv[],
void *cookie);
struct option
{
/* The option that this matches. This must start with "--" or be
the empty string. The empty string matches bare arguments. */
const char *option;
/* The function to call to process this option. */
option_prcessor_t func;
/* Documentation. */
const char *help;
};
/* Merge two lists of options. Note: this makes a shallow copy! The
caller must xfree() the result. */
static struct option *
merge_options (struct option a[], struct option b[])
{
int i, j;
struct option *c;
for (i = 0; a[i].option; i ++)
;
for (j = 0; b[j].option; j ++)
;
c = xmalloc ((i + j + 1) * sizeof (struct option));
memcpy (c, a, i * sizeof (struct option));
memcpy (&c[i], b, j * sizeof (struct option));
c[i + j].option = NULL;
if (a[i].help && b[j].help)
c[i + j].help = xasprintf ("%s\n\n%s", a[i].help, b[j].help);
else if (a[i].help)
c[i + j].help = a[i].help;
else if (b[j].help)
c[i + j].help = b[j].help;
return c;
}
/* Returns whether ARG is an option. All options start with --. */
static int
is_option (const char *arg)
{
return arg[0] == '-' && arg[1] == '-';
}
/* OPTIONS is a NULL terminated array of struct option:s. Finds the
entry that is the same as ARG. Returns -1 if no entry is found.
The empty string option matches bare arguments. */
static int
match_option (const struct option options[], const char *arg)
{
int i;
int bare_arg = ! is_option (arg);
for (i = 0; options[i].option; i ++)
if ((! bare_arg && strcmp (options[i].option, arg) == 0)
/* Non-options match the empty string. */
|| (bare_arg && options[i].option[0] == '\0'))
return i;
return -1;
}
static void
show_help (struct option options[])
{
int i;
int max_length = 0;
int space;
for (i = 0; options[i].option; i ++)
{
const char *option = options[i].option[0] ? options[i].option : "ARG";
int l = strlen (option);
if (l > max_length)
max_length = l;
}
space = 72 - (max_length + 2);
if (space < 40)
space = 40;
for (i = 0; ; i ++)
{
const char *option = options[i].option;
const char *help = options[i].help;
int l;
int j;
char *tmp;
char *formatted;
char *p;
char *newline;
if (! option && ! help)
break;
if (option)
{
const char *o = option[0] ? option : "ARG";
l = strlen (o);
fprintf (stdout, "%s", o);
}
if (! help)
{
fputc ('\n', stdout);
continue;
}
if (option)
for (j = l; j < max_length + 2; j ++)
fputc (' ', stdout);
#define BOLD_START "\033[1m"
#define NORMAL_RESTORE "\033[0m"
#define BOLD(x) BOLD_START x NORMAL_RESTORE
if (! option || options[i].func)
tmp = (char *) help;
else
tmp = xasprintf ("%s " BOLD("(Unimplemented.)"), help);
if (! option)
space = 72;
formatted = format_text (tmp, space, space + 4);
if (!formatted)
abort ();
if (tmp != help)
xfree (tmp);
if (! option)
{
printf ("\n%s\n", formatted);
break;
}
for (p = formatted;
p && *p;
p = (*newline == '\0') ? newline : newline + 1)
{
newline = strchr (p, '\n');
if (! newline)
newline = &p[strlen (p)];
l = (size_t) newline - (size_t) p;
if (p != formatted)
for (j = 0; j < max_length + 2; j ++)
fputc (' ', stdout);
fwrite (p, l, 1, stdout);
fputc ('\n', stdout);
}
xfree (formatted);
}
}
/* Return value is number of consumed argv elements. */
static int
process_options (const char *parent_option,
struct option break_options[],
struct option local_options[], void *lcookie,
struct option global_options[], void *gcookie,
int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i ++)
{
int j;
struct option *option;
void *cookie;
int bare_arg;
option_prcessor_t func;
int consumed;
if (break_options)
{
j = match_option (break_options, argv[i]);
if (j != -1)
/* Match. Break out. */
return i;
}
j = match_option (local_options, argv[i]);
if (j == -1)
{
if (global_options)
j = match_option (global_options, argv[i]);
if (j == -1)
{
if (strcmp (argv[i], "--help") == 0)
{
if (! global_options)
show_help (local_options);
else
{
struct option *combined
= merge_options (local_options, global_options);
show_help (combined);
xfree (combined);
}
g10_exit (0);
}
if (parent_option)
log_fatal ("%s: Unknown option: %s\n", parent_option, argv[i]);
else
log_fatal ("Unknown option: %s\n", argv[i]);
}
option = &global_options[j];
cookie = gcookie;
}
else
{
option = &local_options[j];
cookie = lcookie;
}
bare_arg = strcmp (option->option, "") == 0;
func = option->func;
if (! func)
{
if (bare_arg)
log_fatal ("Bare arguments unimplemented.\n");
else
log_fatal ("Unimplemented option: %s\n",
option->option);
}
consumed = func (bare_arg ? parent_option : argv[i],
argc - i - !bare_arg, &argv[i + !bare_arg],
cookie);
i += consumed;
if (bare_arg)
i --;
}
return i;
}
/* The keys, subkeys, user ids and user attributes in the order that
they were added. */
PACKET components[20];
/* The number of components. */
int ncomponents;
static int
add_component (int pkttype, void *component)
{
int i = ncomponents ++;
log_assert (i < sizeof (components) / sizeof (components[0]));
log_assert (pkttype == PKT_PUBLIC_KEY
|| pkttype == PKT_PUBLIC_SUBKEY
|| pkttype == PKT_SECRET_KEY
|| pkttype == PKT_SECRET_SUBKEY
|| pkttype == PKT_USER_ID
|| pkttype == PKT_ATTRIBUTE);
components[i].pkttype = pkttype;
components[i].pkt.generic = component;
return i;
}
static void
dump_component (PACKET *pkt)
{
struct kbnode_struct kbnode;
if (! do_debug)
return;
memset (&kbnode, 0, sizeof (kbnode));
kbnode.pkt = pkt;
dump_kbnode (&kbnode);
}
/* Returns the first primary key in COMPONENTS or NULL if there is
none. */
static PKT_public_key *
primary_key (void)
{
int i;
for (i = 0; i < ncomponents; i ++)
if (components[i].pkttype == PKT_PUBLIC_KEY)
return components[i].pkt.public_key;
return NULL;
}
/* The last session key (updated when adding a SK-ESK, PK-ESK or SED
packet. */
static DEK session_key;
static int user_id (const char *option, int argc, char *argv[],
void *cookie);
static int public_key (const char *option, int argc, char *argv[],
void *cookie);
static int sk_esk (const char *option, int argc, char *argv[],
void *cookie);
static int pk_esk (const char *option, int argc, char *argv[],
void *cookie);
static int encrypted (const char *option, int argc, char *argv[],
void *cookie);
static int encrypted_pop (const char *option, int argc, char *argv[],
void *cookie);
static int literal (const char *option, int argc, char *argv[],
void *cookie);
static int signature (const char *option, int argc, char *argv[],
void *cookie);
static int copy (const char *option, int argc, char *argv[],
void *cookie);
static struct option major_options[] = {
{ "--user-id", user_id, "Create a user id packet." },
{ "--public-key", public_key, "Create a public key packet." },
{ "--private-key", NULL, "Create a private key packet." },
{ "--public-subkey", public_key, "Create a subkey packet." },
{ "--private-subkey", NULL, "Create a private subkey packet." },
{ "--sk-esk", sk_esk,
"Create a symmetric-key encrypted session key packet." },
{ "--pk-esk", pk_esk,
"Create a public-key encrypted session key packet." },
{ "--encrypted", encrypted, "Create a symmetrically encrypted data packet." },
{ "--encrypted-mdc", encrypted,
"Create a symmetrically encrypted and integrity protected data packet." },
{ "--encrypted-pop", encrypted_pop,
"Pop the most recent encryption container started by either"
" --encrypted or --encrypted-mdc." },
{ "--compressed", NULL, "Create a compressed data packet." },
{ "--literal", literal, "Create a literal (plaintext) data packet." },
{ "--signature", signature, "Create a signature packet." },
{ "--onepass-sig", NULL, "Create a one-pass signature packet." },
{ "--copy", copy, "Copy the specified file." },
{ NULL, NULL,
"To get more information about a given command, use:\n\n"
" $ gpgcompose --command --help to list a command's options."},
};
static struct option global_options[] = {
{ NULL, NULL, NULL },
};
/* Make our lives easier and use a static limit for the user name.
10k is way more than enough anyways... */
const int user_id_max_len = 10 * 1024;
static int
user_id_name (const char *option, int argc, char *argv[], void *cookie)
{
PKT_user_id *uid = cookie;
int l;
if (argc == 0)
log_fatal ("Usage: %s USER_ID\n", option);
if (uid->len)
log_fatal ("Attempt to set user id multiple times.\n");
l = strlen (argv[0]);
if (l > user_id_max_len)
log_fatal ("user id too long (max: %d)\n", user_id_max_len);
memcpy (uid->name, argv[0], l);
uid->name[l] = 0;
uid->len = l;
return 1;
}
static struct option user_id_options[] = {
{ "", user_id_name,
"Set the user id. This is usually in the format "
"\"Name (comment) <email@example.org>\"" },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --user-id \"USERID\" | " GPG_NAME " --list-packets" }
};
static int
user_id (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
gpg_error_t err;
PKT_user_id *uid = xmalloc_clear (sizeof (*uid) + user_id_max_len);
int c = add_component (PKT_USER_ID, uid);
int processed;
processed = process_options (option,
major_options,
user_id_options, uid,
global_options, NULL,
argc, argv);
if (! uid->len)
log_fatal ("%s: user id not given", option);
err = build_packet (out, &components[c]);
if (err)
log_fatal ("Serializing user id packet: %s\n", gpg_strerror (err));
debug ("Wrote user id packet:\n");
dump_component (&components[c]);
return processed;
}
static int
pk_search_terms (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
KEYDB_HANDLE hd;
KEYDB_SEARCH_DESC desc;
kbnode_t kb;
PKT_public_key *pk = cookie;
PKT_public_key *pk_ref;
int i;
if (argc == 0)
log_fatal ("Usage: %s KEYID\n", option);
if (pk->pubkey_algo)
log_fatal ("%s: multiple keys provided\n", option);
err = classify_user_id (argv[0], &desc, 0);
if (err)
log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err));
hd = keydb_new ();
err = keydb_search (hd, &desc, 1, NULL);
if (err)
log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err));
err = keydb_get_keyblock (hd, &kb);
if (err)
log_fatal ("retrieving keyblock for '%s': %s\n",
argv[0], gpg_strerror (err));
keydb_release (hd);
pk_ref = kb->pkt->pkt.public_key;
/* Copy the timestamp (if not already set), algo and public key
parameters. */
if (! pk->timestamp)
pk->timestamp = pk_ref->timestamp;
pk->pubkey_algo = pk_ref->pubkey_algo;
for (i = 0; i < pubkey_get_npkey (pk->pubkey_algo); i ++)
pk->pkey[i] = gcry_mpi_copy (pk_ref->pkey[i]);
release_kbnode (kb);
return 1;
}
static int
pk_timestamp (const char *option, int argc, char *argv[], void *cookie)
{
PKT_public_key *pk = cookie;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s TIMESTAMP\n", option);
errno = 0;
pk->timestamp = parse_timestamp (argv[0], &tail);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
return 1;
}
#define TIMESTAMP_HELP \
"Either as seconds since the epoch or as an ISO 8601 formatted " \
"string (yyyymmddThhmmss, where the T is a literal)."
static struct option pk_options[] = {
{ "--timestamp", pk_timestamp,
"The creation time. " TIMESTAMP_HELP },
{ "", pk_search_terms,
"The key to copy the creation time and public key parameters from." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --public-key $KEYID --user-id \"USERID\" \\\n"
" | " GPG_NAME " --list-packets" }
};
static int
public_key (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
iobuf_t out = cookie;
PKT_public_key *pk;
int c;
int processed;
int t = (strcmp (option, "--public-key") == 0
? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY);
(void) option;
pk = xmalloc_clear (sizeof (*pk));
pk->version = 4;
c = add_component (t, pk);
processed = process_options (option,
major_options,
pk_options, pk,
global_options, NULL,
argc, argv);
if (! pk->pubkey_algo)
log_fatal ("%s: key to extract public key parameters from not given",
option);
/* Clear the keyid in case we updated one of the relevant fields
after accessing it. */
pk->keyid[0] = pk->keyid[1] = 0;
err = build_packet (out, &components[c]);
if (err)
log_fatal ("serializing %s packet: %s\n",
t == PKT_PUBLIC_KEY ? "public key" : "subkey",
gpg_strerror (err));
debug ("Wrote %s packet:\n",
t == PKT_PUBLIC_KEY ? "public key" : "subkey");
dump_component (&components[c]);
return processed;
}
struct signinfo
{
/* Key with which to sign. */
kbnode_t issuer_kb;
PKT_public_key *issuer_pk;
/* Overrides the issuer's key id. */
u32 issuer_keyid[2];
/* Sets the issuer's keyid to the primary key's key id. */
int issuer_keyid_self;
/* Key to sign. */
PKT_public_key *pk;
/* Subkey to sign. */
PKT_public_key *sk;
/* User id to sign. */
PKT_user_id *uid;
int class;
int digest_algo;
u32 timestamp;
u32 key_expiration;
byte *cipher_algorithms;
int cipher_algorithms_len;
byte *digest_algorithms;
int digest_algorithms_len;
byte *compress_algorithms;
int compress_algorithms_len;
u32 expiration;
int exportable_set;
int exportable;
int revocable_set;
int revocable;
int trust_level_set;
byte trust_args[2];
char *trust_scope;
struct revocation_key *revocation_key;
int nrevocation_keys;
struct notation *notations;
byte *key_server_preferences;
int key_server_preferences_len;
char *key_server;
int primary_user_id_set;
int primary_user_id;
char *policy_uri;
byte *key_flags;
int key_flags_len;
char *signers_user_id;
byte reason_for_revocation_code;
char *reason_for_revocation;
byte *features;
int features_len;
/* Whether to corrupt the signature. */
int corrupt;
};
static int
sig_issuer (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
KEYDB_HANDLE hd;
KEYDB_SEARCH_DESC desc;
struct signinfo *si = cookie;
if (argc == 0)
log_fatal ("Usage: %s KEYID\n", option);
if (si->issuer_pk)
log_fatal ("%s: multiple keys provided\n", option);
err = classify_user_id (argv[0], &desc, 0);
if (err)
log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err));
hd = keydb_new ();
err = keydb_search (hd, &desc, 1, NULL);
if (err)
log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err));
err = keydb_get_keyblock (hd, &si->issuer_kb);
if (err)
log_fatal ("retrieving keyblock for '%s': %s\n",
argv[0], gpg_strerror (err));
keydb_release (hd);
si->issuer_pk = si->issuer_kb->pkt->pkt.public_key;
return 1;
}
static int
sig_issuer_keyid (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
KEYDB_SEARCH_DESC desc;
struct signinfo *si = cookie;
if (argc == 0)
log_fatal ("Usage: %s KEYID|self\n", option);
if (si->issuer_keyid[0] || si->issuer_keyid[1] || si->issuer_keyid_self)
log_fatal ("%s given multiple times.\n", option);
if (strcasecmp (argv[0], "self") == 0)
{
si->issuer_keyid_self = 1;
return 1;
}
err = classify_user_id (argv[0], &desc, 0);
if (err)
log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err));
if (desc.mode != KEYDB_SEARCH_MODE_LONG_KID)
log_fatal ("%s is not a valid long key id.\n", argv[0]);
keyid_copy (si->issuer_keyid, desc.u.kid);
return 1;
}
static int
sig_pk (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int i;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s COMPONENT_INDEX\n", option);
errno = 0;
i = strtoul (argv[0], &tail, 10);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
if (i >= ncomponents)
log_fatal ("%d: No such component (have %d components so far)\n",
i, ncomponents);
if (! (components[i].pkttype == PKT_PUBLIC_KEY
|| components[i].pkttype == PKT_PUBLIC_SUBKEY))
log_fatal ("Component %d is not a public key or a subkey.", i);
if (strcmp (option, "--pk") == 0)
{
if (si->pk)
log_fatal ("%s already given.\n", option);
si->pk = components[i].pkt.public_key;
}
else if (strcmp (option, "--sk") == 0)
{
if (si->sk)
log_fatal ("%s already given.\n", option);
si->sk = components[i].pkt.public_key;
}
else
log_fatal ("Cannot handle %s\n", option);
return 1;
}
static int
sig_user_id (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int i;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s COMPONENT_INDEX\n", option);
if (si->uid)
log_fatal ("%s already given.\n", option);
errno = 0;
i = strtoul (argv[0], &tail, 10);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
if (i >= ncomponents)
log_fatal ("%d: No such component (have %d components so far)\n",
i, ncomponents);
if (! (components[i].pkttype != PKT_USER_ID
|| components[i].pkttype == PKT_ATTRIBUTE))
log_fatal ("Component %d is not a public key or a subkey.", i);
si->uid = components[i].pkt.user_id;
return 1;
}
static int
sig_class (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int i;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s CLASS\n", option);
errno = 0;
i = strtoul (argv[0], &tail, 0);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
si->class = i;
return 1;
}
static int
sig_digest (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int i;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s DIGEST_ALGO\n", option);
errno = 0;
i = strtoul (argv[0], &tail, 10);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
si->digest_algo = i;
return 1;
}
static int
sig_timestamp (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s TIMESTAMP\n", option);
errno = 0;
si->timestamp = parse_timestamp (argv[0], &tail);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
return 1;
}
static int
sig_expiration (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int is_expiration = strcmp (option, "--expiration") == 0;
u32 *i = is_expiration ? &si->expiration : &si->key_expiration;
if (! is_expiration)
log_assert (strcmp (option, "--key-expiration") == 0);
if (argc == 0)
log_fatal ("Usage: %s DURATION\n", option);
*i = parse_expire_string (argv[0]);
if (*i == (u32)-1)
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
return 1;
}
static int
sig_int_list (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int nvalues = 1;
char *values = xmalloc (nvalues * sizeof (values[0]));
char *tail = argv[0];
int i;
byte **a;
int *n;
if (argc == 0)
log_fatal ("Usage: %s VALUE[,VALUE...]\n", option);
for (i = 0; tail && *tail; i ++)
{
int v;
char *old_tail = tail;
errno = 0;
v = strtol (tail, &tail, 0);
if (errno || old_tail == tail || (tail && !(*tail == ',' || *tail == 0)))
log_fatal ("Invalid value passed to %s (%s). "
"Expected a list of comma separated numbers\n",
option, argv[0]);
if (! (0 <= v && v <= 255))
log_fatal ("%s: %d is out of range (Expected: 0-255)\n", option, v);
if (i == nvalues)
{
nvalues *= 2;
values = xrealloc (values, nvalues * sizeof (values[0]));
}
values[i] = v;
if (*tail == ',')
tail ++;
else
log_assert (*tail == 0);
}
if (strcmp ("--cipher-algos", option) == 0)
{
a = &si->cipher_algorithms;
n = &si->cipher_algorithms_len;
}
else if (strcmp ("--digest-algos", option) == 0)
{
a = &si->digest_algorithms;
n = &si->digest_algorithms_len;
}
else if (strcmp ("--compress-algos", option) == 0)
{
a = &si->compress_algorithms;
n = &si->compress_algorithms_len;
}
else
log_fatal ("Cannot handle %s\n", option);
if (*a)
log_fatal ("Option %s given multiple times.\n", option);
*a = values;
*n = i;
return 1;
}
static int
sig_flag (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int range[2] = {0, 255};
char *tail;
int v;
if (strcmp (option, "--primary-user-id") == 0)
range[1] = 1;
if (argc <= 1)
{
if (range[0] == 0 && range[1] == 1)
log_fatal ("Usage: %s 0|1\n", option);
else
log_fatal ("Usage: %s %d-%d\n", option, range[0], range[1]);
}
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1]))
log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n",
option, argv[0], range[0], range[1]);
if (strcmp (option, "--exportable") == 0)
{
si->exportable_set = 1;
si->exportable = v;
}
else if (strcmp (option, "--revocable") == 0)
{
si->revocable_set = 1;
si->revocable = v;
}
else if (strcmp (option, "--primary-user-id") == 0)
{
si->primary_user_id_set = 1;
si->primary_user_id = v;
}
else
log_fatal ("Cannot handle %s\n", option);
return 1;
}
static int
sig_trust_level (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int i;
char *tail;
if (argc <= 1)
log_fatal ("Usage: %s DEPTH TRUST_AMOUNT\n", option);
for (i = 0; i < sizeof (si->trust_args) / sizeof (si->trust_args[0]); i ++)
{
int v;
errno = 0;
v = strtol (argv[i], &tail, 0);
if (errno || (tail && *tail) || !(0 <= v && v <= 255))
log_fatal ("Invalid value passed to %s (%s). Expected 0-255\n",
option, argv[i]);
si->trust_args[i] = v;
}
si->trust_level_set = 1;
return 2;
}
static int
sig_string_arg (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
char *p = argv[0];
char **s;
if (argc == 0)
log_fatal ("Usage: %s STRING\n", option);
if (strcmp (option, "--trust-scope") == 0)
s = &si->trust_scope;
else if (strcmp (option, "--key-server") == 0)
s = &si->key_server;
else if (strcmp (option, "--signers-user-id") == 0)
s = &si->signers_user_id;
else if (strcmp (option, "--policy-uri") == 0)
s = &si->policy_uri;
else
log_fatal ("Cannot handle %s\n", option);
if (*s)
log_fatal ("%s already given.\n", option);
*s = xstrdup (p);
return 1;
}
static int
sig_revocation_key (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
struct signinfo *si = cookie;
int v;
char *tail;
PKT_public_key pk;
struct revocation_key *revkey;
if (argc < 2)
log_fatal ("Usage: %s CLASS KEYID\n", option);
memset (&pk, 0, sizeof (pk));
errno = 0;
v = strtol (argv[0], &tail, 16);
if (errno || (tail && *tail) || !(0 <= v && v <= 255))
log_fatal ("%s: Invalid class value (%s). Expected 0-255\n",
option, argv[0]);
pk.req_usage = PUBKEY_USAGE_SIG;
err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL,
NULL, &pk, argv[1], NULL, NULL, 1);
if (err)
log_fatal ("looking up key %s: %s\n", argv[1], gpg_strerror (err));
si->nrevocation_keys ++;
si->revocation_key = xrealloc (si->revocation_key,
si->nrevocation_keys
* sizeof (*si->revocation_key));
revkey = &si->revocation_key[si->nrevocation_keys - 1];
revkey->class = v;
revkey->algid = pk.pubkey_algo;
fingerprint_from_pk (&pk, revkey->fpr, NULL);
release_public_key_parts (&pk);
return 2;
}
static int
sig_notation (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int is_blob = strcmp (option, "--notation") != 0;
struct notation *notation;
char *p = argv[0];
int p_free = 0;
char *data;
int data_size;
int data_len;
if (argc == 0)
log_fatal ("Usage: %s [!<]name=value\n", option);
if ((p[0] == '!' && p[1] == '<') || p[0] == '<')
/* Read from a file. */
{
char *filename = NULL;
iobuf_t in;
int prefix;
if (p[0] == '<')
p ++;
else
{
/* Remove the '<', which string_to_notation does not
understand, and preserve the '!'. */
p = xstrdup (&p[1]);
p_free = 1;
p[0] = '!';
}
filename = strchr (p, '=');
if (! filename)
log_fatal ("No value specified. Usage: %s [!<]name=value\n",
option);
filename ++;
prefix = (size_t) filename - (size_t) p;
errno = 0;
in = iobuf_open (filename);
if (! in)
log_fatal ("Opening '%s': %s\n",
filename, errno ? strerror (errno): "unknown error");
/* A notation can be at most about a few dozen bytes short of
64k. Since this is relatively small, we just allocate that
much instead of trying to dynamically size a buffer. */
data_size = 64 * 1024;
data = xmalloc (data_size);
log_assert (prefix <= data_size);
memcpy (data, p, prefix);
data_len = iobuf_read (in, &data[prefix], data_size - prefix - 1);
if (data_len == -1)
/* EOF => 0 bytes read. */
data_len = 0;
if (data_len == data_size - prefix - 1)
/* Technically, we should do another read and check for EOF,
but what's one byte more or less? */
log_fatal ("Notation data doesn't fit in the packet.\n");
iobuf_close (in);
/* NUL terminate it. */
data[prefix + data_len] = 0;
if (p_free)
xfree (p);
p = data;
p_free = 1;
data = &p[prefix];
if (is_blob)
p[prefix - 1] = 0;
}
else if (is_blob)
{
data = strchr (p, '=');
if (! data)
{
data = p;
data_len = 0;
}
else
{
p = xstrdup (p);
p_free = 1;
data = strchr (p, '=');
log_assert (data);
/* NUL terminate the name. */
*data = 0;
data ++;
data_len = strlen (data);
}
}
if (is_blob)
notation = blob_to_notation (p, data, data_len);
else
notation = string_to_notation (p, 1);
if (! notation)
log_fatal ("creating notation: an unknown error occurred.\n");
notation->next = si->notations;
si->notations = notation;
if (p_free)
xfree (p);
return 1;
}
static int
sig_big_endian_arg (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
char *p = argv[0];
int i;
int l;
char *bytes;
if (argc == 0)
log_fatal ("Usage: %s HEXDIGITS\n", option);
/* Skip a leading "0x". */
if (p[0] == '0' && p[1] == 'x')
p += 2;
for (i = 0; i < strlen (p); i ++)
if (!hexdigitp (&p[i]))
log_fatal ("%s: argument ('%s') must consist of hex digits.\n",
option, p);
if (strlen (p) % 2 != 0)
log_fatal ("%s: argument ('%s') must contain an even number of hex digits.\n",
option, p);
l = strlen (p) / 2;
bytes = xmalloc (l);
hex2bin (p, bytes, l);
if (strcmp (option, "--key-server-preferences") == 0)
{
if (si->key_server_preferences)
log_fatal ("%s given multiple times.\n", option);
si->key_server_preferences = bytes;
si->key_server_preferences_len = l;
}
else if (strcmp (option, "--key-flags") == 0)
{
if (si->key_flags)
log_fatal ("%s given multiple times.\n", option);
si->key_flags = bytes;
si->key_flags_len = l;
}
else if (strcmp (option, "--features") == 0)
{
if (si->features)
log_fatal ("%s given multiple times.\n", option);
si->features = bytes;
si->features_len = l;
}
else
log_fatal ("Cannot handle %s\n", option);
return 1;
}
static int
sig_reason_for_revocation (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
int v;
char *tail;
if (argc < 2)
log_fatal ("Usage: %s REASON_CODE REASON_STRING\n", option);
errno = 0;
v = strtol (argv[0], &tail, 16);
if (errno || (tail && *tail) || !(0 <= v && v <= 255))
log_fatal ("%s: Invalid reason code (%s). Expected 0-255\n",
option, argv[0]);
if (si->reason_for_revocation)
log_fatal ("%s given multiple times.\n", option);
si->reason_for_revocation_code = v;
si->reason_for_revocation = xstrdup (argv[1]);
return 2;
}
static int
sig_corrupt (const char *option, int argc, char *argv[], void *cookie)
{
struct signinfo *si = cookie;
(void) option;
(void) argc;
(void) argv;
(void) cookie;
si->corrupt = 1;
return 0;
}
static struct option sig_options[] = {
{ "--issuer", sig_issuer,
"The key to use to generate the signature."},
{ "--issuer-keyid", sig_issuer_keyid,
"Set the issuer's key id. This is useful for creating a "
"self-signature. As a special case, the value \"self\" refers "
"to the primary key's key id. "
"(RFC 4880, Section 5.2.3.5)" },
{ "--pk", sig_pk,
"The primary keyas an index into the components (keys and uids) "
"created so far where the first component has the index 0." },
{ "--sk", sig_pk,
"The subkey as an index into the components (keys and uids) created "
"so far where the first component has the index 0. Only needed for "
"0x18, 0x19, and 0x28 signatures." },
{ "--user-id", sig_user_id,
"The user id as an index into the components (keys and uids) created "
"so far where the first component has the index 0. Only needed for "
"0x10-0x13 and 0x30 signatures." },
{ "--class", sig_class,
"The signature's class. Valid values are "
"0x10-0x13 (user id and primary-key certification), "
"0x18 (subkey binding), "
"0x19 (primary key binding), "
"0x1f (direct primary key signature), "
"0x20 (key revocation), "
"0x28 (subkey revocation), and "
"0x30 (certification revocation)."
},
{ "--digest", sig_digest, "The digest algorithm" },
{ "--timestamp", sig_timestamp,
"The signature's creation time. " TIMESTAMP_HELP " 0 means now. "
"(RFC 4880, Section 5.2.3.4)" },
{ "--key-expiration", sig_expiration,
"The number of days until the associated key expires. To specify "
"seconds, prefix the value with \"seconds=\". It is also possible "
"to use 'y', 'm' and 'w' as simple multipliers. For instance, 2y "
"means 2 years, etc. "
"(RFC 4880, Section 5.2.3.6)" },
{ "--cipher-algos", sig_int_list,
"A comma separated list of the preferred cipher algorithms (identified by "
"their number, see RFC 4880, Section 9). "
"(RFC 4880, Section 5.2.3.7)" },
{ "--digest-algos", sig_int_list,
"A comma separated list of the preferred algorithms (identified by "
"their number, see RFC 4880, Section 9). "
"(RFC 4880, Section 5.2.3.8)" },
{ "--compress-algos", sig_int_list,
"A comma separated list of the preferred algorithms (identified by "
"their number, see RFC 4880, Section 9)."
"(RFC 4880, Section 5.2.3.9)" },
{ "--expiration", sig_expiration,
"The number of days until the signature expires. To specify seconds, "
"prefix the value with \"seconds=\". It is also possible to use 'y', "
"'m' and 'w' as simple multipliers. For instance, 2y means 2 years, "
"etc. "
"(RFC 4880, Section 5.2.3.10)" },
{ "--exportable", sig_flag,
"Mark this signature as exportable (1) or local (0). "
"(RFC 4880, Section 5.2.3.11)" },
{ "--revocable", sig_flag,
"Mark this signature as revocable (1, revocations are ignored) "
"or non-revocable (0). "
"(RFC 4880, Section 5.2.3.12)" },
{ "--trust-level", sig_trust_level,
"Set the trust level. This takes two integer arguments (0-255): "
"the trusted-introducer level and the degree of trust. "
"(RFC 4880, Section 5.2.3.13.)" },
{ "--trust-scope", sig_string_arg,
"A regular expression that limits the scope of --trust-level. "
"(RFC 4880, Section 5.2.3.14.)" },
{ "--revocation-key", sig_revocation_key,
"Specify a designated revoker. Takes two arguments: the class "
"(normally 0x80 or 0xC0 (sensitive)) and the key id of the "
"designatured revoker. May be given multiple times. "
"(RFC 4880, Section 5.2.3.15)" },
{ "--notation", sig_notation,
"Add a human-readable notation of the form \"[!<]name=value\" where "
"\"!\" means that the critical flag should be set and \"<\" means "
"that VALUE is a file to read the data from. "
"(RFC 4880, Section 5.2.3.16)" },
{ "--notation-binary", sig_notation,
"Add a binary notation of the form \"[!<]name=value\" where "
"\"!\" means that the critical flag should be set and \"<\" means "
"that VALUE is a file to read the data from. "
"(RFC 4880, Section 5.2.3.16)" },
{ "--key-server-preferences", sig_big_endian_arg,
"Big-endian number encoding the keyserver preferences. "
"(RFC 4880, Section 5.2.3.17)" },
{ "--key-server", sig_string_arg,
"The preferred keyserver. (RFC 4880, Section 5.2.3.18)" },
{ "--primary-user-id", sig_flag,
"Sets the primary user id flag. (RFC 4880, Section 5.2.3.19)" },
{ "--policy-uri", sig_string_arg,
"URI of a document that describes the issuer's signing policy. "
"(RFC 4880, Section 5.2.3.20)" },
{ "--key-flags", sig_big_endian_arg,
"Big-endian number encoding the key flags. "
"(RFC 4880, Section 5.2.3.21)" },
{ "--signers-user-id", sig_string_arg,
"The user id (as a string) responsible for the signing. "
"(RFC 4880, Section 5.2.3.22)" },
{ "--reason-for-revocation", sig_reason_for_revocation,
"Takes two arguments: a reason for revocation code and a "
"user-provided string. "
"(RFC 4880, Section 5.2.3.23)" },
{ "--features", sig_big_endian_arg,
"Big-endian number encoding the feature flags. "
"(RFC 4880, Section 5.2.3.24)" },
{ "--signature-target", NULL,
"Takes three arguments: the target signature's public key algorithm "
" (as an integer), the hash algorithm (as an integer) and the hash "
" (as a hexadecimal string). "
"(RFC 4880, Section 5.2.3.25)" },
{ "--embedded-signature", NULL,
"An embedded signature. This must be immediately followed by a "
"signature packet (created using --signature ...) or a filename "
"containing the packet."
"(RFC 4880, Section 5.2.3.26)" },
{ "--hashed", NULL,
"The following attributes will be placed in the hashed area of "
"the signature. (This is the default and it reset at the end of"
"each signature.)" },
{ "--unhashed", NULL,
"The following attributes will be placed in the unhashed area of "
"the signature (and thus not integrity protected)." },
{ "--corrupt", sig_corrupt,
"Corrupt the signature." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --public-key $KEYID --user-id USERID \\\n"
" --signature --class 0x10 --issuer $KEYID --issuer-keyid self \\\n"
" | " GPG_NAME " --list-packets"}
};
static int
mksubpkt_callback (PKT_signature *sig, void *cookie)
{
struct signinfo *si = cookie;
int i;
if (si->key_expiration)
{
char buf[4];
buf[0] = (si->key_expiration >> 24) & 0xff;
buf[1] = (si->key_expiration >> 16) & 0xff;
buf[2] = (si->key_expiration >> 8) & 0xff;
buf[3] = si->key_expiration & 0xff;
build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4);
}
if (si->cipher_algorithms)
build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM,
si->cipher_algorithms,
si->cipher_algorithms_len);
if (si->digest_algorithms)
build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH,
si->digest_algorithms,
si->digest_algorithms_len);
if (si->compress_algorithms)
build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR,
si->compress_algorithms,
si->compress_algorithms_len);
if (si->exportable_set)
{
char buf = si->exportable;
build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, &buf, 1);
}
if (si->trust_level_set)
build_sig_subpkt (sig, SIGSUBPKT_TRUST,
si->trust_args, sizeof (si->trust_args));
if (si->trust_scope)
build_sig_subpkt (sig, SIGSUBPKT_REGEXP,
si->trust_scope, strlen (si->trust_scope));
for (i = 0; i < si->nrevocation_keys; i ++)
{
struct revocation_key *revkey = &si->revocation_key[i];
gpg_error_t err = keygen_add_revkey (sig, revkey);
if (err)
{
u32 keyid[2];
keyid_from_fingerprint (global_ctrl, revkey->fpr, 20, keyid);
log_fatal ("adding revocation key %s: %s\n",
keystr (keyid), gpg_strerror (err));
}
}
/* keygen_add_revkey sets revocable=0 so be sure to do this after
adding the rev keys. */
if (si->revocable_set)
{
char buf = si->revocable;
build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, &buf, 1);
}
keygen_add_notations (sig, si->notations);
if (si->key_server_preferences)
build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS,
si->key_server_preferences,
si->key_server_preferences_len);
if (si->key_server)
build_sig_subpkt (sig, SIGSUBPKT_PREF_KS,
si->key_server, strlen (si->key_server));
if (si->primary_user_id_set)
{
char buf = si->primary_user_id;
build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, &buf, 1);
}
if (si->policy_uri)
build_sig_subpkt (sig, SIGSUBPKT_POLICY,
si->policy_uri, strlen (si->policy_uri));
if (si->key_flags)
build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS,
si->key_flags, si->key_flags_len);
if (si->signers_user_id)
build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID,
si->signers_user_id, strlen (si->signers_user_id));
if (si->reason_for_revocation)
{
int len = 1 + strlen (si->reason_for_revocation);
char *buf;
buf = xmalloc (len);
buf[0] = si->reason_for_revocation_code;
memcpy (&buf[1], si->reason_for_revocation, len - 1);
build_sig_subpkt (sig, SIGSUBPKT_REVOC_REASON, buf, len);
xfree (buf);
}
if (si->features)
build_sig_subpkt (sig, SIGSUBPKT_FEATURES,
si->features, si->features_len);
return 0;
}
static int
signature (const char *option, int argc, char *argv[], void *cookie)
{
gpg_error_t err;
iobuf_t out = cookie;
struct signinfo si;
int processed;
PKT_public_key *pk;
PKT_signature *sig;
PACKET pkt;
u32 keyid_orig[2], keyid[2];
(void) option;
memset (&si, 0, sizeof (si));
memset (&pkt, 0, sizeof (pkt));
processed = process_options (option,
major_options,
sig_options, &si,
global_options, NULL,
argc, argv);
if (ncomponents)
{
int pkttype = components[ncomponents - 1].pkttype;
if (pkttype == PKT_PUBLIC_KEY)
{
if (! si.class)
/* Direct key sig. */
si.class = 0x1F;
}
else if (pkttype == PKT_PUBLIC_SUBKEY)
{
if (! si.sk)
si.sk = components[ncomponents - 1].pkt.public_key;
if (! si.class)
/* Subkey binding sig. */
si.class = 0x18;
}
else if (pkttype == PKT_USER_ID)
{
if (! si.uid)
si.uid = components[ncomponents - 1].pkt.user_id;
if (! si.class)
/* Certification of a user id and public key packet. */
si.class = 0x10;
}
}
pk = NULL;
if (! si.pk || ! si.issuer_pk)
/* No primary key specified. Default to the first one that we
find. */
{
int i;
for (i = 0; i < ncomponents; i ++)
if (components[i].pkttype == PKT_PUBLIC_KEY)
{
pk = components[i].pkt.public_key;
break;
}
}
if (! si.pk)
{
if (! pk)
log_fatal ("%s: no primary key given and no primary key available",
"--pk");
si.pk = pk;
}
if (! si.issuer_pk)
{
if (! pk)
log_fatal ("%s: no issuer key given and no primary key available",
"--issuer");
si.issuer_pk = pk;
}
if (si.class == 0x18 || si.class == 0x19 || si.class == 0x28)
/* Requires the primary key and a subkey. */
{
if (! si.sk)
log_fatal ("sig class 0x%x requires a subkey (--sk)\n", si.class);
}
else if (si.class == 0x10
|| si.class == 0x11
|| si.class == 0x12
|| si.class == 0x13
|| si.class == 0x30)
/* Requires the primary key and a user id. */
{
if (! si.uid)
log_fatal ("sig class 0x%x requires a uid (--uid)\n", si.class);
}
else if (si.class == 0x1F || si.class == 0x20)
/* Just requires the primary key. */
;
else
log_fatal ("Unsupported signature class: 0x%x\n", si.class);
sig = xmalloc_clear (sizeof (*sig));
/* Save SI.ISSUER_PK->KEYID. */
keyid_copy (keyid_orig, pk_keyid (si.issuer_pk));
if (si.issuer_keyid[0] || si.issuer_keyid[1])
keyid_copy (si.issuer_pk->keyid, si.issuer_keyid);
else if (si.issuer_keyid_self)
{
PKT_public_key *pripk = primary_key();
if (! pripk)
log_fatal ("--issuer-keyid self given, but no primary key available.\n");
keyid_copy (si.issuer_pk->keyid, pk_keyid (pripk));
}
/* Changing the issuer's key id is fragile. Check to make sure
make_keysig_packet didn't recompute the keyid. */
keyid_copy (keyid, si.issuer_pk->keyid);
err = make_keysig_packet (global_ctrl,
&sig, si.pk, si.uid, si.sk, si.issuer_pk,
si.class, si.digest_algo,
si.timestamp, si.expiration,
mksubpkt_callback, &si, NULL);
log_assert (keyid_cmp (keyid, si.issuer_pk->keyid) == 0);
if (err)
log_fatal ("Generating signature: %s\n", gpg_strerror (err));
/* Restore SI.PK->KEYID. */
keyid_copy (si.issuer_pk->keyid, keyid_orig);
if (si.corrupt)
{
/* Set the top 32-bits to 0xBAD0DEAD. */
int bits = gcry_mpi_get_nbits (sig->data[0]);
gcry_mpi_t x = gcry_mpi_new (0);
gcry_mpi_add_ui (x, x, 0xBAD0DEAD);
gcry_mpi_lshift (x, x, bits > 32 ? bits - 32 : bits);
gcry_mpi_clear_highbit (sig->data[0], bits > 32 ? bits - 32 : 0);
gcry_mpi_add (sig->data[0], sig->data[0], x);
gcry_mpi_release (x);
}
pkt.pkttype = PKT_SIGNATURE;
pkt.pkt.signature = sig;
err = build_packet (out, &pkt);
if (err)
log_fatal ("serializing public key packet: %s\n", gpg_strerror (err));
debug ("Wrote signature packet:\n");
dump_component (&pkt);
free_seckey_enc (sig);
release_kbnode (si.issuer_kb);
xfree (si.revocation_key);
return processed;
}
struct sk_esk_info
{
/* The cipher used for encrypting the session key (when a session
key is used). */
int cipher;
/* The cipher used for encryping the SED packet. */
int sed_cipher;
/* S2K related data. */
int hash;
int mode;
int mode_set;
byte salt[8];
int salt_set;
int iterations;
/* If applying the S2K function to the passphrase is the session key
or if it is the decryption key for the session key. */
int s2k_is_session_key;
/* Generate a new, random session key. */
int new_session_key;
/* The unencrypted session key. */
int session_key_len;
char *session_key;
char *password;
};
static int
sk_esk_cipher (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "integer|IDEA|3DES|CAST5|BLOWFISH|AES|AES192|AES256|CAMELLIA128|CAMELLIA192|CAMELLIA256";
int cipher;
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (strcasecmp (argv[0], "IDEA") == 0)
cipher = CIPHER_ALGO_IDEA;
else if (strcasecmp (argv[0], "3DES") == 0)
cipher = CIPHER_ALGO_3DES;
else if (strcasecmp (argv[0], "CAST5") == 0)
cipher = CIPHER_ALGO_CAST5;
else if (strcasecmp (argv[0], "BLOWFISH") == 0)
cipher = CIPHER_ALGO_BLOWFISH;
else if (strcasecmp (argv[0], "AES") == 0)
cipher = CIPHER_ALGO_AES;
else if (strcasecmp (argv[0], "AES192") == 0)
cipher = CIPHER_ALGO_AES192;
else if (strcasecmp (argv[0], "TWOFISH") == 0)
cipher = CIPHER_ALGO_TWOFISH;
else if (strcasecmp (argv[0], "CAMELLIA128") == 0)
cipher = CIPHER_ALGO_CAMELLIA128;
else if (strcasecmp (argv[0], "CAMELLIA192") == 0)
cipher = CIPHER_ALGO_CAMELLIA192;
else if (strcasecmp (argv[0], "CAMELLIA256") == 0)
cipher = CIPHER_ALGO_CAMELLIA256;
else
{
char *tail;
int v;
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail) || ! valid_cipher (v))
log_fatal ("Invalid or unsupported value. Usage: %s %s\n",
option, usage);
cipher = v;
}
if (strcmp (option, "--cipher") == 0)
{
if (si->cipher)
log_fatal ("%s given multiple times.", option);
si->cipher = cipher;
}
else if (strcmp (option, "--sed-cipher") == 0)
{
if (si->sed_cipher)
log_fatal ("%s given multiple times.", option);
si->sed_cipher = cipher;
}
return 1;
}
static int
sk_esk_mode (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "integer|simple|salted|iterated";
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (si->mode)
log_fatal ("%s given multiple times.", option);
if (strcasecmp (argv[0], "simple") == 0)
si->mode = 0;
else if (strcasecmp (argv[0], "salted") == 0)
si->mode = 1;
else if (strcasecmp (argv[0], "iterated") == 0)
si->mode = 3;
else
{
char *tail;
int v;
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail) || ! (v == 0 || v == 1 || v == 3))
log_fatal ("Invalid or unsupported value. Usage: %s %s\n",
option, usage);
si->mode = v;
}
si->mode_set = 1;
return 1;
}
static int
sk_esk_hash_algorithm (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "integer|MD5|SHA1|RMD160|SHA256|SHA384|SHA512|SHA224";
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (si->hash)
log_fatal ("%s given multiple times.", option);
if (strcasecmp (argv[0], "MD5") == 0)
si->hash = DIGEST_ALGO_MD5;
else if (strcasecmp (argv[0], "SHA1") == 0)
si->hash = DIGEST_ALGO_SHA1;
else if (strcasecmp (argv[0], "RMD160") == 0)
si->hash = DIGEST_ALGO_RMD160;
else if (strcasecmp (argv[0], "SHA256") == 0)
si->hash = DIGEST_ALGO_SHA256;
else if (strcasecmp (argv[0], "SHA384") == 0)
si->hash = DIGEST_ALGO_SHA384;
else if (strcasecmp (argv[0], "SHA512") == 0)
si->hash = DIGEST_ALGO_SHA512;
else if (strcasecmp (argv[0], "SHA224") == 0)
si->hash = DIGEST_ALGO_SHA224;
else
{
char *tail;
int v;
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail)
|| ! (v == DIGEST_ALGO_MD5
|| v == DIGEST_ALGO_SHA1
|| v == DIGEST_ALGO_RMD160
|| v == DIGEST_ALGO_SHA256
|| v == DIGEST_ALGO_SHA384
|| v == DIGEST_ALGO_SHA512
|| v == DIGEST_ALGO_SHA224))
log_fatal ("Invalid or unsupported value. Usage: %s %s\n",
option, usage);
si->hash = v;
}
return 1;
}
static int
sk_esk_salt (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "16-HEX-CHARACTERS";
char *p = argv[0];
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (si->salt_set)
log_fatal ("%s given multiple times.", option);
if (p[0] == '0' && p[1] == 'x')
p += 2;
if (strlen (p) != 16)
log_fatal ("%s: Salt must be exactly 16 hexadecimal characters (have: %zd)\n",
option, strlen (p));
if (hex2bin (p, si->salt, sizeof (si->salt)) == -1)
log_fatal ("%s: Salt must only contain hexadecimal characters\n",
option);
si->salt_set = 1;
return 1;
}
static int
sk_esk_iterations (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "ITERATION-COUNT";
char *tail;
int v;
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail) || v < 0)
log_fatal ("%s: Non-negative integer expected.\n", option);
si->iterations = v;
return 1;
}
static int
sk_esk_session_key (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "HEX-CHARACTERS|auto|none";
char *p = argv[0];
struct session_key sk;
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (si->session_key || si->s2k_is_session_key
|| si->new_session_key)
log_fatal ("%s given multiple times.", option);
if (strcasecmp (p, "none") == 0)
{
si->s2k_is_session_key = 1;
return 1;
}
if (strcasecmp (p, "new") == 0)
{
si->new_session_key = 1;
return 1;
}
if (strcasecmp (p, "auto") == 0)
return 1;
sk = parse_session_key (option, p, 0);
if (si->session_key)
log_fatal ("%s given multiple times.", option);
if (sk.algo)
si->sed_cipher = sk.algo;
si->session_key_len = sk.keylen;
si->session_key = sk.key;
return 1;
}
static int
sk_esk_password (const char *option, int argc, char *argv[], void *cookie)
{
struct sk_esk_info *si = cookie;
char *usage = "PASSWORD";
if (argc == 0)
log_fatal ("Usage: --sk-esk %s\n", usage);
if (si->password)
log_fatal ("%s given multiple times.", option);
si->password = xstrdup (argv[0]);
return 1;
}
static struct option sk_esk_options[] = {
{ "--cipher", sk_esk_cipher,
"The encryption algorithm for encrypting the session key. "
"One of IDEA, 3DES, CAST5, BLOWFISH, AES (default), AES192, "
"AES256, TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." },
{ "--sed-cipher", sk_esk_cipher,
"The encryption algorithm for encrypting the SED packet. "
"One of IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, "
"AES256 (default), TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." },
{ "--mode", sk_esk_mode,
"The S2K mode. Either one of the strings \"simple\", \"salted\" "
"or \"iterated\" or an integer." },
{ "--hash", sk_esk_hash_algorithm,
"The hash algorithm to used to derive the key. One of "
"MD5, SHA1 (default), RMD160, SHA256, SHA384, SHA512, or SHA224." },
{ "--salt", sk_esk_salt,
"The S2K salt encoded as 16 hexadecimal characters. One needed "
"if the S2K function is in salted or iterated mode." },
{ "--iterations", sk_esk_iterations,
"The iteration count. If not provided, a reasonable value is chosen. "
"Note: due to the encoding scheme, not every value is valid. For "
"convenience, the provided value will be rounded appropriately. "
"Only needed if the S2K function is in iterated mode." },
{ "--session-key", sk_esk_session_key,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is \"new\", then a new session key is generated."
"If this is \"auto\", then either the last session key is "
"used, if the was none, one is generated. If this is \"none\", then "
"the session key is the result of applying the S2K algorithms to the "
"password. The session key may be prefaced with an integer and a colon "
"to indicate the cipher to use for the SED packet (making --sed-cipher "
"unnecessary and allowing the direct use of the result of "
"\"" GPG_NAME " --show-session-key\")." },
{ "", sk_esk_password, "The password." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --sk-esk foobar --encrypted \\\n"
" --literal --value foo | " GPG_NAME " --list-packets" }
};
static int
sk_esk (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
gpg_error_t err;
int processed;
struct sk_esk_info si;
DEK sesdek;
DEK s2kdek;
PKT_symkey_enc *ske;
PACKET pkt;
memset (&si, 0, sizeof (si));
processed = process_options (option,
major_options,
sk_esk_options, &si,
global_options, NULL,
argc, argv);
if (! si.password)
log_fatal ("%s: missing password. Usage: %s PASSWORD", option, option);
/* Fill in defaults, if appropriate. */
if (! si.cipher)
si.cipher = CIPHER_ALGO_AES;
if (! si.sed_cipher)
si.sed_cipher = CIPHER_ALGO_AES256;
if (! si.hash)
si.hash = DIGEST_ALGO_SHA1;
if (! si.mode_set)
/* Salted and iterated. */
si.mode = 3;
if (si.mode != 0 && ! si.salt_set)
/* Generate a salt. */
gcry_randomize (si.salt, 8, GCRY_STRONG_RANDOM);
if (si.mode == 0)
{
if (si.iterations)
log_info ("%s: --iterations provided, but not used for mode=0\n",
option);
si.iterations = 0;
}
else if (! si.iterations)
si.iterations = 10000;
memset (&sesdek, 0, sizeof (sesdek));
/* The session key is used to encrypt the SED packet. */
sesdek.algo = si.sed_cipher;
if (si.session_key)
/* Copy the unencrypted session key into SESDEK. */
{
sesdek.keylen = openpgp_cipher_get_algo_keylen (sesdek.algo);
if (sesdek.keylen != si.session_key_len)
log_fatal ("%s: Cipher algorithm requires a %d byte session key, but provided session key is %d bytes.",
option, sesdek.keylen, si.session_key_len);
log_assert (sesdek.keylen <= sizeof (sesdek.key));
memcpy (sesdek.key, si.session_key, sesdek.keylen);
}
else if (! si.s2k_is_session_key || si.new_session_key)
/* We need a session key, but one wasn't provided. Generate it. */
make_session_key (&sesdek);
/* The encrypted session key needs 1 + SESDEK.KEYLEN bytes of
space. */
ske = xmalloc_clear (sizeof (*ske) + sesdek.keylen);
ske->version = 4;
ske->cipher_algo = si.cipher;
ske->s2k.mode = si.mode;
ske->s2k.hash_algo = si.hash;
log_assert (sizeof (si.salt) == sizeof (ske->s2k.salt));
memcpy (ske->s2k.salt, si.salt, sizeof (ske->s2k.salt));
if (! si.s2k_is_session_key)
/* 0 means get the default. */
ske->s2k.count = encode_s2k_iterations (si.iterations);
/* Derive the symmetric key that is either the session key or the
key used to encrypt the session key. */
memset (&s2kdek, 0, sizeof (s2kdek));
s2kdek.algo = si.cipher;
s2kdek.keylen = openpgp_cipher_get_algo_keylen (s2kdek.algo);
err = gcry_kdf_derive (si.password, strlen (si.password),
ske->s2k.mode == 3 ? GCRY_KDF_ITERSALTED_S2K
: ske->s2k.mode == 1 ? GCRY_KDF_SALTED_S2K
: GCRY_KDF_SIMPLE_S2K,
ske->s2k.hash_algo, ske->s2k.salt, 8,
S2K_DECODE_COUNT (ske->s2k.count),
/* The size of the desired key and its
buffer. */
s2kdek.keylen, s2kdek.key);
if (err)
log_fatal ("gcry_kdf_derive failed: %s", gpg_strerror (err));
if (si.s2k_is_session_key)
{
ske->seskeylen = 0;
session_key = s2kdek;
}
else
/* Encrypt the session key using the s2k specifier. */
{
DEK *sesdekp = &sesdek;
/* Now encrypt the session key (or rather, the algorithm used to
encrypt the SED plus the session key) using ENCKEY. */
ske->seskeylen = 1 + sesdek.keylen;
encrypt_seskey (&s2kdek, &sesdekp, ske->seskey);
/* Save the session key for later. */
session_key = sesdek;
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = ske;
err = build_packet (out, &pkt);
if (err)
log_fatal ("Serializing sym-key encrypted packet: %s\n",
gpg_strerror (err));
debug ("Wrote sym-key encrypted packet:\n");
dump_component (&pkt);
xfree (si.session_key);
xfree (si.password);
xfree (ske);
return processed;
}
struct pk_esk_info
{
int session_key_set;
int new_session_key;
int sed_cipher;
int session_key_len;
char *session_key;
int throw_keyid;
char *keyid;
};
static int
pk_esk_session_key (const char *option, int argc, char *argv[], void *cookie)
{
struct pk_esk_info *pi = cookie;
char *usage = "HEX-CHARACTERS|auto|none";
char *p = argv[0];
struct session_key sk;
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (pi->session_key_set)
log_fatal ("%s given multiple times.", option);
pi->session_key_set = 1;
if (strcasecmp (p, "new") == 0)
{
pi->new_session_key = 1;
return 1;
}
if (strcasecmp (p, "auto") == 0)
return 1;
sk = parse_session_key (option, p, 0);
if (pi->session_key)
log_fatal ("%s given multiple times.", option);
if (sk.algo)
pi->sed_cipher = sk.algo;
pi->session_key_len = sk.keylen;
pi->session_key = sk.key;
return 1;
}
static int
pk_esk_throw_keyid (const char *option, int argc, char *argv[], void *cookie)
{
struct pk_esk_info *pi = cookie;
(void) option;
(void) argc;
(void) argv;
pi->throw_keyid = 1;
return 0;
}
static int
pk_esk_keyid (const char *option, int argc, char *argv[], void *cookie)
{
struct pk_esk_info *pi = cookie;
char *usage = "KEYID";
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (pi->keyid)
log_fatal ("Multiple key ids given, but only one is allowed.");
pi->keyid = xstrdup (argv[0]);
return 1;
}
static struct option pk_esk_options[] = {
{ "--session-key", pk_esk_session_key,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is not given or is \"auto\", then the current "
"session key is used. If there is no session key or this is \"new\", "
"then a new session key is generated. The session key may be "
"prefaced with an integer and a colon to indicate the cipher to use "
"for the SED packet (making --sed-cipher unnecessary and allowing the "
"direct use of the result of \"" GPG_NAME " --show-session-key\")." },
{ "--throw-keyid", pk_esk_throw_keyid,
"Throw the keyid." },
{ "", pk_esk_keyid, "The key id." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --pk-esk $KEYID --encrypted --literal --value foo \\\n"
" | " GPG_NAME " --list-packets"}
};
static int
pk_esk (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
gpg_error_t err;
int processed;
struct pk_esk_info pi;
PKT_public_key pk;
memset (&pi, 0, sizeof (pi));
processed = process_options (option,
major_options,
pk_esk_options, &pi,
global_options, NULL,
argc, argv);
if (! pi.keyid)
log_fatal ("%s: missing keyid. Usage: %s KEYID", option, option);
memset (&pk, 0, sizeof (pk));
pk.req_usage = PUBKEY_USAGE_ENC;
err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL,
NULL, &pk, pi.keyid, NULL, NULL, 1);
if (err)
log_fatal ("%s: looking up key %s: %s\n",
option, pi.keyid, gpg_strerror (err));
if (pi.sed_cipher)
/* Have a session key. */
{
session_key.algo = pi.sed_cipher;
session_key.keylen = pi.session_key_len;
log_assert (session_key.keylen <= sizeof (session_key.key));
memcpy (session_key.key, pi.session_key, session_key.keylen);
}
if (pi.new_session_key || ! session_key.algo)
{
if (! pi.new_session_key)
/* Default to AES256. */
session_key.algo = CIPHER_ALGO_AES256;
make_session_key (&session_key);
}
err = write_pubkey_enc (global_ctrl, &pk, pi.throw_keyid, &session_key, out);
if (err)
log_fatal ("%s: writing pk_esk packet for %s: %s\n",
option, pi.keyid, gpg_strerror (err));
debug ("Wrote pk_esk packet for %s\n", pi.keyid);
xfree (pi.keyid);
xfree (pi.session_key);
return processed;
}
struct encinfo
{
int saw_session_key;
};
static int
encrypted_session_key (const char *option, int argc, char *argv[], void *cookie)
{
struct encinfo *ei = cookie;
char *usage = "HEX-CHARACTERS|auto";
char *p = argv[0];
struct session_key sk;
if (argc == 0)
log_fatal ("Usage: %s %s\n", option, usage);
if (ei->saw_session_key)
log_fatal ("%s given multiple times.", option);
ei->saw_session_key = 1;
if (strcasecmp (p, "auto") == 0)
return 1;
sk = parse_session_key (option, p, 1);
session_key.algo = sk.algo;
log_assert (sk.keylen <= sizeof (session_key.key));
memcpy (session_key.key, sk.key, sk.keylen);
xfree (sk.key);
return 1;
}
static struct option encrypted_options[] = {
{ "--session-key", encrypted_session_key,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is not given or is \"auto\", then the last session key "
"is used. If there was none, then an error is raised. The session key "
"must be prefaced with an integer and a colon to indicate the cipher "
"to use (this is format used by \"" GPG_NAME " --show-session-key\")." },
{ NULL, NULL,
"After creating the packet, this command clears the current "
"session key.\n\n"
"Example: nested encryption packets:\n\n"
" $ gpgcompose --sk-esk foo --encrypted-mdc \\\n"
" --sk-esk bar --encrypted-mdc \\\n"
" --literal --value 123 --encrypted-pop --encrypted-pop | " GPG_NAME" -d" }
};
static int
encrypted (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
int processed;
struct encinfo ei;
PKT_encrypted e;
cipher_filter_context_t *cfx;
memset (&ei, 0, sizeof (ei));
processed = process_options (option,
major_options,
encrypted_options, &ei,
global_options, NULL,
argc, argv);
if (! session_key.algo)
log_fatal ("%s: no session key configured\n"
" (use e.g. --sk-esk PASSWORD or --pk-esk KEYID).\n",
option);
memset (&e, 0, sizeof (e));
/* We only need to set E->LEN, E->EXTRALEN (if E->LEN is not
0), and E->NEW_CTB. */
e.len = 0;
e.new_ctb = 1;
/* Register the cipher filter. */
cfx = xmalloc_clear (sizeof (*cfx));
/* Copy the session key. */
cfx->dek = xmalloc (sizeof (*cfx->dek));
*cfx->dek = session_key;
if (do_debug)
{
char *buf;
buf = xmalloc (2 * session_key.keylen + 1);
debug ("session key: algo: %d; keylen: %d; key: %s\n",
session_key.algo, session_key.keylen,
bin2hex (session_key.key, session_key.keylen, buf));
xfree (buf);
}
if (strcmp (option, "--encrypted-mdc") == 0)
cfx->dek->use_mdc = 1;
else if (strcmp (option, "--encrypted") == 0)
cfx->dek->use_mdc = 0;
else
log_fatal ("%s: option not handled by this function!\n", option);
cfx->datalen = 0;
- filter_push (out, cipher_filter, cfx, PKT_ENCRYPTED, cfx->datalen == 0);
+ filter_push (out, cipher_filter_cfb, cfx, PKT_ENCRYPTED, cfx->datalen == 0);
debug ("Wrote encrypted packet:\n");
/* Clear the current session key. */
memset (&session_key, 0, sizeof (session_key));
return processed;
}
static struct option encrypted_pop_options[] = {
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --sk-esk PASSWORD \\\n"
" --encrypted-mdc \\\n"
" --literal --value foo \\\n"
" --encrypted-pop | " GPG_NAME " --list-packets" }
};
static int
encrypted_pop (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
int processed;
processed = process_options (option,
major_options,
encrypted_pop_options,
NULL,
global_options, NULL,
argc, argv);
/* We only support a single option, --help, which causes the program
* to exit. */
log_assert (processed == 0);
filter_pop (out, PKT_ENCRYPTED);
debug ("Popped encryption container.\n");
return processed;
}
struct data
{
int file;
union
{
char *data;
char *filename;
};
struct data *next;
};
/* This must be the first member of the struct to be able to use
add_value! */
struct datahead
{
struct data *head;
struct data **last_next;
};
static int
add_value (const char *option, int argc, char *argv[], void *cookie)
{
struct datahead *dh = cookie;
struct data *d = xmalloc_clear (sizeof (struct data));
d->file = strcmp ("--file", option) == 0;
if (! d->file)
log_assert (strcmp ("--value", option) == 0);
if (argc == 0)
{
if (d->file)
log_fatal ("Usage: %s FILENAME\n", option);
else
log_fatal ("Usage: %s STRING\n", option);
}
if (! dh->last_next)
/* First time through. Initialize DH->LAST_NEXT. */
{
log_assert (! dh->head);
dh->last_next = &dh->head;
}
if (d->file)
d->filename = argv[0];
else
d->data = argv[0];
/* Append it. */
*dh->last_next = d;
dh->last_next = &d->next;
return 1;
}
struct litinfo
{
/* This must be the first element for add_value to work! */
struct datahead data;
int timestamp_set;
u32 timestamp;
char mode;
int partial_body_length_encoding;
char *name;
};
static int
literal_timestamp (const char *option, int argc, char *argv[], void *cookie)
{
struct litinfo *li = cookie;
char *tail = NULL;
if (argc == 0)
log_fatal ("Usage: %s TIMESTAMP\n", option);
errno = 0;
li->timestamp = parse_timestamp (argv[0], &tail);
if (errno || (tail && *tail))
log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]);
li->timestamp_set = 1;
return 1;
}
static int
literal_mode (const char *option, int argc, char *argv[], void *cookie)
{
struct litinfo *li = cookie;
if (argc == 0
|| ! (strcmp (argv[0], "b") == 0
|| strcmp (argv[0], "t") == 0
|| strcmp (argv[0], "u") == 0))
log_fatal ("Usage: %s [btu]\n", option);
li->mode = argv[0][0];
return 1;
}
static int
literal_partial_body_length (const char *option, int argc, char *argv[],
void *cookie)
{
struct litinfo *li = cookie;
char *tail;
int v;
int range[2] = {0, 1};
if (argc <= 1)
log_fatal ("Usage: %s [0|1]\n", option);
errno = 0;
v = strtol (argv[0], &tail, 0);
if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1]))
log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n",
option, argv[0], range[0], range[1]);
li->partial_body_length_encoding = v;
return 1;
}
static int
literal_name (const char *option, int argc, char *argv[], void *cookie)
{
struct litinfo *li = cookie;
if (argc <= 0)
log_fatal ("Usage: %s NAME\n", option);
if (strlen (argv[0]) > 255)
log_fatal ("%s: name is too long (%zd > 255 characters).\n",
option, strlen (argv[0]));
li->name = argv[0];
return 1;
}
static struct option literal_options[] = {
{ "--value", add_value,
"A string to store in the literal packet." },
{ "--file", add_value,
"A file to copy into the literal packet." },
{ "--timestamp", literal_timestamp,
"The literal packet's time stamp. This defaults to the current time." },
{ "--mode", literal_mode,
"The content's mode (normally 'b' (default), 't' or 'u')." },
{ "--partial-body-length", literal_partial_body_length,
"Force partial body length encoding." },
{ "--name", literal_name,
"The literal's name." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --literal --value foobar | " GPG_NAME " -d"}
};
static int
literal (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
gpg_error_t err;
int processed;
struct litinfo li;
PKT_plaintext *pt;
PACKET pkt;
struct data *data;
memset (&li, 0, sizeof (li));
processed = process_options (option,
major_options,
literal_options, &li,
global_options, NULL,
argc, argv);
if (! li.data.head)
log_fatal ("%s: no data provided (use --value or --file)", option);
pt = xmalloc_clear (sizeof (*pt) + (li.name ? strlen (li.name) : 0));
pt->new_ctb = 1;
if (li.timestamp_set)
pt->timestamp = li.timestamp;
else
/* Default to the current time. */
pt->timestamp = make_timestamp ();
pt->mode = li.mode;
if (! pt->mode)
/* Default to binary. */
pt->mode = 'b';
if (li.name)
{
strcpy (pt->name, li.name);
pt->namelen = strlen (pt->name);
}
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
if (! li.partial_body_length_encoding)
/* Compute the amount of data. */
{
pt->len = 0;
for (data = li.data.head; data; data = data->next)
{
if (data->file)
{
iobuf_t in;
int overflow;
off_t off;
in = iobuf_open (data->filename);
if (! in)
/* An error opening the file. We do error handling
below so just break here. */
{
pt->len = 0;
break;
}
off = iobuf_get_filelength (in, &overflow);
iobuf_close (in);
if (overflow || off == 0)
/* Length is unknown or there was an error
(unfortunately, iobuf_get_filelength doesn't
distinguish between 0 length files and an error!).
Fall back to partial body mode. */
{
pt->len = 0;
break;
}
pt->len += off;
}
else
pt->len += strlen (data->data);
}
}
err = build_packet (out, &pkt);
if (err)
log_fatal ("Serializing literal packet: %s\n", gpg_strerror (err));
/* Write out the data. */
for (data = li.data.head; data; data = data->next)
{
if (data->file)
{
iobuf_t in;
errno = 0;
in = iobuf_open (data->filename);
if (! in)
log_fatal ("Opening '%s': %s\n",
data->filename,
errno ? strerror (errno): "unknown error");
iobuf_copy (out, in);
if (iobuf_error (in))
log_fatal ("Reading from %s: %s\n",
data->filename,
gpg_strerror (iobuf_error (in)));
if (iobuf_error (out))
log_fatal ("Writing literal data from %s: %s\n",
data->filename,
gpg_strerror (iobuf_error (out)));
iobuf_close (in);
}
else
{
err = iobuf_write (out, data->data, strlen (data->data));
if (err)
log_fatal ("Writing literal data: %s\n", gpg_strerror (err));
}
}
if (! pt->len)
{
/* Disable partial body length mode. */
log_assert (pt->new_ctb == 1);
iobuf_set_partial_body_length_mode (out, 0);
}
debug ("Wrote literal packet:\n");
dump_component (&pkt);
while (li.data.head)
{
data = li.data.head->next;
xfree (li.data.head);
li.data.head = data;
}
xfree (pt);
return processed;
}
static int
copy_file (const char *option, int argc, char *argv[], void *cookie)
{
char **filep = cookie;
if (argc == 0)
log_fatal ("Usage: %s FILENAME\n", option);
*filep = argv[0];
return 1;
}
static struct option copy_options[] = {
{ "", copy_file, "Copy the specified file to stdout." },
{ NULL, NULL,
"Example:\n\n"
" $ gpgcompose --copy /etc/hostname\n\n"
"This is particularly useful when combined with gpgsplit." }
};
static int
copy (const char *option, int argc, char *argv[], void *cookie)
{
iobuf_t out = cookie;
char *file = NULL;
iobuf_t in;
int processed;
processed = process_options (option,
major_options,
copy_options, &file,
global_options, NULL,
argc, argv);
if (! file)
log_fatal ("Usage: %s FILE\n", option);
errno = 0;
in = iobuf_open (file);
if (! in)
log_fatal ("Error opening %s: %s.\n",
file, errno ? strerror (errno): "unknown error");
iobuf_copy (out, in);
if (iobuf_error (out))
log_fatal ("Copying data to destination: %s\n",
gpg_strerror (iobuf_error (out)));
if (iobuf_error (in))
log_fatal ("Reading data from %s: %s\n",
argv[0], gpg_strerror (iobuf_error (in)));
iobuf_close (in);
return processed;
}
int
main (int argc, char *argv[])
{
const char *filename = "-";
iobuf_t out;
int preprocessed = 1;
int processed;
ctrl_t ctrl;
opt.ignore_time_conflict = 1;
/* Allow notations in the IETF space, for instance. */
opt.expert = 1;
global_ctrl = ctrl = xcalloc (1, sizeof *ctrl);
keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG,
KEYDB_RESOURCE_FLAG_DEFAULT);
if (argc == 1)
/* Nothing to do. */
return 0;
if (strcmp (argv[1], "--output") == 0
|| strcmp (argv[1], "-o") == 0)
{
filename = argv[2];
log_info ("Writing to %s\n", filename);
preprocessed += 2;
}
out = iobuf_create (filename, 0);
if (! out)
log_fatal ("Failed to open stdout for writing\n");
processed = process_options (NULL, NULL,
major_options, out,
global_options, NULL,
argc - preprocessed, &argv[preprocessed]);
if (processed != argc - preprocessed)
log_fatal ("Didn't process %d options.\n", argc - preprocessed - processed);
iobuf_close (out);
return 0;
}
/* Stubs duplicated from gpg.c. */
int g10_errors_seen = 0;
/* Note: This function is used by signal handlers!. */
static void
emergency_cleanup (void)
{
gcry_control (GCRYCTL_TERM_SECMEM );
}
void
g10_exit( int rc )
{
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
emergency_cleanup ();
rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
exit (rc);
}
void
keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
strlist_t commands, int quiet, int seckey_check)
{
(void) ctrl;
(void) username;
(void) locusr;
(void) commands;
(void) quiet;
(void) seckey_check;
}
void
show_basic_key_info (ctrl_t ctrl, KBNODE keyblock, int made_from_sec)
{
(void)ctrl;
(void)keyblock;
(void)made_from_sec;
}
int
keyedit_print_one_sig (ctrl_t ctrl, estream_t fp,
int rc, kbnode_t keyblock, kbnode_t node,
int *inv_sigs, int *no_key, int *oth_err,
int is_selfsig, int print_without_key, int extended)
{
(void) ctrl;
(void) fp;
(void) rc;
(void) keyblock;
(void) node;
(void) inv_sigs;
(void) no_key;
(void) oth_err;
(void) is_selfsig;
(void) print_without_key;
(void) extended;
return 0;
}
diff --git a/g10/sign.c b/g10/sign.c
index 2f48a7078..f27231950 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -1,1819 +1,1819 @@
/* sign.c - sign data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2010, 2012 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "filter.h"
#include "../common/ttyio.h"
#include "trustdb.h"
#include "../common/status.h"
#include "../common/i18n.h"
#include "pkglue.h"
#include "../common/sysutils.h"
#include "call-agent.h"
#include "../common/mbox-util.h"
#include "../common/compliance.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
#else
#define LF "\n"
#endif
/* Bitflags to convey hints on what kind of signayire is created. */
#define SIGNHINT_KEYSIG 1
#define SIGNHINT_SELFSIG 2
/* Hack */
static int recipient_digest_algo=0;
/****************
* Create notations and other stuff. It is assumed that the stings in
* STRLIST are already checked to contain only printable data and have
* a valid NAME=VALUE format.
*/
static void
mk_notation_policy_etc (PKT_signature *sig,
PKT_public_key *pk, PKT_public_key *pksk)
{
const char *string;
char *p = NULL;
strlist_t pu = NULL;
struct notation *nd = NULL;
struct expando_args args;
log_assert (sig->version >= 4);
memset (&args, 0, sizeof(args));
args.pk = pk;
args.pksk = pksk;
/* Notation data. */
if (IS_SIG(sig) && opt.sig_notations)
nd = opt.sig_notations;
else if (IS_CERT(sig) && opt.cert_notations)
nd = opt.cert_notations;
if (nd)
{
struct notation *item;
for (item = nd; item; item = item->next)
{
item->altvalue = pct_expando (item->value,&args);
if (!item->altvalue)
log_error (_("WARNING: unable to %%-expand notation "
"(too large). Using unexpanded.\n"));
}
keygen_add_notations (sig, nd);
for (item = nd; item; item = item->next)
{
xfree (item->altvalue);
item->altvalue = NULL;
}
}
/* Set policy URL. */
if (IS_SIG(sig) && opt.sig_policy_url)
pu = opt.sig_policy_url;
else if (IS_CERT(sig) && opt.cert_policy_url)
pu = opt.cert_policy_url;
for (; pu; pu = pu->next)
{
string = pu->d;
p = pct_expando (string, &args);
if (!p)
{
log_error(_("WARNING: unable to %%-expand policy URL "
"(too large). Using unexpanded.\n"));
p = xstrdup(string);
}
build_sig_subpkt (sig, (SIGSUBPKT_POLICY
| ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)),
p, strlen (p));
xfree (p);
}
/* Preferred keyserver URL. */
if (IS_SIG(sig) && opt.sig_keyserver_url)
pu = opt.sig_keyserver_url;
for (; pu; pu = pu->next)
{
string = pu->d;
p = pct_expando (string, &args);
if (!p)
{
log_error (_("WARNING: unable to %%-expand preferred keyserver URL"
" (too large). Using unexpanded.\n"));
p = xstrdup (string);
}
build_sig_subpkt (sig, (SIGSUBPKT_PREF_KS
| ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)),
p, strlen (p));
xfree (p);
}
/* Set signer's user id. */
if (IS_SIG (sig) && !opt.flags.disable_signer_uid)
{
char *mbox;
/* For now we use the uid which was used to locate the key. */
if (pksk->user_id && (mbox = mailbox_from_userid (pksk->user_id->name)))
{
if (DBG_LOOKUP)
log_debug ("setting Signer's UID to '%s'\n", mbox);
build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, mbox, strlen (mbox));
xfree (mbox);
}
else if (opt.sender_list)
{
/* If a list of --sender was given we scan that list and use
* the first one matching a user id of the current key. */
/* FIXME: We need to get the list of user ids for the PKSK
* packet. That requires either a function to look it up
* again or we need to extend the key packet struct to link
* to the primary key which in turn could link to the user
* ids. Too much of a change right now. Let's take just
* one from the supplied list and hope that the caller
* passed a matching one. */
build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID,
opt.sender_list->d, strlen (opt.sender_list->d));
}
}
}
/*
* 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)))
{
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.
*/
static void
hash_uid (gcry_md_hd_t md, int sigversion, const PKT_user_id *uid)
{
byte buf[5];
(void)sigversion;
if (uid->attrib_data)
{
buf[0] = 0xd1; /* Indicates an attribute packet. */
buf[1] = uid->attrib_len >> 24; /* Always use 4 length bytes. */
buf[2] = uid->attrib_len >> 16;
buf[3] = uid->attrib_len >> 8;
buf[4] = uid->attrib_len;
}
else
{
buf[0] = 0xb4; /* Indicates a userid packet. */
buf[1] = uid->len >> 24; /* Always use 4 length bytes. */
buf[2] = uid->len >> 16;
buf[3] = uid->len >> 8;
buf[4] = uid->len;
}
gcry_md_write( md, buf, 5 );
if (uid->attrib_data)
gcry_md_write (md, uid->attrib_data, uid->attrib_len );
else
gcry_md_write (md, uid->name, uid->len );
}
/*
* Helper to hash some parts from the signature
*/
static void
hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig)
{
byte buf[6];
size_t n;
gcry_md_putc (md, sig->version);
gcry_md_putc (md, sig->sig_class);
gcry_md_putc (md, sig->pubkey_algo);
gcry_md_putc (md, sig->digest_algo);
if (sig->hashed)
{
n = sig->hashed->len;
gcry_md_putc (md, (n >> 8) );
gcry_md_putc (md, n );
gcry_md_write (md, sig->hashed->data, n );
n += 6;
}
else
{
gcry_md_putc (md, 0); /* Always hash the length of the subpacket. */
gcry_md_putc (md, 0);
n = 6;
}
/* Add some magic. */
buf[0] = sig->version;
buf[1] = 0xff;
buf[2] = n >> 24; /* (n is only 16 bit, so this is always 0) */
buf[3] = n >> 16;
buf[4] = n >> 8;
buf[5] = n;
gcry_md_write (md, buf, 6);
}
/* Perform the sign operation. If CACHE_NONCE is given the agent is
* advised to use that cached passphrase for the key. SIGNHINTS has
* hints so that we can do some additional checks. */
static int
do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig,
gcry_md_hd_t md, int mdalgo,
const char *cache_nonce, unsigned int signhints)
{
gpg_error_t err;
byte *dp;
char *hexgrip;
if (pksk->timestamp > sig->timestamp )
{
ulong d = pksk->timestamp - sig->timestamp;
log_info (ngettext("key %s was created %lu second"
" in the future (time warp or clock problem)\n",
"key %s was created %lu seconds"
" in the future (time warp or clock problem)\n",
d), keystr_from_pk (pksk), d);
if (!opt.ignore_time_conflict)
return gpg_error (GPG_ERR_TIME_CONFLICT);
}
print_pubkey_algo_note (pksk->pubkey_algo);
if (!mdalgo)
mdalgo = gcry_md_get_algo (md);
if ((signhints & SIGNHINT_KEYSIG) && !(signhints & SIGNHINT_SELFSIG)
&& mdalgo == GCRY_MD_SHA1
&& !opt.flags.allow_weak_key_signatures)
{
/* We do not allow the creation of third-party key signatures
* using SHA-1 because we also reject them when verifying. Note
* that this will render dsa1024 keys unsuitable for such
* keysigs and in turn the WoT. */
print_sha1_keysig_rejected_note ();
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
/* Check compliance. */
if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (mdalgo),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING,
pksk->pubkey_algo, 0,
pksk->pkey, nbits_from_pk (pksk), NULL))
{
log_error (_("key %s may not be used for signing in %s mode\n"),
keystr_from_pk (pksk),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
err = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", err);
goto leave;
}
print_digest_algo_note (mdalgo);
dp = gcry_md_read (md, mdalgo);
sig->digest_algo = mdalgo;
sig->digest_start[0] = dp[0];
sig->digest_start[1] = dp[1];
mpi_release (sig->data[0]);
sig->data[0] = NULL;
mpi_release (sig->data[1]);
sig->data[1] = NULL;
err = hexkeygrip_from_pk (pksk, &hexgrip);
if (!err)
{
char *desc;
gcry_sexp_t s_sigval;
desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc,
pksk->keyid, pksk->main_keyid, pksk->pubkey_algo,
dp, gcry_md_get_algo_dlen (mdalgo), mdalgo,
&s_sigval);
xfree (desc);
if (err)
;
else if (pksk->pubkey_algo == GCRY_PK_RSA
|| pksk->pubkey_algo == GCRY_PK_RSA_S)
sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
else if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
{
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE);
}
else
{
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
}
gcry_sexp_release (s_sigval);
}
xfree (hexgrip);
leave:
if (err)
{
log_error (_("signing failed: %s\n"), gpg_strerror (err));
if (gpg_err_source (err) == GPG_ERR_SOURCE_SCD
&& gpg_err_code (err) == GPG_ERR_INV_ID)
print_further_info ("a reason might be a card with replaced keys");
}
else
{
if (opt.verbose)
{
char *ustr = get_user_id_string_native (ctrl, sig->keyid);
log_info (_("%s/%s signature from: \"%s\"\n"),
openpgp_pk_algo_name (pksk->pubkey_algo),
openpgp_md_algo_name (sig->digest_algo),
ustr);
xfree (ustr);
}
}
return err;
}
static int
complete_sig (ctrl_t ctrl,
PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md,
const char *cache_nonce, unsigned int signhints)
{
int rc;
/* if (!(rc = check_secret_key (pksk, 0))) */
rc = do_sign (ctrl, pksk, sig, md, 0, cache_nonce, signhints);
return rc;
}
/* Return true if the key seems to be on a version 1 OpenPGP card.
This works by asking the agent and may fail if the card has not yet
been used with the agent. */
static int
openpgp_card_v1_p (PKT_public_key *pk)
{
gpg_error_t err;
int result;
/* Shortcut if we are not using RSA: The v1 cards only support RSA
thus there is no point in looking any further. */
if (!is_RSA (pk->pubkey_algo))
return 0;
if (!pk->flags.serialno_valid)
{
char *hexgrip;
err = hexkeygrip_from_pk (pk, &hexgrip);
if (err)
{
log_error ("error computing a keygrip: %s\n", gpg_strerror (err));
return 0; /* Ooops. */
}
xfree (pk->serialno);
agent_get_keyinfo (NULL, hexgrip, &pk->serialno, NULL);
xfree (hexgrip);
pk->flags.serialno_valid = 1;
}
if (!pk->serialno)
result = 0; /* Error from a past agent_get_keyinfo or no card. */
else
{
/* The version number of the card is included in the serialno. */
result = !strncmp (pk->serialno, "D2760001240101", 14);
}
return result;
}
/* Get a matching hash algorithm for DSA and ECDSA. */
static int
match_dsa_hash (unsigned int qbytes)
{
if (qbytes <= 20)
return DIGEST_ALGO_SHA1;
if (qbytes <= 28)
return DIGEST_ALGO_SHA224;
if (qbytes <= 32)
return DIGEST_ALGO_SHA256;
if (qbytes <= 48)
return DIGEST_ALGO_SHA384;
if (qbytes <= 66 ) /* 66 corresponds to 521 (64 to 512) */
return DIGEST_ALGO_SHA512;
return DEFAULT_DIGEST_ALGO;
/* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong
answer we have if a digest larger than 512 bits is requested. */
}
/*
First try --digest-algo. If that isn't set, see if the recipient
has a preferred algorithm (which is also filtered through
--personal-digest-prefs). If we're making a signature without a
particular recipient (i.e. signing, rather than signing+encrypting)
then take the first algorithm in --personal-digest-prefs that is
usable for the pubkey algorithm. If --personal-digest-prefs isn't
set, then take the OpenPGP default (i.e. SHA-1).
Note that Ed25519+EdDSA takes an input of arbitrary length and thus
we don't enforce any particular algorithm like we do for standard
ECDSA. However, we use SHA256 as the default algorithm.
Possible improvement: Use the highest-ranked usable algorithm from
the signing key prefs either before or after using the personal
list?
*/
static int
hash_for (PKT_public_key *pk)
{
if (opt.def_digest_algo)
{
return opt.def_digest_algo;
}
else if (recipient_digest_algo && !is_weak_digest (recipient_digest_algo))
{
return recipient_digest_algo;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA
&& openpgp_oid_is_ed25519 (pk->pkey[0]))
{
if (opt.personal_digest_prefs)
return opt.personal_digest_prefs[0].value;
else
return DIGEST_ALGO_SHA256;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_DSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]);
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
qbytes = ecdsa_qbits_from_Q (qbytes);
qbytes = qbytes/8;
/* It's a DSA key, so find a hash that is the same size as q or
larger. If q is 160, assume it is an old DSA key and use a
160-bit hash unless --enable-dsa2 is set, in which case act
like a new DSA key that just happens to have a 160-bit q
(i.e. allow truncation). If q is not 160, by definition it
must be a new DSA key. We ignore the personal_digest_prefs
for ECDSA because they should always macth the curve and
truncated hashes are not useful either. Even worse,
smartcards may reject non matching hash lengths for curves
(e.g. using SHA-512 with brainpooolP385r1 on a Yubikey). */
if (pk->pubkey_algo == PUBKEY_ALGO_DSA && opt.personal_digest_prefs)
{
prefitem_t *prefs;
if (qbytes != 20 || opt.flags.dsa2)
{
for (prefs=opt.personal_digest_prefs; prefs->type; prefs++)
if (gcry_md_get_algo_dlen (prefs->value) >= qbytes)
return prefs->value;
}
else
{
for (prefs=opt.personal_digest_prefs; prefs->type; prefs++)
if (gcry_md_get_algo_dlen (prefs->value) == qbytes)
return prefs->value;
}
}
return match_dsa_hash(qbytes);
}
else if (openpgp_card_v1_p (pk))
{
/* The sk lives on a smartcard, and old smartcards only handle
SHA-1 and RIPEMD/160. Newer smartcards (v2.0) don't have
this restriction anymore. Fortunately the serial number
encodes the version of the card and thus we know that this
key is on a v1 card. */
if(opt.personal_digest_prefs)
{
prefitem_t *prefs;
for (prefs=opt.personal_digest_prefs;prefs->type;prefs++)
if (prefs->value==DIGEST_ALGO_SHA1
|| prefs->value==DIGEST_ALGO_RMD160)
return prefs->value;
}
return DIGEST_ALGO_SHA1;
}
else if (opt.personal_digest_prefs)
{
/* It's not DSA, so we can use whatever the first hash algorithm
is in the pref list */
return opt.personal_digest_prefs[0].value;
}
else
return DEFAULT_DIGEST_ALGO;
}
static void
print_status_sig_created (PKT_public_key *pk, PKT_signature *sig, int what)
{
byte array[MAX_FINGERPRINT_LEN];
char buf[100+MAX_FINGERPRINT_LEN*2];
size_t n;
snprintf (buf, sizeof buf - 2*MAX_FINGERPRINT_LEN, "%c %d %d %02x %lu ",
what, sig->pubkey_algo, sig->digest_algo, sig->sig_class,
(ulong)sig->timestamp );
fingerprint_from_pk (pk, array, &n);
bin2hex (array, n, buf + strlen (buf));
write_status_text( STATUS_SIG_CREATED, buf );
}
/*
* Loop over the secret certificates in SK_LIST and build the one pass
* signature packets. OpenPGP says that the data should be bracket by
* the onepass-sig and signature-packet; so we build these onepass
* packet here in reverse order
*/
static int
write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass )
{
int skcount;
SK_LIST sk_rover;
for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next)
skcount++;
for (; skcount; skcount--) {
PKT_public_key *pk;
PKT_onepass_sig *ops;
PACKET pkt;
int i, rc;
for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
if (++i == skcount)
break;
}
pk = sk_rover->pk;
ops = xmalloc_clear (sizeof *ops);
ops->sig_class = sigclass;
ops->digest_algo = hash_for (pk);
ops->pubkey_algo = pk->pubkey_algo;
keyid_from_pk (pk, ops->keyid);
ops->last = (skcount == 1);
init_packet(&pkt);
pkt.pkttype = PKT_ONEPASS_SIG;
pkt.pkt.onepass_sig = ops;
rc = build_packet (out, &pkt);
free_packet (&pkt, NULL);
if (rc) {
log_error ("build onepass_sig packet failed: %s\n",
gpg_strerror (rc));
return rc;
}
}
return 0;
}
/*
* Helper to write the plaintext (literal data) packet
*/
static int
write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode)
{
PKT_plaintext *pt = NULL;
u32 filesize;
int rc = 0;
if (!opt.no_literal)
pt=setup_plaintext_name(fname,inp);
/* try to calculate the length of the data */
if ( !iobuf_is_pipe_filename (fname) && *fname )
{
off_t tmpsize;
int overflow;
if( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info (_("WARNING: '%s' is an empty file\n"), fname);
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size of
a file is larger than 2^32 minus some bytes for packet
headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
/* Because the text_filter modifies the length of the
* data, it is not possible to know the used length
* without a double read of the file - to avoid that
* we simple use partial length packets. */
if ( ptmode == 't' || ptmode == 'u' || ptmode == 'm')
filesize = 0;
}
else
filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal) {
PACKET pkt;
/* Note that PT has been initialized above in no_literal mode. */
pt->timestamp = make_timestamp ();
pt->mode = ptmode;
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
init_packet(&pkt);
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
/*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/
if( (rc = build_packet (out, &pkt)) )
log_error ("build_packet(PLAINTEXT) failed: %s\n",
gpg_strerror (rc) );
pt->buf = NULL;
free_packet (&pkt, NULL);
}
else {
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc));
break;
}
wipememory(copy_buffer,4096); /* burn buffer */
}
/* fixme: it seems that we never freed pt/pkt */
return rc;
}
/*
* Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized
* hash which will not be changes here.
*/
static int
write_signature_packets (ctrl_t ctrl,
SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash,
int sigclass, u32 timestamp, u32 duration,
int status_letter, const char *cache_nonce)
{
SK_LIST sk_rover;
/* Loop over the certificates with secret keys. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
{
PKT_public_key *pk;
PKT_signature *sig;
gcry_md_hd_t md;
gpg_error_t err;
pk = sk_rover->pk;
/* Build the signature packet. */
sig = xtrycalloc (1, sizeof *sig);
if (!sig)
return gpg_error_from_syserror ();
if (duration || opt.sig_policy_url
|| opt.sig_notations || opt.sig_keyserver_url)
sig->version = 4;
else
sig->version = pk->version;
keyid_from_pk (pk, sig->keyid);
sig->digest_algo = hash_for (pk);
sig->pubkey_algo = pk->pubkey_algo;
if (timestamp)
sig->timestamp = timestamp;
else
sig->timestamp = make_timestamp();
if (duration)
sig->expiredate = sig->timestamp + duration;
sig->sig_class = sigclass;
if (gcry_md_copy (&md, hash))
BUG ();
if (sig->version >= 4)
{
build_sig_subpkt_from_sig (sig, pk);
mk_notation_policy_etc (sig, NULL, pk);
if (opt.flags.include_key_block && IS_SIG (sig))
err = mk_sig_subpkt_key_block (ctrl, sig, pk);
else
err = 0;
}
else
err = 0; /* Actually never reached. */
hash_sigversion_to_magic (md, sig);
gcry_md_final (md);
if (!err)
err = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce, 0);
gcry_md_close (md);
if (!err)
{
/* Write the packet. */
PACKET pkt;
init_packet (&pkt);
pkt.pkttype = PKT_SIGNATURE;
pkt.pkt.signature = sig;
err = build_packet (out, &pkt);
if (!err && is_status_enabled())
print_status_sig_created (pk, sig, status_letter);
free_packet (&pkt, NULL);
if (err)
log_error ("build signature packet failed: %s\n",
gpg_strerror (err));
}
else
free_seckey_enc (sig);
if (err)
return err;
}
return 0;
}
/* Sign the files whose names are in FILENAME using all secret keys
* which can be taken from LOCUSR, if this is NULL, use the default
* secret key.
* If DETACHED has the value true, make a detached signature.
* If FILENAMES->d is NULL read from stdin and ignore the detached mode.
* If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to
* encrypt the signed data for these users. If ENCRYPTFLAG is 2
* symmetric encryption is also used.
* If OUTFILE is not NULL; this file is used for output and the
* function does not ask for overwrite permission; output is then
* always uncompressed, non-armored and in binary mode.
*/
int
sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
int encryptflag, strlist_t remusr, const char *outfile )
{
const char *fname;
armor_filter_context_t *afx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
encrypt_filter_context_t efx;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
int rc = 0;
PK_LIST pk_list = NULL;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int multifile = 0;
u32 duration=0;
pfx = new_progress_context ();
afx = new_armor_context ();
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &efx, 0, sizeof efx);
efx.ctrl = ctrl;
init_packet( &pkt );
if( filenames ) {
fname = filenames->d;
multifile = !!filenames->next;
}
else
fname = NULL;
if( fname && filenames->next && (!detached || encryptflag) )
log_bug("multiple files can only be detached signed");
if(encryptflag==2
&& (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek)))
goto leave;
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval(1,opt.def_sig_expire);
else
duration = parse_expire_string(opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
unprotect the secret key. This is now done on demand by the agent. */
if( (rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) )
goto leave;
if (encryptflag
&& (rc=build_pk_list (ctrl, remusr, &pk_list)))
goto leave;
/* prepare iobufs */
if( multifile ) /* have list of filenames */
inp = NULL; /* we do it later */
else {
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if( !inp )
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), fname? fname: "[stdin]",
strerror(errno) );
goto leave;
}
handle_progress (pfx, inp, fname);
}
if( outfile ) {
if (is_secured_filename ( outfile )) {
out = NULL;
gpg_err_set_errno (EPERM);
}
else
out = iobuf_create (outfile, 0);
if( !out )
{
rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) );
goto leave;
}
else if( opt.verbose )
log_info(_("writing to '%s'\n"), outfile );
}
else if( (rc = open_outfile (-1, fname,
opt.armor? 1: detached? 2:0, 0, &out)))
goto leave;
/* prepare to calculate the MD over the input */
if( opt.textmode && !outfile && !multifile )
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
if ( gcry_md_open (&mfx.md, 0, 0) )
BUG ();
if (DBG_HASHING)
gcry_md_debug (mfx.md, "sign");
/* If we're encrypting and signing, it is reasonable to pick the
hash algorithm to use out of the recipient key prefs. This is
best effort only, as in a DSA2 and smartcard world there are
cases where we cannot please everyone with a single hash (DSA2
wants >160 and smartcards want =160). In the future this could
be more complex with different hashes for each sk, but the
current design requires a single hash for all SKs. */
if(pk_list)
{
if(opt.def_digest_algo)
{
if(!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_HASH,
opt.def_digest_algo,
NULL)!=opt.def_digest_algo)
log_info(_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"),
gcry_md_algo_name (opt.def_digest_algo),
opt.def_digest_algo );
}
else
{
int algo;
int conflict = 0;
struct pref_hint hint = { 0 };
/* Of course, if the recipient asks for something
unreasonable (like the wrong hash for a DSA key) then
don't do it. Check all sk's - if any are DSA or live
on a smartcard, then the hash has restrictions and we
may not be able to give the recipient what they want.
For DSA, pass a hint for the largest q we have. Note
that this means that a q>160 key will override a q=160
key and force the use of truncation for the q=160 key.
The alternative would be to ignore the recipient prefs
completely and get a different hash for each DSA key in
hash_for(). The override behavior here is more or less
reasonable as it is under the control of the user which
keys they sign with for a given message and the fact
that the message with multiple signatures won't be
usable on an implementation that doesn't understand
DSA2 anyway. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
|| sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
int temp_hashlen = (gcry_mpi_get_nbits
(sk_rover->pk->pkey[1]));
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen);
if (!temp_hashlen)
conflict = 1; /* Better don't use the prefs. */
temp_hashlen = (temp_hashlen+7)/8;
/* Fixup for that funny nistp521 (yes, 521)
* were we need to use a 512 bit hash algo. */
if (temp_hashlen == 66)
temp_hashlen = 64;
}
else
temp_hashlen = (temp_hashlen+7)/8;
/* Pick a hash that is large enough for our
largest q or matches our Q but if tehreare
several of them we run into a conflict and
don't use the preferences. */
if (hint.digest_length < temp_hashlen)
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
if (hint.exact)
conflict = 1;
hint.exact = 1;
}
hint.digest_length = temp_hashlen;
}
}
}
if (!conflict
&& (algo = select_algo_from_prefs (pk_list,PREFTYPE_HASH,
-1,&hint)) > 0)
{
/* Note that we later check that the algo is not weak. */
recipient_digest_algo = algo;
}
}
}
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
if( !multifile )
iobuf_push_filter( inp, md_filter, &mfx );
if( detached && !encryptflag)
afx->what = 2;
if( opt.armor && !outfile )
push_armor_filter (afx, out);
if( encryptflag ) {
efx.pk_list = pk_list;
/* fixme: set efx.cfx.datalen if known */
iobuf_push_filter( out, encrypt_filter, &efx );
}
if (opt.compress_algo && !outfile && !detached)
{
int compr_algo=opt.compress_algo;
/* If not forced by user */
if(compr_algo==-1)
{
/* If we're not encrypting, then select_algo_from_prefs
will fail and we'll end up with the default. If we are
encrypting, select_algo_from_prefs cannot fail since
there is an assumed preference for uncompressed data.
Still, if it did fail, we'll also end up with the
default. */
if((compr_algo=
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
compr_algo=default_compress_algo();
}
else if(!opt.expert && pk_list
&& select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
compr_algo,NULL)!=compr_algo)
log_info(_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo),compr_algo);
/* algo 0 means no compression */
if( compr_algo )
push_compress_filter(out,&zfx,compr_algo);
}
/* Write the one-pass signature packets if needed */
if (!detached) {
rc = write_onepass_sig_packets (sk_list, out,
opt.textmode && !outfile ? 0x01:0x00);
if (rc)
goto leave;
}
write_status_begin_signing (mfx.md);
/* Setup the inner packet. */
if( detached ) {
if( multifile ) {
strlist_t sl;
if( opt.verbose )
log_info(_("signing:") );
/* must walk reverse trough this list */
for( sl = strlist_last(filenames); sl;
sl = strlist_prev( filenames, sl ) ) {
inp = iobuf_open(sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if( !inp )
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"),
sl->d,strerror(errno));
goto leave;
}
handle_progress (pfx, inp, sl->d);
if( opt.verbose )
log_printf (" '%s'", sl->d );
if(opt.textmode)
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
iobuf_push_filter( inp, md_filter, &mfx );
while( iobuf_get(inp) != -1 )
;
iobuf_close(inp); inp = NULL;
}
if( opt.verbose )
log_printf ("\n");
}
else {
/* read, so that the filter can calculate the digest */
while( iobuf_get(inp) != -1 )
;
}
}
else {
rc = write_plaintext_packet (out, inp, fname,
opt.textmode && !outfile ?
(opt.mimemode? 'm':'t'):'b');
}
/* catch errors from above */
if (rc)
goto leave;
/* write the signatures */
rc = write_signature_packets (ctrl, sk_list, out, mfx.md,
opt.textmode && !outfile? 0x01 : 0x00,
0, duration, detached ? 'D':'S', NULL);
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out);
if (encryptflag)
write_status( STATUS_END_ENCRYPTION );
}
iobuf_close(inp);
gcry_md_close ( mfx.md );
release_sk_list( sk_list );
release_pk_list( pk_list );
recipient_digest_algo=0;
release_progress_context (pfx);
release_armor_context (afx);
return rc;
}
/****************
* make a clear signature. note that opt.armor is not needed
*/
int
clearsign_file (ctrl_t ctrl,
const char *fname, strlist_t locusr, const char *outfile )
{
armor_filter_context_t *afx;
progress_filter_context_t *pfx;
gcry_md_hd_t textmd = NULL;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
u32 duration=0;
pfx = new_progress_context ();
afx = new_armor_context ();
init_packet( &pkt );
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval (1,opt.def_sig_expire);
else
duration = parse_expire_string (opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
unprotect the secret key. This is now done on demand by the agent. */
if( (rc=build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) )
goto leave;
/* prepare iobufs */
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if( !inp ) {
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"),
fname? fname: "[stdin]", strerror(errno) );
goto leave;
}
handle_progress (pfx, inp, fname);
if( outfile ) {
if (is_secured_filename (outfile) ) {
outfile = NULL;
gpg_err_set_errno (EPERM);
}
else
out = iobuf_create (outfile, 0);
if( !out )
{
rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) );
goto leave;
}
else if( opt.verbose )
log_info(_("writing to '%s'\n"), outfile );
}
else if ((rc = open_outfile (-1, fname, 1, 0, &out)))
goto leave;
iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----" LF );
{
const char *s;
int any = 0;
byte hashs_seen[256];
memset( hashs_seen, 0, sizeof hashs_seen );
iobuf_writestr(out, "Hash: " );
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
int i = hash_for (sk_rover->pk);
if( !hashs_seen[ i & 0xff ] ) {
s = gcry_md_algo_name ( i );
if( s ) {
hashs_seen[ i & 0xff ] = 1;
if( any )
iobuf_put(out, ',' );
iobuf_writestr(out, s );
any = 1;
}
}
}
log_assert(any);
iobuf_writestr(out, LF );
}
if( opt.not_dash_escaped )
iobuf_writestr( out,
"NotDashEscaped: You need "GPG_NAME
" to verify this message" LF );
iobuf_writestr(out, LF );
if ( gcry_md_open (&textmd, 0, 0) )
BUG ();
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (textmd, hash_for(sk_rover->pk));
if ( DBG_HASHING )
gcry_md_debug ( textmd, "clearsign" );
copy_clearsig_text (out, inp, textmd, !opt.not_dash_escaped,
opt.escape_from);
/* fixme: check for read errors */
/* now write the armor */
afx->what = 2;
push_armor_filter (afx, out);
/* Write the signatures. */
rc = write_signature_packets (ctrl, sk_list, out, textmd, 0x01, 0,
duration, 'C', NULL);
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else
iobuf_close(out);
iobuf_close(inp);
gcry_md_close ( textmd );
release_sk_list( sk_list );
release_progress_context (pfx);
release_armor_context (afx);
return rc;
}
/*
* Sign and conventionally encrypt the given file.
* FIXME: Far too much code is duplicated - revamp the whole file.
*/
int
sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr)
{
armor_filter_context_t *afx;
progress_filter_context_t *pfx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
cipher_filter_context_t cfx;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
STRING2KEY *s2k = NULL;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int algo;
u32 duration=0;
int canceled;
pfx = new_progress_context ();
afx = new_armor_context ();
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &tfx, 0, sizeof tfx);
memset( &cfx, 0, sizeof cfx);
init_packet( &pkt );
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval (1, opt.def_sig_expire);
else
duration = parse_expire_string (opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
unprotect the secret key. This is now done on demand by the agent. */
rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG);
if (rc)
goto leave;
/* prepare iobufs */
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if( !inp ) {
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"),
fname? fname: "[stdin]", strerror(errno) );
goto leave;
}
handle_progress (pfx, inp, fname);
/* prepare key */
s2k = xmalloc_clear( sizeof *s2k );
s2k->mode = opt.s2k_mode;
s2k->hash_algo = S2K_DIGEST_ALGO;
algo = default_cipher_algo();
if (!opt.quiet || !opt.batch)
log_info (_("%s encryption will be used\n"),
openpgp_cipher_algo_name (algo) );
cfx.dek = passphrase_to_dek (algo, s2k, 1, 1, NULL, 0, &canceled);
if (!cfx.dek || !cfx.dek->keylen) {
rc = gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE);
log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc) );
goto leave;
}
cfx.dek->use_mdc = use_mdc (NULL, cfx.dek->algo);
/* now create the outfile */
rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out);
if (rc)
goto leave;
/* prepare to calculate the MD over the input */
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
if ( gcry_md_open (&mfx.md, 0, 0) )
BUG ();
if ( DBG_HASHING )
gcry_md_debug (mfx.md, "symc-sign");
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
iobuf_push_filter (inp, md_filter, &mfx);
/* Push armor output filter */
if (opt.armor)
push_armor_filter (afx, out);
/* Write the symmetric key packet */
/*(current filters: armor)*/
{
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc );
enc->version = 4;
enc->cipher_algo = cfx.dek->algo;
enc->s2k = *s2k;
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if( (rc = build_packet( out, &pkt )) )
log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
xfree(enc);
}
/* Push the encryption filter */
- iobuf_push_filter( out, cipher_filter, &cfx );
+ iobuf_push_filter (out, cipher_filter_cfb, &cfx );
/* Push the compress filter */
if (default_compress_algo())
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter (out, &zfx,default_compress_algo() );
}
/* Write the one-pass signature packets */
/*(current filters: zip - encrypt - armor)*/
rc = write_onepass_sig_packets (sk_list, out,
opt.textmode? 0x01:0x00);
if (rc)
goto leave;
write_status_begin_signing (mfx.md);
/* Pipe data through all filters; i.e. write the signed stuff */
/*(current filters: zip - encrypt - armor)*/
rc = write_plaintext_packet (out, inp, fname,
opt.textmode ? (opt.mimemode?'m':'t'):'b');
if (rc)
goto leave;
/* Write the signatures */
/*(current filters: zip - encrypt - armor)*/
rc = write_signature_packets (ctrl, sk_list, out, mfx.md,
opt.textmode? 0x01 : 0x00,
0, duration, 'S', NULL);
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out);
write_status( STATUS_END_ENCRYPTION );
}
iobuf_close(inp);
release_sk_list( sk_list );
gcry_md_close( mfx.md );
xfree(cfx.dek);
xfree(s2k);
release_progress_context (pfx);
release_armor_context (afx);
return rc;
}
/****************
* Create a v4 signature in *RET_SIG.
*
* PK is the primary key to sign (required for all sigs)
* UID is the user id to sign (required for 0x10..0x13, 0x30)
* SUBPK is subkey to sign (required for 0x18, 0x19, 0x28)
*
* PKSK is the signing key
*
* SIGCLASS is the type of signature to create.
*
* DIGEST_ALGO is the digest algorithm. If it is 0 the function
* selects an appropriate one.
*
* TIMESTAMP is the timestamp to use for the signature. 0 means "now"
*
* DURATION is the amount of time (in seconds) until the signature
* expires.
*
* This function creates the following subpackets: issuer, created,
* and expire (if duration is not 0). Additional subpackets can be
* added using MKSUBPKT, which is called after these subpackets are
* added and before the signature is generated. OPAQUE is passed to
* MKSUBPKT.
*/
int
make_keysig_packet (ctrl_t ctrl,
PKT_signature **ret_sig, PKT_public_key *pk,
PKT_user_id *uid, PKT_public_key *subpk,
PKT_public_key *pksk,
int sigclass, int digest_algo,
u32 timestamp, u32 duration,
int (*mksubpkt)(PKT_signature *, void *), void *opaque,
const char *cache_nonce)
{
PKT_signature *sig;
int rc=0;
int sigversion;
gcry_md_hd_t md;
u32 pk_keyid[2], pksk_keyid[2];
unsigned int signhints;
log_assert ((sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F
|| sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19
|| sigclass == 0x30 || sigclass == 0x28 );
sigversion = 4;
if (sigversion < pksk->version)
sigversion = pksk->version;
if( !digest_algo )
{
/* Basically, this means use SHA1 always unless the user
specified something (use whatever they said), or it's DSA
(use the best match). They still can't pick an
inappropriate hash for DSA or the signature will fail.
Note that this still allows the caller of
make_keysig_packet to override the user setting if it
must. */
if(opt.cert_digest_algo)
digest_algo=opt.cert_digest_algo;
else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA)
digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8);
else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
{
if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
digest_algo = DIGEST_ALGO_SHA256;
else
digest_algo = match_dsa_hash
(ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8);
}
else
digest_algo = DEFAULT_DIGEST_ALGO;
}
signhints = SIGNHINT_KEYSIG;
keyid_from_pk (pk, pk_keyid);
keyid_from_pk (pksk, pksk_keyid);
if (pk_keyid[0] == pksk_keyid[0] && pk_keyid[1] == pksk_keyid[1])
signhints |= SIGNHINT_SELFSIG;
if ( gcry_md_open (&md, digest_algo, 0 ) )
BUG ();
/* Hash the public key certificate. */
hash_public_key( md, pk );
if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 )
{
/* hash the subkey binding/backsig/revocation */
hash_public_key( md, subpk );
}
else if( sigclass != 0x1F && sigclass != 0x20 )
{
/* hash the user id */
hash_uid (md, sigversion, uid);
}
/* and make the signature packet */
sig = xmalloc_clear( sizeof *sig );
sig->version = sigversion;
sig->flags.exportable=1;
sig->flags.revocable=1;
keyid_from_pk (pksk, sig->keyid);
sig->pubkey_algo = pksk->pubkey_algo;
sig->digest_algo = digest_algo;
if(timestamp)
sig->timestamp=timestamp;
else
sig->timestamp=make_timestamp();
if(duration)
sig->expiredate=sig->timestamp+duration;
sig->sig_class = sigclass;
build_sig_subpkt_from_sig (sig, pksk);
mk_notation_policy_etc (sig, pk, pksk);
/* Crucial that the call to mksubpkt comes LAST before the calls
to finalize the sig as that makes it possible for the mksubpkt
function to get a reliable pointer to the subpacket area. */
if (mksubpkt)
rc = (*mksubpkt)( sig, opaque );
if( !rc ) {
hash_sigversion_to_magic (md, sig);
gcry_md_final (md);
rc = complete_sig (ctrl, sig, pksk, md, cache_nonce, signhints);
}
gcry_md_close (md);
if( rc )
free_seckey_enc( sig );
else
*ret_sig = sig;
return rc;
}
/****************
* Create a new signature packet based on an existing one.
* Only user ID signatures are supported for now.
* PK is the public key to work on.
* PKSK is the key used to make the signature.
*
* TODO: Merge this with make_keysig_packet.
*/
gpg_error_t
update_keysig_packet (ctrl_t ctrl,
PKT_signature **ret_sig,
PKT_signature *orig_sig,
PKT_public_key *pk,
PKT_user_id *uid,
PKT_public_key *subpk,
PKT_public_key *pksk,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque)
{
PKT_signature *sig;
gpg_error_t rc = 0;
int digest_algo;
gcry_md_hd_t md;
u32 pk_keyid[2], pksk_keyid[2];
unsigned int signhints;
if ((!orig_sig || !pk || !pksk)
|| (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid)
|| (orig_sig->sig_class == 0x18 && !subpk))
return GPG_ERR_GENERAL;
if ( opt.cert_digest_algo )
digest_algo = opt.cert_digest_algo;
else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA
|| pksk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
digest_algo = orig_sig->digest_algo;
else if (orig_sig->digest_algo == DIGEST_ALGO_SHA1
|| orig_sig->digest_algo == DIGEST_ALGO_RMD160)
digest_algo = DEFAULT_DIGEST_ALGO;
else
digest_algo = orig_sig->digest_algo;
signhints = SIGNHINT_KEYSIG;
keyid_from_pk (pk, pk_keyid);
keyid_from_pk (pksk, pksk_keyid);
if (pk_keyid[0] == pksk_keyid[0] && pk_keyid[1] == pksk_keyid[1])
signhints |= SIGNHINT_SELFSIG;
if ( gcry_md_open (&md, digest_algo, 0 ) )
BUG ();
/* Hash the public key certificate and the user id. */
hash_public_key( md, pk );
if( orig_sig->sig_class == 0x18 )
hash_public_key( md, subpk );
else
hash_uid (md, orig_sig->version, uid);
/* create a new signature packet */
sig = copy_signature (NULL, orig_sig);
sig->digest_algo=digest_algo;
/* We need to create a new timestamp so that new sig expiration
calculations are done correctly... */
sig->timestamp=make_timestamp();
/* ... but we won't make a timestamp earlier than the existing
one. */
{
int tmout = 0;
while(sig->timestamp<=orig_sig->timestamp)
{
if (++tmout > 5 && !opt.ignore_time_conflict)
{
rc = gpg_error (GPG_ERR_TIME_CONFLICT);
goto leave;
}
gnupg_sleep (1);
sig->timestamp=make_timestamp();
}
}
/* Note that already expired sigs will remain expired (with a
duration of 1) since build-packet.c:build_sig_subpkt_from_sig
detects this case. */
/* Put the updated timestamp into the sig. Note that this will
automagically lower any sig expiration dates to correctly
correspond to the differences in the timestamps (i.e. the
duration will shrink). */
build_sig_subpkt_from_sig (sig, pksk);
if (mksubpkt)
rc = (*mksubpkt)(sig, opaque);
if (!rc) {
hash_sigversion_to_magic (md, sig);
gcry_md_final (md);
rc = complete_sig (ctrl, sig, pksk, md, NULL, signhints);
}
leave:
gcry_md_close (md);
if( rc )
free_seckey_enc (sig);
else
*ret_sig = sig;
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 20, 6:36 AM (1 d, 16 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
87/de/00a32a24bcbaf82b194253df9fcd

Event Timeline