diff --git a/g10/encrypt.c b/g10/encrypt.c index 856849799..ab745cee8 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -1,1125 +1,1181 @@ /* 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 . */ #include #include #include #include #include #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) +/* Encrypt a session key using DEK and store a pointer to the result + * at R_ENCKEY and its length at R_ENCKEYLEN. + * + * R_SESKEY points to the unencrypted session key (.KEY, >KEYLEN) and + * the algorithm that will be used to encrypt the contents of the + * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random + * session key that is appropriate for DEK->ALGO is generated and + * stored at R_SESKEY. + */ +gpg_error_t +encrypt_seskey (DEK *dek, DEK **r_seskey, void **r_enckey, size_t *r_enckeylen) { - gcry_cipher_hd_t hd; - byte buf[33]; + gpg_error_t err; + gcry_cipher_hd_t hd = NULL; + byte *buf = NULL; + DEK *seskey; + + *r_enckey = NULL; + *r_enckeylen = 0; - log_assert ( dek->keylen <= 32 ); - if (!*seskey) + if (*r_seskey) + seskey = *r_seskey; + else { - *seskey=xmalloc_clear(sizeof(DEK)); - (*seskey)->algo=dek->algo; - make_session_key(*seskey); + seskey = xtrycalloc (1, sizeof(DEK)); + if (!seskey) + { + err = gpg_error_from_syserror (); + goto leave; + } + seskey->algo = dek->algo; + make_session_key (seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } + buf = xtrymalloc_secure (1 + seskey->keylen); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + /* 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); + buf[0] = seskey->algo; + memcpy (buf + 1, seskey->key, seskey->keylen); + + err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, NULL, 0); + if (!err) + err = gcry_cipher_encrypt (hd, buf, seskey->keylen + 1, NULL, 0); + if (err) + goto leave; + + *r_enckey = buf; + buf = NULL; + *r_enckeylen = seskey->keylen + 1; + /* Return the session key in case we allocated it. */ + *r_seskey = seskey; + seskey = NULL; - memcpy( enckey, buf, (*seskey)->keylen + 1 ); - wipememory( buf, sizeof buf ); /* burn key */ + leave: + gcry_cipher_close (hd); + if (seskey != *r_seskey) + xfree (seskey); + xfree (buf); + return err; } /* Return true if we shall use AEAD mode. */ int use_aead (pk_list_t pk_list, int algo) { int can_use; if (!opt.flags.rfc4880bis) { if (opt.force_aead) log_info ("Warning: Option %s currently requires option '%s'\n", "--force-aead", "--rfc4880bis"); return 0; } can_use = openpgp_cipher_get_algo_blklen (algo) == 16; /* With --force-mdc we clearly do not want AEAD. */ if (opt.force_mdc) return 0; /* However with --force-aead we want AEAD. */ if (opt.force_aead) { if (!can_use) { log_info ("Warning: request to use AEAD ignored for cipher '%s'\n", openpgp_cipher_algo_name (algo)); return 0; } return 1; } /* AEAD does only work with 128 bit cipher blocklength. */ if (!can_use) return 0; /* If all keys support AEAD we can use it. */ if (select_aead_from_pklist (pk_list)) return 1; return 0; /* No AEAD. */ } /* We try very hard to use a MDC */ int use_mdc (pk_list_t pk_list,int algo) { /* RFC-2440 don't has MDC */ if (RFC2440) return 0; /* --force-mdc overrides --disable-mdc */ if(opt.force_mdc) return 1; if(opt.disable_mdc) return 0; /* Do the keys really support MDC? */ if(select_mdc_from_pklist(pk_list)) return 1; /* The keys don't support MDC, so now we do a bit of a hack - if any of the AESes or TWOFISH are in the prefs, we assume that the user can handle a MDC. This is valid for PGP 7, which can handle MDCs though it will not generate them. 2440bis allows this, by the way. */ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH) return 1; /* Last try. Use MDC for the modern ciphers. */ if (openpgp_cipher_get_algo_blklen (algo) != 8) return 1; if (opt.verbose) warn_missing_mdc_from_pklist (pk_list); return 0; /* No 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]; + void *enckey = NULL; + size_t enckeylen = 0; 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 ) { int canceled; + aead_algo_t aead_algo; s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; cfx.dek = passphrase_to_dek (default_cipher_algo (), s2k, 1, 0, NULL, &canceled); if ( !cfx.dek || !cfx.dek->keylen ) { rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE); xfree (cfx.dek); xfree (s2k); iobuf_close (inp); 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")); } + /* See whether we want to use AEAD. */ + aead_algo = use_aead (NULL, cfx.dek->algo)? default_aead_algo () : 0; + 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; + rc = encrypt_seskey (cfx.dek, &dek, &enckey, &enckeylen); + if (rc) + { + xfree (cfx.dek); + xfree (s2k); + iobuf_close (inp); + release_progress_context (pfx); + return rc; + } + /* Replace key in DEK. */ + xfree (cfx.dek); + cfx.dek = dek; } if (opt.verbose) log_info(_("using cipher %s\n"), openpgp_cipher_algo_name (cfx.dek->algo)); - if (use_aead (NULL, cfx.dek->algo)) - cfx.dek->use_aead = default_aead_algo (); + if (aead_algo) + cfx.dek->use_aead = aead_algo; else cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); } if (do_compress && cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead) && 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 ); + /* Fixme: This is quite similar to write_symkey_enc. */ + PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; - if ( use_seskey && seskeylen ) + if (enckeylen) { - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy (enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); } 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); + xfree (enckey); + enckey = NULL; } 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, cfx.dek->use_aead? cipher_filter_aead /**/ : cipher_filter_cfb, &cfx ); /* Register the compress filter. */ if ( do_compress ) { if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) 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 (enckey); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); release_progress_context (pfx); return rc; } int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek) { int canceled; *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY)); (*symkey_s2k)->mode = opt.s2k_mode; (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO; *symkey_dek = passphrase_to_dek (opt.s2k_cipher_algo, *symkey_s2k, 1, 0, NULL, &canceled); if(!*symkey_dek || !(*symkey_dek)->keylen) { xfree(*symkey_dek); xfree(*symkey_s2k); return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_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); - + int rc; + void *enckey; + size_t enckeylen; PKT_symkey_enc *enc; - byte enckey[33]; PACKET pkt; - enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); - encrypt_seskey(symkey_dek,&dek,enckey); + rc = encrypt_seskey (symkey_dek, &dek, &enckey, &enckeylen); + if (rc) + return rc; + enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen); + if (!enc) + { + rc = gpg_error_from_syserror (); + xfree (enckey); + return rc; + } 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 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); + xfree (enckey); 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); + 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, 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, pk->pkey, nbits, NULL)) compliant = 0; } } if (compliant) write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS), NULL); if (use_aead (pk_list, cfx.dek->algo)) cfx.dek->use_aead = 1; else 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 or AEAD. 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 || cfx.dek->use_aead) && 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, cfx.dek->use_aead? cipher_filter_aead /**/ : 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 || cfx.dek->use_aead)) 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; } if (use_aead (efx->pk_list, efx->cfx.dek->algo)) efx->cfx.dek->use_aead = 1; else 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, efx->cfx.dek->use_aead? cipher_filter_aead /**/ : 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/gpgcompose.c b/g10/gpgcompose.c index f87983802..f22c7c202 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1,3085 +1,3087 @@ /* 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 . */ #include #include #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) \"" }, { 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, NULL, &pk, argv[1], NULL, NULL, 1, 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); xfree (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); + encrypt the SKESK plus the session key) using ENCKEY. */ + err = encrypt_seskey (&s2kdek, &sesdekp, + (void**)&ske->seskey, (size_t *)&ske->seskeylen); + if (err) + log_fatal ("encrypt_seskey failed: %s\n", gpg_strerror (err)); /* 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, NULL, &pk, pi.keyid, NULL, NULL, 1, 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_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) { (void)ctrl; (void) keyblock; } 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/main.h b/g10/main.h index 393a1b09e..509126c65 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,502 +1,503 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 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 . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "../common/types.h" #include "../common/iobuf.h" #include "keydb.h" #include "keyedit.h" #include "../common/util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #if GPG_USE_AES256 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES256 #elif GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif #define DEFAULT_AEAD_ALGO AEAD_ALGO_EAX #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ typedef struct { ctrl_t ctrl; int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; strlist_t values; struct groupitem *next; }; struct weakhash { enum gcry_md_algos algo; int rejection_shown; struct weakhash *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_digest_rejected_note (enum gcry_md_algos algo); void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec); void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); void additional_weak_digest (const char* digestname); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); gpg_error_t openpgp_aead_test_algo (aead_algo_t algo); const char *openpgp_aead_algo_name (aead_algo_t algo); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; char *pct_expando(const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); aead_algo_t string_to_aead_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); aead_algo_t default_aead_algo(void); int default_compress_algo(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- cpr.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_failure (const char *where, gpg_error_t err); void write_status_text ( int no, const char *text ); void write_status_printf (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void write_status_strings (int no, const char *text, ...) GPGRT_ATTR_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); -void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); +gpg_error_t encrypt_seskey (DEK *dek, DEK **r_seskey, + void **r_enckey, size_t *r_enckeylen); int use_aead (pk_list_t pk_list, int algo); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out); /*-- sign.c --*/ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile); int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr); /*-- sig-check.c --*/ void sig_check_dump_stats (void); /* SIG is a revocation signature. Check if any of PK's designated revokers generated it. If so, return 0. Note: this function (correctly) doesn't care if the designated revoker is revoked. */ int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig); /* Check that the backsig BACKSIG from the subkey SUB_PK to its primary key MAIN_PK is valid. */ int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); /* Check that the signature SIG over a key (e.g., a key binding or a key revocation) is valid. (To check signatures over data, use check_signature.) */ int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig, int *is_selfsig ); /* Like check_key_signature, but with the ability to specify some additional parameters and get back additional information. See the documentation for the implementation for details. */ int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired); /* Returns whether SIGNER generated the signature SIG over the packet PACKET, which is a key, subkey or uid, and comes from the key block KB. If SIGNER is NULL, it is looked up based on the information in SIG. If not NULL, sets *IS_SELFSIG to indicate whether the signature is a self-signature and *RET_PK to a copy of the signer's key. */ gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, PKT_signature *sig, KBNODE kb, PACKET *packet, int *is_selfsig, PKT_public_key *ret_pk); /*-- delkey.c --*/ gpg_error_t delete_keys (ctrl_t ctrl, strlist_t names, int secret, int allow_both); /*-- keygen.c --*/ const char *get_default_pubkey_algo (void); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); unsigned int ask_key_flags (int algo, int subkey, unsigned int current); void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr); void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno); #endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ struct import_stats_s; typedef struct import_stats_s *import_stats_t; struct import_filter_s; typedef struct import_filter_s *import_filter_t; typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_import_filter (const char *string); import_filter_t save_and_clear_import_filter (void); void restore_import_filter (import_filter_t filt); gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock); void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_hd, unsigned int options, int origin, const char *url); gpg_error_t import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); import_stats_t import_new_stats_handle (void); void import_release_stats_handle (import_stats_t hd); void import_print_stats (import_stats_t hd); /* Communication for impex_filter_getval */ struct impex_filter_parm_s { ctrl_t ctrl; kbnode_t node; }; const char *impex_filter_getval (void *cookie, const char *propname); gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ struct export_stats_s; typedef struct export_stats_s *export_stats_t; export_stats_t export_new_stats (void); void export_release_stats (export_stats_t stats); void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_export_filter (const char *string); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk); gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options); gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce); int gen_revoke (ctrl_t ctrl, const char *uname); int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); struct revocation_reason_info * get_default_uid_revocation_reason(void); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode ); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); char *format_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/