diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 50755c0fd..420dbb464 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1,1512 +1,1523 @@ /* cvt-openpgp.c - Convert an OpenPGP key to our internal format. * Copyright (C) 1998-2002, 2006, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 2013, 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include "agent.h" #include "../common/i18n.h" #include "cvt-openpgp.h" #include "../common/host2net.h" /* Helper to pass data via the callback to do_unprotect. */ struct try_do_unprotect_arg_s { int is_v4; int is_protected; int pubkey_algo; const char *curve; int protect_algo; char *iv; int ivlen; int s2k_mode; int s2k_algo; byte *s2k_salt; u32 s2k_count; u16 desired_csum; gcry_mpi_t *skey; size_t skeysize; int skeyidx; gcry_sexp_t *r_key; }; /* Compute the keygrip from the public key and store it at GRIP. */ static gpg_error_t get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey, unsigned char *grip) { gpg_error_t err; gcry_sexp_t s_pkey = NULL; switch (pubkey_algo) { case GCRY_PK_DSA: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", pkey[0], pkey[1], pkey[2], pkey[3]); break; case GCRY_PK_ELG: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", pkey[0], pkey[1], pkey[2]); break; case GCRY_PK_RSA: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); break; case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); else { const char *format; if (!strcmp (curve, "Ed25519")) format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))"; else if (!strcmp (curve, "Curve25519")) format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))"; else format = "(public-key(ecc(curve %s)(q%m)))"; err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]); } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } if (!err && !gcry_pk_get_keygrip (s_pkey, grip)) err = gpg_error (GPG_ERR_INTERNAL); gcry_sexp_release (s_pkey); return err; } /* Convert a secret key given as algorithm id and an array of key parameters into our s-expression based format. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ static gpg_error_t convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, const char *curve) { gpg_error_t err; gcry_sexp_t s_skey = NULL; *r_key = NULL; switch (pubkey_algo) { case GCRY_PK_DSA: err = gcry_sexp_build (&s_skey, NULL, "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", skey[0], skey[1], skey[2], skey[3], skey[4]); break; case GCRY_PK_ELG: case GCRY_PK_ELG_E: err = gcry_sexp_build (&s_skey, NULL, "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", skey[0], skey[1], skey[2], skey[3]); break; case GCRY_PK_RSA: case GCRY_PK_RSA_E: case GCRY_PK_RSA_S: err = gcry_sexp_build (&s_skey, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", skey[0], skey[1], skey[2], skey[3], skey[4], skey[5]); break; case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); else { const char *format; if (!strcmp (curve, "Ed25519")) /* Do not store the OID as name but the real name and the EdDSA flag. */ format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; else if (!strcmp (curve, "Curve25519")) format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))"; else format = "(private-key(ecc(curve %s)(q%m)(d%m)))"; err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]); } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } if (!err) *r_key = s_skey; return err; } /* Convert a secret key given as algorithm id, an array of key parameters, and an S-expression of the original OpenPGP transfer key into our s-expression based format. This is a variant of convert_secret_key which is used for the openpgp-native protection mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ static gpg_error_t convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, const char *curve, gcry_sexp_t transfer_key) { gpg_error_t err; gcry_sexp_t s_skey = NULL; *r_key = NULL; switch (pubkey_algo) { case GCRY_PK_DSA: err = gcry_sexp_build (&s_skey, NULL, "(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)" "(protected openpgp-native%S)))", skey[0], skey[1], skey[2], skey[3], transfer_key); break; case GCRY_PK_ELG: err = gcry_sexp_build (&s_skey, NULL, "(protected-private-key(elg(p%m)(g%m)(y%m)" "(protected openpgp-native%S)))", skey[0], skey[1], skey[2], transfer_key); break; case GCRY_PK_RSA: err = gcry_sexp_build (&s_skey, NULL, "(protected-private-key(rsa(n%m)(e%m)" "(protected openpgp-native%S)))", skey[0], skey[1], transfer_key ); break; case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); else { const char *format; if (!strcmp (curve, "Ed25519")) /* Do not store the OID as name but the real name and the EdDSA flag. */ format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)" "(protected openpgp-native%S)))"; else if (!strcmp (curve, "Curve25519")) format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)" "(protected openpgp-native%S)))"; else format = "(protected-private-key(ecc(curve %s)(q%m)" "(protected openpgp-native%S)))"; err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key); } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } if (!err) *r_key = s_skey; return err; } /* Hash the passphrase and set the key. */ static gpg_error_t hash_passphrase_and_set_key (const char *passphrase, gcry_cipher_hd_t hd, int protect_algo, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count) { gpg_error_t err; unsigned char *key; size_t keylen; keylen = gcry_cipher_get_algo_keylen (protect_algo); if (!keylen) return gpg_error (GPG_ERR_INTERNAL); key = xtrymalloc_secure (keylen); if (!key) return gpg_error_from_syserror (); err = s2k_hash_passphrase (passphrase, s2k_algo, s2k_mode, s2k_salt, s2k_count, key, keylen); if (!err) err = gcry_cipher_setkey (hd, key, keylen); xfree (key); return err; } static u16 checksum (const unsigned char *p, unsigned int n) { u16 a; for (a=0; n; n-- ) a += *p++; return a; } /* Return the number of expected key parameters. */ static void get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey) { switch (pubkey_algo) { case GCRY_PK_RSA: *npkey = 2; *nskey = 6; break; case GCRY_PK_ELG: *npkey = 3; *nskey = 4; break; case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break; case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break; case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break; default: *npkey = 0; *nskey = 0; break; } } /* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number. On success R_NPKEY and R_NSKEY receive the number or parameters for the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of SKEY. */ static int prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, int s2k_mode, unsigned int *r_npkey, unsigned int *r_nskey, unsigned int *r_skeylen) { size_t npkey, nskey, skeylen; int i; /* Count the actual number of MPIs is in the array and set the remainder to NULL for easier processing later on. */ for (skeylen = 0; skey[skeylen]; skeylen++) ; for (i=skeylen; i < skeysize; i++) skey[i] = NULL; /* Check some args. */ if (s2k_mode == 1001) { /* Stub key. */ log_info (_("secret key parts are not available\n")); return gpg_error (GPG_ERR_UNUSABLE_SECKEY); } if (gcry_pk_test_algo (pubkey_algo)) { log_info (_("public key algorithm %d (%s) is not supported\n"), pubkey_algo, gcry_pk_algo_name (pubkey_algo)); return gpg_error (GPG_ERR_PUBKEY_ALGO); } /* Get properties of the public key algorithm and do some consistency checks. Note that we need at least NPKEY+1 elements in the SKEY array. */ get_npkey_nskey (pubkey_algo, &npkey, &nskey); if (!npkey || !nskey || npkey >= nskey) return gpg_error (GPG_ERR_INTERNAL); if (skeylen <= npkey) return gpg_error (GPG_ERR_MISSING_VALUE); if (nskey+1 >= skeysize) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* Check that the public key parameters are all available and not encrypted. */ for (i=0; i < npkey; i++) { if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) return gpg_error (GPG_ERR_BAD_SECKEY); } if (r_npkey) *r_npkey = npkey; if (r_nskey) *r_nskey = nskey; if (r_skeylen) *r_skeylen = skeylen; return 0; } /* Scan octet string in the PGP format (length-in-two-octet octets) */ static int scan_pgp_format (gcry_mpi_t *r_mpi, int pubkey_algo, const unsigned char *buffer, size_t buflen, size_t *r_nbytes) { /* Using gcry_mpi_scan with GCRYMPI_FLAG_PGP can be used if it is MPI, but it will be "normalized" removing leading zeros. */ unsigned int nbits, nbytes; if (pubkey_algo != GCRY_PK_ECC) return gcry_mpi_scan (r_mpi, GCRYMPI_FMT_PGP, buffer, buflen, r_nbytes); /* It's ECC, where we use SOS. */ if (buflen < 2) return GPG_ERR_INV_OBJ; nbits = (buffer[0] << 8) | buffer[1]; if (nbits >= 16384) return GPG_ERR_INV_OBJ; nbytes = (nbits + 7) / 8; if (buflen < nbytes + 2) return GPG_ERR_INV_OBJ; *r_nbytes = nbytes + 2; *r_mpi = gcry_mpi_set_opaque_copy (NULL, buffer+2, nbits); return 0; } /* Note that this function modifies SKEY. SKEYSIZE is the allocated size of the array including the NULL item; this is used for a bounds check. On success a converted key is stored at R_KEY. */ static int do_unprotect (const char *passphrase, int pkt_version, int pubkey_algo, int is_protected, const char *curve, gcry_mpi_t *skey, size_t skeysize, int protect_algo, void *protect_iv, size_t protect_ivlen, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count, u16 desired_csum, gcry_sexp_t *r_key) { gpg_error_t err; unsigned int npkey, nskey, skeylen; gcry_cipher_hd_t cipher_hd = NULL; u16 actual_csum; size_t nbytes; int i; gcry_mpi_t tmpmpi; *r_key = NULL; err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode, &npkey, &nskey, &skeylen); if (err) return err; /* Check whether SKEY is at all protected. If it is not protected merely verify the checksum. */ if (!is_protected) { actual_csum = 0; for (i=npkey; i < nskey; i++) { unsigned char *buffer; if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) return gpg_error (GPG_ERR_BAD_SECKEY); if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; buffer = gcry_mpi_get_opaque (skey[i], &nbits); nbytes = (nbits+7)/8; nbits = nbytes * 8; if (*buffer) if (nbits >= 8 && !(*buffer & 0x80)) if (--nbits >= 7 && !(*buffer & 0x40)) if (--nbits >= 6 && !(*buffer & 0x20)) if (--nbits >= 5 && !(*buffer & 0x10)) if (--nbits >= 4 && !(*buffer & 0x08)) if (--nbits >= 3 && !(*buffer & 0x04)) if (--nbits >= 2 && !(*buffer & 0x02)) if (--nbits >= 1 && !(*buffer & 0x01)) --nbits; actual_csum += (nbits >> 8); actual_csum += (nbits & 0xff); actual_csum += checksum (buffer, nbytes); } else { err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes, skey[i]); if (err) return err; actual_csum += checksum (buffer, nbytes); xfree (buffer); } } if (actual_csum != desired_csum) return gpg_error (GPG_ERR_CHECKSUM); goto do_convert; } if (gcry_cipher_test_algo (protect_algo)) { /* The algorithm numbers are Libgcrypt numbers but fortunately the OpenPGP algorithm numbers map one-to-one to the Libgcrypt numbers. */ log_info (_("protection algorithm %d (%s) is not supported\n"), protect_algo, gnupg_cipher_algo_name (protect_algo)); return gpg_error (GPG_ERR_CIPHER_ALGO); } if (gcry_md_test_algo (s2k_algo)) { log_info (_("protection hash algorithm %d (%s) is not supported\n"), s2k_algo, gcry_md_algo_name (s2k_algo)); return gpg_error (GPG_ERR_DIGEST_ALGO); } err = gcry_cipher_open (&cipher_hd, protect_algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (protect_algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (err) { log_error ("failed to open cipher_algo %d: %s\n", protect_algo, gpg_strerror (err)); return err; } err = hash_passphrase_and_set_key (passphrase, cipher_hd, protect_algo, s2k_mode, s2k_algo, s2k_salt, s2k_count); if (err) { gcry_cipher_close (cipher_hd); return err; } gcry_cipher_setiv (cipher_hd, protect_iv, protect_ivlen); actual_csum = 0; if (pkt_version >= 4) { int ndata; unsigned int ndatabits; const unsigned char *p; unsigned char *data; u16 csum_pgp7 = 0; gcry_mpi_t skey_encrypted = skey[npkey]; if (!gcry_mpi_get_flag (skey_encrypted, GCRYMPI_FLAG_USER1)) { gcry_cipher_close (cipher_hd); return gpg_error (GPG_ERR_BAD_SECKEY); } p = gcry_mpi_get_opaque (skey_encrypted, &ndatabits); ndata = (ndatabits+7)/8; if (ndata > 1) csum_pgp7 = buf16_to_u16 (p+ndata-2); data = xtrymalloc_secure (ndata); if (!data) { err = gpg_error_from_syserror (); gcry_cipher_close (cipher_hd); return err; } gcry_cipher_decrypt (cipher_hd, data, ndata, p, ndata); p = data; if (is_protected == 2) { /* This is the new SHA1 checksum method to detect tampering with the key as used by the Klima/Rosa attack. */ desired_csum = 0; actual_csum = 1; /* Default to bad checksum. */ if (ndata < 20) log_error ("not enough bytes for SHA-1 checksum\n"); else { gcry_md_hd_t h; if (gcry_md_open (&h, GCRY_MD_SHA1, 1)) BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, GCRY_MD_SHA1), data+ndata-20, 20)) actual_csum = 0; /* Digest does match. */ gcry_md_close (h); } } else { /* Old 16 bit checksum method. */ if (ndata < 2) { log_error ("not enough bytes for checksum\n"); desired_csum = 0; actual_csum = 1; /* Mark checksum bad. */ } else { desired_csum = buf16_to_u16 (data+ndata-2); actual_csum = checksum (data, ndata-2); if (desired_csum != actual_csum) { /* This is a PGP 7.0.0 workaround */ desired_csum = csum_pgp7; /* Take the encrypted one. */ } } } /* Better check it here. Otherwise the gcry_mpi_scan would fail because the length may have an arbitrary value. */ if (desired_csum == actual_csum) { for (i = npkey; i < nskey; i++) { if (scan_pgp_format (&tmpmpi, pubkey_algo, p, ndata, &nbytes)) break; skey[i] = tmpmpi; ndata -= nbytes; p += nbytes; } if (i == nskey) { skey[nskey] = NULL; skeylen = nskey; gcry_mpi_release (skey_encrypted); log_assert (skeylen <= skeysize); /* Note: at this point NDATA should be 2 for a simple checksum or 20 for the sha1 digest. */ } else { /* Checksum was okay, but not correctly decrypted. */ desired_csum = 0; actual_csum = 1; /* Mark checksum bad. */ /* Recover encrypted SKEY. */ for (--i; i >= npkey; i--) { gcry_mpi_release (skey[i]); skey[i] = NULL; } skey[npkey] = skey_encrypted; } } xfree(data); } else /* Packet version <= 3. */ { unsigned char *buffer; gcry_mpi_t skey_tmpmpi[10]; log_assert (nskey - npkey <= 10); for (i = npkey; i < nskey; i++) { const unsigned char *p; size_t ndata; unsigned int ndatabits; if (!skey[i] || !gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) { gcry_cipher_close (cipher_hd); return gpg_error (GPG_ERR_BAD_SECKEY); } p = gcry_mpi_get_opaque (skey[i], &ndatabits); ndata = (ndatabits+7)/8; if (!(ndata >= 2) || !(ndata == (buf16_to_ushort (p) + 7)/8 + 2)) { gcry_cipher_close (cipher_hd); return gpg_error (GPG_ERR_BAD_SECKEY); } buffer = xtrymalloc_secure (ndata); if (!buffer) { err = gpg_error_from_syserror (); gcry_cipher_close (cipher_hd); return err; } gcry_cipher_sync (cipher_hd); buffer[0] = p[0]; buffer[1] = p[1]; gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2); actual_csum += checksum (buffer, ndata); err = scan_pgp_format (&tmpmpi, pubkey_algo, buffer, ndata, &nbytes); xfree (buffer); if (err) break; skey_tmpmpi[i - npkey] = tmpmpi; } if (i == nskey) { for (i = npkey; i < nskey; i++) { gcry_mpi_release (skey[i]); skey[i] = skey_tmpmpi[i - npkey]; } } else { /* Checksum was okay, but not correctly decrypted. */ desired_csum = 0; actual_csum = 1; /* Mark checksum bad. */ for (--i; i >= npkey; i--) gcry_mpi_release (skey_tmpmpi[i - npkey]); } } gcry_cipher_close (cipher_hd); /* Now let's see whether we have used the correct passphrase. */ if (actual_csum != desired_csum) return gpg_error (GPG_ERR_BAD_PASSPHRASE); do_convert: if (nskey != skeylen) err = gpg_error (GPG_ERR_BAD_SECKEY); else err = convert_secret_key (r_key, pubkey_algo, skey, curve); if (err) return err; /* The checksum may fail, thus we also check the key itself. */ err = gcry_pk_testkey (*r_key); if (err) { gcry_sexp_release (*r_key); *r_key = NULL; return gpg_error (GPG_ERR_BAD_PASSPHRASE); } return 0; } /* Callback function to try the unprotection from the passphrase query code. */ static gpg_error_t try_do_unprotect_cb (struct pin_entry_info_s *pi) { gpg_error_t err; struct try_do_unprotect_arg_s *arg = pi->check_cb_arg; err = do_unprotect (pi->pin, arg->is_v4? 4:3, arg->pubkey_algo, arg->is_protected, arg->curve, arg->skey, arg->skeysize, arg->protect_algo, arg->iv, arg->ivlen, arg->s2k_mode, arg->s2k_algo, arg->s2k_salt, arg->s2k_count, arg->desired_csum, arg->r_key); /* SKEY may be modified now, thus we need to re-compute SKEYIDX. */ for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize && arg->skey[arg->skeyidx]); arg->skeyidx++) ; return err; } /* See convert_from_openpgp for the core of the description. This function adds an optional PASSPHRASE argument and uses this to silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be NULL in this mode. */ static gpg_error_t convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, unsigned char *grip, const char *prompt, const char *cache_nonce, const char *passphrase, unsigned char **r_key, char **r_passphrase) { gpg_error_t err; int unattended; int from_native; gcry_sexp_t top_list; gcry_sexp_t list = NULL; const char *value; size_t valuelen; char *string; int idx; int is_v4, is_protected; int pubkey_algo; int protect_algo = 0; char iv[16]; int ivlen = 0; int s2k_mode = 0; int s2k_algo = 0; byte s2k_salt[8]; u32 s2k_count = 0; size_t npkey, nskey; gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ char *curve = NULL; u16 desired_csum; int skeyidx = 0; gcry_sexp_t s_skey = NULL; *r_key = NULL; if (r_passphrase) *r_passphrase = NULL; unattended = !r_passphrase; from_native = (!cache_nonce && passphrase && !r_passphrase); top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0); if (!top_list) goto bad_seckey; list = gcry_sexp_find_token (top_list, "version", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4' || value[0] == '5')) goto bad_seckey; is_v4 = (value[0] == '4' || value[0] == '5'); gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "protection", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value) goto bad_seckey; if (valuelen == 4 && !memcmp (value, "sha1", 4)) is_protected = 2; else if (valuelen == 3 && !memcmp (value, "sum", 3)) is_protected = 1; else if (valuelen == 4 && !memcmp (value, "none", 4)) is_protected = 0; else goto bad_seckey; if (is_protected) { string = gcry_sexp_nth_string (list, 2); if (!string) goto bad_seckey; protect_algo = gcry_cipher_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 3, &valuelen); if (!value || !valuelen || valuelen > sizeof iv) goto bad_seckey; memcpy (iv, value, valuelen); ivlen = valuelen; string = gcry_sexp_nth_string (list, 4); if (!string) goto bad_seckey; s2k_mode = strtol (string, NULL, 10); xfree (string); string = gcry_sexp_nth_string (list, 5); if (!string) goto bad_seckey; s2k_algo = gcry_md_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 6, &valuelen); if (!value || !valuelen || valuelen > sizeof s2k_salt) goto bad_seckey; memcpy (s2k_salt, value, valuelen); string = gcry_sexp_nth_string (list, 7); if (!string) goto bad_seckey; s2k_count = strtoul (string, NULL, 10); xfree (string); } gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "algo", 0); if (!list) goto bad_seckey; string = gcry_sexp_nth_string (list, 1); if (!string) goto bad_seckey; pubkey_algo = gcry_pk_map_name (string); xfree (string); get_npkey_nskey (pubkey_algo, &npkey, &nskey); if (!npkey || !nskey || npkey >= nskey) goto bad_seckey; if (npkey == 1) /* This is ECC */ { gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "curve", 0); if (!list) goto bad_seckey; curve = gcry_sexp_nth_string (list, 1); if (!curve) goto bad_seckey; } gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "skey", 0); if (!list) goto bad_seckey; for (idx=0;;) { int is_enc; value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value && skeyidx >= npkey) break; /* Ready. */ /* Check for too many parameters. Note that depending on the protection mode and version number we may see less than NSKEY (but at least NPKEY+1) parameters. */ if (idx >= 2*nskey) goto bad_seckey; if (skeyidx >= DIM (skey)-1) goto bad_seckey; if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e')) goto bad_seckey; is_enc = (value[0] == 'e'); value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value || !valuelen) goto bad_seckey; if (is_enc || npkey == 1 /* This is ECC */) { skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8); if (!skey[skeyidx]) goto outofmem; if (is_enc) /* Encrypted parameters need to have a USER1 flag. */ gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1); } else { if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD, value, valuelen, NULL)) goto bad_seckey; } skeyidx++; } skey[skeyidx++] = NULL; gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "csum", 0); if (list) { string = gcry_sexp_nth_string (list, 1); if (!string) goto bad_seckey; desired_csum = strtoul (string, NULL, 10); xfree (string); } else desired_csum = 0; gcry_sexp_release (list); list = NULL; gcry_sexp_release (top_list); top_list = NULL; #if 0 log_debug ("XXX is v4_or_later=%d\n", is_v4); log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); log_debug ("XXX is_protected=%d\n", is_protected); log_debug ("XXX protect_algo=%d\n", protect_algo); log_printhex (iv, ivlen, "XXX iv"); log_debug ("XXX ivlen=%d\n", ivlen); log_debug ("XXX s2k_mode=%d\n", s2k_mode); log_debug ("XXX s2k_algo=%d\n", s2k_algo); log_printhex (s2k_salt, sizeof s2k_salt, "XXX s2k_salt"); log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); log_debug ("XXX curve='%s'\n", curve); for (idx=0; skey[idx]; idx++) gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1) ? "skey(e)" : "skey(_)", skey[idx]); #endif /*0*/ err = get_keygrip (pubkey_algo, curve, skey, grip); if (err) goto leave; if (!dontcare_exist && !from_native && !agent_key_available (ctrl, grip)) { err = gpg_error (GPG_ERR_EEXIST); goto leave; } if (unattended && !from_native) { err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode, NULL, NULL, NULL); if (err) goto leave; err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp); if (err) goto leave; } else { struct pin_entry_info_s *pi; struct try_do_unprotect_arg_s pi_arg; pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1); if (!pi) { err = gpg_error_from_syserror (); goto leave; } pi->max_length = MAX_PASSPHRASE_LEN + 1; pi->min_digits = 0; /* We want a real passphrase. */ pi->max_digits = 16; pi->max_tries = 3; pi->check_cb = try_do_unprotect_cb; pi->check_cb_arg = &pi_arg; pi_arg.is_v4 = is_v4; pi_arg.is_protected = is_protected; pi_arg.pubkey_algo = pubkey_algo; pi_arg.curve = curve; pi_arg.protect_algo = protect_algo; pi_arg.iv = iv; pi_arg.ivlen = ivlen; pi_arg.s2k_mode = s2k_mode; pi_arg.s2k_algo = s2k_algo; pi_arg.s2k_salt = s2k_salt; pi_arg.s2k_count = s2k_count; pi_arg.desired_csum = desired_csum; pi_arg.skey = skey; pi_arg.skeysize = DIM (skey); pi_arg.skeyidx = skeyidx; pi_arg.r_key = &s_skey; err = gpg_error (GPG_ERR_BAD_PASSPHRASE); if (!is_protected) { err = try_do_unprotect_cb (pi); if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) err = gpg_error (GPG_ERR_BAD_SECKEY); } else if (cache_nonce) { char *cache_value; cache_value = agent_get_cache (ctrl, cache_nonce, CACHE_MODE_NONCE); if (cache_value) { if (strlen (cache_value) < pi->max_length) strcpy (pi->pin, cache_value); xfree (cache_value); } if (*pi->pin) err = try_do_unprotect_cb (pi); } else if (from_native) { if (strlen (passphrase) < pi->max_length) strcpy (pi->pin, passphrase); err = try_do_unprotect_cb (pi); } if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native) err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL, 0); skeyidx = pi_arg.skeyidx; if (!err && r_passphrase && is_protected) { *r_passphrase = xtrystrdup (pi->pin); if (!*r_passphrase) err = gpg_error_from_syserror (); } xfree (pi); if (err) goto leave; } /* Save some memory and get rid of the SKEY array now. */ for (idx=0; idx < skeyidx; idx++) gcry_mpi_release (skey[idx]); skeyidx = 0; /* Note that the padding is not required - we use it only because that function allows us to create the result in secure memory. */ err = make_canon_sexp_pad (s_skey, 1, r_key, NULL); leave: xfree (curve); gcry_sexp_release (s_skey); gcry_sexp_release (list); gcry_sexp_release (top_list); for (idx=0; idx < skeyidx; idx++) gcry_mpi_release (skey[idx]); if (err && r_passphrase) { xfree (*r_passphrase); *r_passphrase = NULL; } return err; bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; outofmem: err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* Convert an OpenPGP transfer key into our internal format. Before asking for a passphrase we check whether the key already exists in our key storage. S_PGP is the OpenPGP key in transfer format. If CACHE_NONCE is given the passphrase will be looked up in the cache. On success R_KEY will receive a canonical encoded S-expression with the unprotected key in our internal format; the caller needs to release that memory. The passphrase used to decrypt the OpenPGP key will be returned at R_PASSPHRASE; the caller must release this passphrase. If R_PASSPHRASE is NULL the unattended conversion mode will be used which uses the openpgp-native protection format for the key. The keygrip will be stored at the 20 byte buffer pointed to by GRIP. On error NULL is stored at all return arguments. */ gpg_error_t convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, unsigned char *grip, const char *prompt, const char *cache_nonce, unsigned char **r_key, char **r_passphrase) { return convert_from_openpgp_main (ctrl, s_pgp, dontcare_exist, grip, prompt, cache_nonce, NULL, r_key, r_passphrase); } /* This function is called by agent_unprotect to re-protect an openpgp-native protected private-key into the standard private-key protection format. */ gpg_error_t convert_from_openpgp_native (ctrl_t ctrl, gcry_sexp_t s_pgp, const char *passphrase, unsigned char **r_key) { gpg_error_t err; unsigned char grip[20]; if (!passphrase) return gpg_error (GPG_ERR_INTERNAL); err = convert_from_openpgp_main (ctrl, s_pgp, 0, grip, NULL, NULL, passphrase, r_key, NULL); /* On success try to re-write the key. */ if (!err) { if (*passphrase) { unsigned char *protectedkey = NULL; size_t protectedkeylen; if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen, ctrl->s2k_count)) agent_write_private_key (ctrl, grip, protectedkey, protectedkeylen, 1, NULL, NULL, NULL, 0); xfree (protectedkey); } else { /* Empty passphrase: write key without protection. */ agent_write_private_key (ctrl, grip, *r_key, gcry_sexp_canon_len (*r_key, 0, NULL,NULL), 1, NULL, NULL, NULL, 0); } } return err; } /* Given an ARRAY of mpis with the key parameters, protect the secret parameters in that array and replace them by one opaque encoded mpi. NPKEY is the number of public key parameters and NSKEY is the number of secret key parameters (including the public ones). On success the array will have NPKEY+1 elements. */ static gpg_error_t apply_protection (gcry_mpi_t *array, int npkey, int nskey, const char *passphrase, int protect_algo, void *protect_iv, size_t protect_ivlen, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count) { gpg_error_t err; int i, j; gcry_cipher_hd_t cipherhd; unsigned char *bufarr[10]; size_t narr[10]; unsigned int nbits[10]; int ndata; unsigned char *p, *data; log_assert (npkey < nskey); log_assert (nskey < DIM (bufarr)); /* Collect only the secret key parameters into BUFARR et al and compute the required size of the data buffer. */ ndata = 20; /* Space for the SHA-1 checksum. */ for (i = npkey, j = 0; i < nskey; i++, j++ ) { if (gcry_mpi_get_flag (array[i], GCRYMPI_FLAG_OPAQUE)) { p = gcry_mpi_get_opaque (array[i], &nbits[j]); narr[j] = (nbits[j] + 7)/8; data = xtrymalloc_secure (narr[j]); if (!data) err = gpg_error_from_syserror (); else { memcpy (data, p, narr[j]); bufarr[j] = data; err = 0; } } else { err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]); nbits[j] = gcry_mpi_get_nbits (array[i]); } if (err) { for (i = 0; i < j; i++) xfree (bufarr[i]); return err; } ndata += 2 + narr[j]; } /* Allocate data buffer and stuff it with the secret key parameters. */ data = xtrymalloc_secure (ndata); if (!data) { err = gpg_error_from_syserror (); for (i = 0; i < (nskey-npkey); i++ ) xfree (bufarr[i]); return err; } p = data; for (i = 0; i < (nskey-npkey); i++ ) { *p++ = nbits[i] >> 8 ; *p++ = nbits[i]; memcpy (p, bufarr[i], narr[i]); p += narr[i]; xfree (bufarr[i]); bufarr[i] = NULL; } log_assert (p == data + ndata - 20); /* Append a hash of the secret key parameters. */ gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20); /* Encrypt it. */ err = gcry_cipher_open (&cipherhd, protect_algo, GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE); if (!err) err = hash_passphrase_and_set_key (passphrase, cipherhd, protect_algo, s2k_mode, s2k_algo, s2k_salt, s2k_count); if (!err) err = gcry_cipher_setiv (cipherhd, protect_iv, protect_ivlen); if (!err) err = gcry_cipher_encrypt (cipherhd, data, ndata, NULL, 0); gcry_cipher_close (cipherhd); if (err) { xfree (data); return err; } /* Replace the secret key parameters in the array by one opaque value. */ for (i = npkey; i < nskey; i++ ) { gcry_mpi_release (array[i]); array[i] = NULL; } array[npkey] = gcry_mpi_set_opaque (NULL, data, ndata*8); gcry_mpi_set_flag (array[npkey], GCRYMPI_FLAG_USER1); return 0; } /* * Examining S_KEY in S-Expression and extract data. * When REQ_PRIVATE_KEY_DATA == 1, S_KEY's CAR should be 'private-key', * but it also allows shadowed or protected versions. * On success, it returns 0, otherwise error number. * R_ALGONAME is static string which is no need to free by caller. * R_NPKEY is pointer to number of public key data. * R_NSKEY is pointer to number of private key data. * R_ELEMS is static string which is no need to free by caller. * ARRAY contains public and private key data. * ARRAYSIZE is the allocated size of the array for cross-checking. * R_CURVE is pointer to S-Expression of the curve (can be NULL). * R_FLAGS is pointer to S-Expression of the flags (can be NULL). */ gpg_error_t extract_private_key (gcry_sexp_t s_key, int req_private_key_data, const char **r_algoname, int *r_npkey, int *r_nskey, const char **r_elems, gcry_mpi_t *array, int arraysize, gcry_sexp_t *r_curve, gcry_sexp_t *r_flags) { gpg_error_t err; gcry_sexp_t list, l2; char *name; const char *algoname, *format, *elems; int npkey, nskey; gcry_sexp_t curve = NULL; gcry_sexp_t flags = NULL; *r_curve = NULL; *r_flags = NULL; if (!req_private_key_data) { list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_key, "protected-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_key, "private-key", 0 ); } else list = gcry_sexp_find_token (s_key, "private-key", 0); if (!list) { log_error ("invalid private key format\n"); return gpg_error (GPG_ERR_BAD_SECKEY); } l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_string (list, 0); if (!name) { gcry_sexp_release (list); return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */ } if (arraysize < 7) BUG (); /* Map NAME to a name as used by Libgcrypt. We do not use the Libgcrypt function here because we need a lowercase name and require special treatment for some algorithms. */ strlwr (name); if (!strcmp (name, "rsa")) { algoname = "rsa"; format = elems = "ned?p?q?u?"; npkey = 2; nskey = 6; err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, array+2, array+3, array+4, array+5, NULL); } else if (!strcmp (name, "elg")) { algoname = "elg"; format = elems = "pgyx?"; npkey = 3; nskey = 4; err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, array+2, array+3, NULL); } else if (!strcmp (name, "dsa")) { algoname = "dsa"; format = elems = "pqgyx?"; npkey = 4; nskey = 5; err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, array+2, array+3, array+4, NULL); } else if (!strcmp (name, "ecc") || !strcmp (name, "ecdsa")) { algoname = "ecc"; format = "/qd?"; elems = "qd?"; npkey = 1; nskey = 2; curve = gcry_sexp_find_token (list, "curve", 0); flags = gcry_sexp_find_token (list, "flags", 0); err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, NULL); } + else if ( !strcmp (name, (algoname = "kyber512")) + || !strcmp (name, (algoname = "kyber768")) + || !strcmp (name, (algoname = "kyber1024"))) + { + format = "/ps?"; + elems = "ps?"; + npkey = 1; + nskey = 2; + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, NULL); + } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); } xfree (name); gcry_sexp_release (list); if (err) { gcry_sexp_release (curve); gcry_sexp_release (flags); return err; } else { *r_algoname = algoname; if (r_elems) *r_elems = elems; *r_npkey = npkey; if (r_nskey) *r_nskey = nskey; *r_curve = curve; *r_flags = flags; return 0; } } /* Convert our key S_KEY into an OpenPGP key transfer format. On success a canonical encoded S-expression is stored at R_TRANSFERKEY and its length at R_TRANSFERKEYLEN; this S-expression is also padded to a multiple of 64 bits. */ gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, unsigned char **r_transferkey, size_t *r_transferkeylen) { gpg_error_t err; const char *algoname; int npkey, nskey; gcry_mpi_t array[10]; gcry_sexp_t curve = NULL; gcry_sexp_t flags = NULL; char protect_iv[16]; char salt[8]; unsigned long s2k_count; int i, j; (void)ctrl; *r_transferkey = NULL; for (i=0; i < DIM (array); i++) array[i] = NULL; err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL, array, DIM (array), &curve, &flags); if (err) return err; gcry_create_nonce (protect_iv, sizeof protect_iv); gcry_create_nonce (salt, sizeof salt); /* We need to use the encoded S2k count. It is not possible to encode it after it has been used because the encoding procedure may round the value up. */ s2k_count = get_standard_s2k_count_rfc4880 (); err = apply_protection (array, npkey, nskey, passphrase, GCRY_CIPHER_AES, protect_iv, sizeof protect_iv, 3, GCRY_MD_SHA1, salt, s2k_count); /* Turn it into the transfer key S-expression. Note that we always return a protected key. */ if (!err) { char countbuf[35]; membuf_t mbuf; void *format_args[10+2]; gcry_sexp_t tmpkey; gcry_sexp_t tmpsexp = NULL; snprintf (countbuf, sizeof countbuf, "%lu", s2k_count); init_membuf (&mbuf, 50); put_membuf_str (&mbuf, "(skey"); for (i=j=0; i < npkey; i++) { put_membuf_str (&mbuf, " _ %m"); format_args[j++] = array + i; } put_membuf_str (&mbuf, " e %m"); format_args[j++] = array + npkey; put_membuf_str (&mbuf, ")\n"); put_membuf (&mbuf, "", 1); tmpkey = NULL; { char *format = get_membuf (&mbuf, NULL); if (!format) err = gpg_error_from_syserror (); else err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args); xfree (format); } if (!err) err = gcry_sexp_build (&tmpsexp, NULL, "(openpgp-private-key\n" " (version 1:4)\n" " (algo %s)\n" " %S%S\n" " (protection sha1 aes %b 1:3 sha1 %b %s))\n", algoname, curve, tmpkey, (int)sizeof protect_iv, protect_iv, (int)sizeof salt, salt, countbuf); gcry_sexp_release (tmpkey); if (!err) err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen); gcry_sexp_release (tmpsexp); } for (i=0; i < DIM (array); i++) gcry_mpi_release (array[i]); gcry_sexp_release (curve); gcry_sexp_release (flags); return err; } diff --git a/agent/divert-scd.c b/agent/divert-scd.c index ed0173ea1..4a2bebffa 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -1,501 +1,501 @@ /* divert-scd.c - divert operations to the scdaemon * Copyright (C) 2002, 2003, 2009 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 . */ #include #include #include #include #include #include #include #include #include #include "agent.h" #include "../common/i18n.h" #include "../common/sexp-parse.h" /* Put the DIGEST into an DER encoded container and return it in R_VAL. */ static int encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo, unsigned char **r_val, size_t *r_len) { unsigned char *frame; unsigned char asn[100]; size_t asnlen; *r_val = NULL; *r_len = 0; asnlen = DIM(asn); if (!algo || gcry_md_test_algo (algo)) return gpg_error (GPG_ERR_DIGEST_ALGO); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } frame = xtrymalloc (asnlen + digestlen); if (!frame) return out_of_core (); memcpy (frame, asn, asnlen); memcpy (frame+asnlen, digest, digestlen); if (DBG_CRYPTO) log_printhex (frame, asnlen+digestlen, "encoded hash:"); *r_val = frame; *r_len = asnlen+digestlen; return 0; } /* Return true if STRING ends in "%0A". */ static int has_percent0A_suffix (const char *string) { size_t n; return (string && (n = strlen (string)) >= 3 && !strcmp (string + n - 3, "%0A")); } /* Callback used to ask for the PIN which should be set into BUF. The buf has been allocated by the caller and is of size MAXBUF which includes the terminating null. The function should return an UTF-8 string with the passphrase, the buffer may optionally be padded with arbitrary characters. If DESC_TEXT is not NULL it can be used as further information shown atop of the INFO message. INFO gets displayed as part of a generic string. However if the first character of INFO is a vertical bar all up to the next verical bar are considered flags and only everything after the second vertical bar gets displayed as the full prompt. Flags: 'N' = New PIN, this requests a second prompt to repeat the PIN. If the PIN is not correctly repeated it starts from all over. 'A' = The PIN is an Admin PIN, SO-PIN or alike. 'P' = The PIN is a PUK (Personal Unblocking Key). 'R' = The PIN is a Reset Code. Example: "|AN|Please enter the new security officer's PIN" The text "Please ..." will get displayed and the flags 'A' and 'N' are considered. */ static int getpin_cb (void *opaque, const char *desc_text, const char *info, char *buf, size_t maxbuf) { struct pin_entry_info_s *pi; int rc; ctrl_t ctrl = opaque; const char *ends, *s; int any_flags = 0; int newpin = 0; int resetcode = 0; int is_puk = 0; const char *again_text = NULL; const char *prompt = "PIN"; if (buf && maxbuf < 2) return gpg_error (GPG_ERR_INV_VALUE); /* Parse the flags. */ if (info && *info =='|' && (ends=strchr (info+1, '|'))) { for (s=info+1; s < ends; s++) { if (*s == 'A') prompt = L_("Admin PIN"); else if (*s == 'P') { /* TRANSLATORS: A PUK is the Personal Unblocking Code used to unblock a PIN. */ prompt = L_("PUK"); is_puk = 1; } else if (*s == 'N') newpin = 1; else if (*s == 'R') { prompt = L_("Reset Code"); resetcode = 1; } } info = ends+1; any_flags = 1; } else if (info && *info == '|') log_debug ("pin_cb called without proper PIN info hack\n"); /* If BUF has been passed as NULL, we are in pinpad mode: The callback opens the popup and immediately returns. */ if (!buf) { if (maxbuf == 0) /* Close the pinentry. */ { agent_popup_message_stop (ctrl); rc = 0; } else if (maxbuf == 1) /* Open the pinentry. */ { if (info) { char *desc; const char *desc2; if (!strcmp (info, "--ack")) { desc2 = L_("Push ACK button on card/token."); if (desc_text) { desc = strconcat (desc_text, has_percent0A_suffix (desc_text) ? "%0A" : "%0A%0A", desc2, NULL); desc2 = NULL; } else desc = NULL; } else { desc2 = NULL; if (desc_text) desc = strconcat (desc_text, has_percent0A_suffix (desc_text) ? "%0A" : "%0A%0A", info, "%0A%0A", L_("Use the reader's pinpad for input."), NULL); else desc = strconcat (info, "%0A%0A", L_("Use the reader's pinpad for input."), NULL); } if (!desc2 && !desc) rc = gpg_error_from_syserror (); else { rc = agent_popup_message_start (ctrl, desc2? desc2:desc, NULL); xfree (desc); } } else rc = agent_popup_message_start (ctrl, desc_text, NULL); } else rc = gpg_error (GPG_ERR_INV_VALUE); return rc; } /* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole mess because we should call the card's verify function from the pinentry check pin CB. */ again: pi = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10); if (!pi) return gpg_error_from_syserror (); pi->max_length = maxbuf-1; pi->min_digits = 0; /* we want a real passphrase */ pi->max_digits = 16; pi->max_tries = 3; if (any_flags) { { char *desc2; if (desc_text) desc2 = strconcat (desc_text, has_percent0A_suffix (desc_text) ? "%0A" : "%0A%0A", info, NULL); else desc2 = NULL; rc = agent_askpin (ctrl, desc2? desc2 : info, prompt, again_text, pi, NULL, 0); xfree (desc2); } again_text = NULL; if (!rc && newpin) { struct pin_entry_info_s *pi2; pi2 = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10); if (!pi2) { rc = gpg_error_from_syserror (); xfree (pi); return rc; } pi2->max_length = maxbuf-1; pi2->min_digits = 0; pi2->max_digits = 16; pi2->max_tries = 1; rc = agent_askpin (ctrl, (resetcode? L_("Repeat this Reset Code"): is_puk? L_("Repeat this PUK"): L_("Repeat this PIN")), prompt, NULL, pi2, NULL, 0); if (!rc && strcmp (pi->pin, pi2->pin)) { again_text = (resetcode? L_("Reset Code not correctly repeated; try again"): is_puk? L_("PUK not correctly repeated; try again"): L_("PIN not correctly repeated; try again")); xfree (pi2); xfree (pi); goto again; } xfree (pi2); } } else { char *desc, *desc2; if ( asprintf (&desc, L_("Please enter the PIN%s%s%s to unlock the card"), info? " (":"", info? info:"", info? ")":"") < 0) desc = NULL; if (desc_text) desc2 = strconcat (desc_text, has_percent0A_suffix (desc_text) ? "%0A" : "%0A%0A", desc, NULL); else desc2 = NULL; rc = agent_askpin (ctrl, desc2? desc2 : desc? desc : info, prompt, NULL, pi, NULL, 0); xfree (desc2); xfree (desc); } if (!rc) { strncpy (buf, pi->pin, maxbuf-1); buf[maxbuf-1] = 0; } xfree (pi); return rc; } /* This function is used when a sign operation has been diverted to a * smartcard. * * Note: If SHADOW_INFO is NULL the user can't be asked to insert the * card, we simply try to use an inserted card with the given keygrip. * * FIXME: Explain the other args. */ int divert_pksign (ctrl_t ctrl, const unsigned char *grip, const unsigned char *digest, size_t digestlen, int algo, unsigned char **r_sig, size_t *r_siglen) { int rc; char hexgrip[41]; size_t siglen; unsigned char *sigval = NULL; bin2hex (grip, 20, hexgrip); if (!algo) { /* This is the PureEdDSA case. (DIGEST,DIGESTLEN) this the * entire data which will be signed. */ rc = agent_card_pksign (ctrl, hexgrip, getpin_cb, ctrl, NULL, 0, digest, digestlen, &sigval, &siglen); } else if (algo == MD_USER_TLS_MD5SHA1) { int save = ctrl->use_auth_call; ctrl->use_auth_call = 1; rc = agent_card_pksign (ctrl, hexgrip, getpin_cb, ctrl, NULL, algo, digest, digestlen, &sigval, &siglen); ctrl->use_auth_call = save; } else { unsigned char *data; size_t ndata; rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata); if (!rc) { rc = agent_card_pksign (ctrl, hexgrip, getpin_cb, ctrl, NULL, algo, data, ndata, &sigval, &siglen); xfree (data); } } if (!rc) { *r_sig = sigval; *r_siglen = siglen; } return rc; } -/* Decrypt the value given asn an S-expression in CIPHER using the +/* Decrypt the value given as an s-expression in CIPHER using the key identified by SHADOW_INFO and return the plaintext in an allocated buffer in R_BUF. The padding information is stored at R_PADDING with -1 for not known. */ int divert_pkdecrypt (ctrl_t ctrl, const unsigned char *grip, const unsigned char *cipher, char **r_buf, size_t *r_len, int *r_padding) { int rc; char hexgrip[41]; const unsigned char *s; size_t n; int depth; const unsigned char *ciphertext; size_t ciphertextlen; char *plaintext; size_t plaintextlen; bin2hex (grip, 20, hexgrip); *r_padding = -1; s = cipher; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); if (!smatch (&s, n, "enc-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s != '(') return gpg_error (GPG_ERR_UNKNOWN_SEXP); s++; n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); /* First check whether we have a flags parameter and skip it. */ if (smatch (&s, n, "flags")) { depth = 1; if (sskip (&s, &depth) || depth) return gpg_error (GPG_ERR_INV_SEXP); if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); } if (smatch (&s, n, "rsa")) { if (*s != '(') return gpg_error (GPG_ERR_UNKNOWN_SEXP); s++; n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); if (!smatch (&s, n, "a")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); } else if (smatch (&s, n, "ecdh")) { if (*s != '(') return gpg_error (GPG_ERR_UNKNOWN_SEXP); s++; n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); if (smatch (&s, n, "s")) { n = snext (&s); s += n; if (*s++ != ')') return gpg_error (GPG_ERR_INV_SEXP); if (*s++ != '(') return gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (!n) return gpg_error (GPG_ERR_INV_SEXP); } if (!smatch (&s, n, "e")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); } else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (!n) return gpg_error (GPG_ERR_UNKNOWN_SEXP); ciphertext = s; ciphertextlen = n; rc = agent_card_pkdecrypt (ctrl, hexgrip, getpin_cb, ctrl, NULL, ciphertext, ciphertextlen, &plaintext, &plaintextlen, r_padding); if (!rc) { *r_buf = plaintext; *r_len = plaintextlen; } return rc; } gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno, const char *keyref, const char *keydata, size_t keydatalen) { return agent_card_writekey (ctrl, force, serialno, keyref, keydata, keydatalen, getpin_cb, ctrl); } int divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context) { return agent_card_scd (ctrl, cmdline, getpin_cb, ctrl, assuan_context); } diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index ceb211dd3..bc82cc6b0 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -1,751 +1,752 @@ /* openpgp-oids.c - OID helper for OpenPGP * Copyright (C) 2011 Free Software Foundation, Inc. * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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 "util.h" #include "openpgpdefs.h" /* A table with all our supported OpenPGP curves. */ static struct { const char *name; /* Standard name. */ const char *oidstr; /* IETF formatted OID. */ unsigned int nbits; /* Nominal bit length of the curve. */ const char *alias; /* NULL or alternative name of the curve. */ const char *abbr; /* NULL or abbreviated name of the curve. */ int pubkey_algo; /* Required OpenPGP algo or 0 for ECDSA/ECDH. */ } oidtable[] = { { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", NULL, PUBKEY_ALGO_ECDH }, { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", NULL, PUBKEY_ALGO_EDDSA }, { "Curve25519", "1.3.101.110", 255, "cv25519", NULL, PUBKEY_ALGO_ECDH }, { "Ed25519", "1.3.101.112", 255, "ed25519", NULL, PUBKEY_ALGO_EDDSA }, { "X448", "1.3.101.111", 448, "cv448", NULL, PUBKEY_ALGO_ECDH }, { "Ed448", "1.3.101.113", 456, "ed448", NULL, PUBKEY_ALGO_EDDSA }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, { "NIST P-521", "1.3.132.0.35", 521, "nistp521" }, { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256, NULL, "bp256" }, { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384, NULL, "bp384" }, { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512, NULL, "bp512" }, { "secp256k1", "1.3.132.0.10", 256 }, { NULL, NULL, 0} }; /* The OID for Curve Ed25519 in OpenPGP format. The shorter v5 * variant may only be used with v5 keys. */ static const char oid_ed25519[] = { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; static const char oid_ed25519_v5[] = { 0x03, 0x2b, 0x65, 0x70 }; /* The OID for Curve25519 in OpenPGP format. The shorter v5 * variant may only be used with v5 keys. */ static const char oid_cv25519[] = { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; static const char oid_cv25519_v5[] = { 0x03, 0x2b, 0x65, 0x6e }; /* The OID for X448 in OpenPGP format. */ /* * Here, we have a little semantic discrepancy. X448 is the name of * the ECDH computation and the OID is assigned to the algorithm in * RFC 8410. Note that this OID is not the one which is assigned to * the curve itself (originally in 8410). Nevertheless, we use "X448" * for the curve in libgcrypt. */ static const char oid_cv448[] = { 0x03, 0x2b, 0x65, 0x6f }; /* The OID for Ed448 in OpenPGP format. */ static const char oid_ed448[] = { 0x03, 0x2b, 0x65, 0x71 }; /* A table to store keyalgo strings like "rsa2048 or "ed25519" so that * we do not need to allocate them. This is currently a simple array * but may eventually be changed to a fast data structure. Noet that * unknown algorithms are stored with (NBITS,CURVE) set to (0,NULL). */ struct keyalgo_string_s { enum gcry_pk_algos algo; /* Mandatory. */ unsigned int nbits; /* Size for classical algos. */ char *curve; /* Curvename (OID) or NULL. */ char *name; /* Allocated name. */ }; static struct keyalgo_string_s *keyalgo_strings; /* The table. */ static size_t keyalgo_strings_size; /* Allocated size. */ static size_t keyalgo_strings_used; /* Used size. */ /* Helper for openpgp_oid_from_str. */ static size_t make_flagged_int (unsigned long value, char *buf, size_t buflen) { int more = 0; int shift; /* fixme: figure out the number of bits in an ulong and start with that value as shift (after making it a multiple of 7) a more straigtforward implementation is to do it in reverse order using a temporary buffer - saves a lot of compares */ for (more=0, shift=28; shift > 0; shift -= 7) { if (more || value >= (1<> shift); value -= (value >> shift) << shift; more = 1; } } buf[buflen++] = value; return buflen; } /* Convert the OID given in dotted decimal form in STRING to an DER * encoding and store it as an opaque value at R_MPI. The format of * the DER encoded is not a regular ASN.1 object but the modified * format as used by OpenPGP for the ECC curve description. On error * the function returns and error code an NULL is stored at R_BUG. * Note that scanning STRING stops at the first white space * character. */ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) { unsigned char *buf; size_t buflen; unsigned long val1, val; const char *endp; int arcno; *r_mpi = NULL; if (!string || !*string) return gpg_error (GPG_ERR_INV_VALUE); /* We can safely assume that the encoded OID is shorter than the string. */ buf = xtrymalloc (1 + strlen (string) + 2); if (!buf) return gpg_error_from_syserror (); /* Save the first byte for the length. */ buflen = 1; val1 = 0; /* Avoid compiler warning. */ arcno = 0; do { arcno++; val = strtoul (string, (char**)&endp, 10); if (!digitp (string) || !(*endp == '.' || !*endp)) { xfree (buf); return gpg_error (GPG_ERR_INV_OID_STRING); } if (*endp == '.') string = endp+1; if (arcno == 1) { if (val > 2) break; /* Not allowed, error caught below. */ val1 = val; } else if (arcno == 2) { /* Need to combine the first two arcs in one octet. */ if (val1 < 2) { if (val > 39) { xfree (buf); return gpg_error (GPG_ERR_INV_OID_STRING); } buf[buflen++] = val1*40 + val; } else { val += 80; buflen = make_flagged_int (val, buf, buflen); } } else { buflen = make_flagged_int (val, buf, buflen); } } while (*endp == '.'); if (arcno == 1 || buflen < 2 || buflen > 254 ) { /* It is not possible to encode only the first arc. */ xfree (buf); return gpg_error (GPG_ERR_INV_OID_STRING); } *buf = buflen - 1; *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); if (!*r_mpi) { xfree (buf); return gpg_error_from_syserror (); } return 0; } /* Return a malloced string representation of the OID in the buffer * (BUF,LEN). In case of an error NULL is returned and ERRNO is set. * As per OpenPGP spec the first byte of the buffer is the length of * the rest; the function performs a consistency check. */ char * openpgp_oidbuf_to_str (const unsigned char *buf, size_t len) { char *string, *p; int n = 0; unsigned long val, valmask; valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); /* The first bytes gives the length; check consistency. */ if (!len || buf[0] != len -1) { gpg_err_set_errno (EINVAL); return NULL; } /* Skip length byte. */ len--; buf++; /* To calculate the length of the string we can safely assume an upper limit of 3 decimal characters per byte. Two extra bytes account for the special first octet */ string = p = xtrymalloc (len*(1+3)+2+1); if (!string) return NULL; if (!len) { *p = 0; return string; } if (buf[0] < 40) p += sprintf (p, "0.%d", buf[n]); else if (buf[0] < 80) p += sprintf (p, "1.%d", buf[n]-40); else { val = buf[n] & 0x7f; while ( (buf[n]&0x80) && ++n < len ) { if ( (val & valmask) ) goto badoid; /* Overflow. */ val <<= 7; val |= buf[n] & 0x7f; } if (val < 80) goto badoid; val -= 80; sprintf (p, "2.%lu", val); p += strlen (p); } for (n++; n < len; n++) { val = buf[n] & 0x7f; while ( (buf[n]&0x80) && ++n < len ) { if ( (val & valmask) ) goto badoid; /* Overflow. */ val <<= 7; val |= buf[n] & 0x7f; } sprintf (p, ".%lu", val); p += strlen (p); } *p = 0; return string; badoid: /* Return a special OID (gnu.gnupg.badoid) to indicate the error case. The OID is broken and thus we return one which can't do any harm. Formally this does not need to be a bad OID but an OID with an arc that can't be represented in a 32 bit word is more than likely corrupt. */ xfree (string); return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); } /* Return a malloced string representation of the OID in the opaque * MPI A. In case of an error NULL is returned and ERRNO is set. */ char * openpgp_oid_to_str (gcry_mpi_t a) { const unsigned char *buf; unsigned int lengthi; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE) || !(buf = gcry_mpi_get_opaque (a, &lengthi))) { gpg_err_set_errno (EINVAL); return NULL; } return openpgp_oidbuf_to_str (buf, (lengthi+7)/8); } /* Return true if (BUF,LEN) represents the OID for Ed25519. */ int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len) { if (!buf) return 0; return ((len == DIM (oid_ed25519) && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))) || (len == DIM (oid_ed25519_v5) && !memcmp (buf, oid_ed25519_v5, DIM (oid_ed25519_v5)))); } /* Return true if A represents the OID for Ed25519. */ int openpgp_oid_is_ed25519 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); return openpgp_oidbuf_is_ed25519 (buf, (nbits+7)/8); } /* Return true if (BUF,LEN) represents the OID for Curve25519. */ int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len) { if (!buf) return 0; return ((len == DIM (oid_cv25519) && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))) || (len == DIM (oid_cv25519_v5) && !memcmp (buf, oid_cv25519_v5, DIM (oid_cv25519_v5)))); } /* Return true if (BUF,LEN) represents the OID for Ed448. */ static int openpgp_oidbuf_is_ed448 (const void *buf, size_t len) { return (buf && len == DIM (oid_ed448) && !memcmp (buf, oid_ed448, DIM (oid_ed448))); } /* Return true if (BUF,LEN) represents the OID for X448. */ static int openpgp_oidbuf_is_cv448 (const void *buf, size_t len) { return (buf && len == DIM (oid_cv448) && !memcmp (buf, oid_cv448, DIM (oid_cv448))); } /* Return true if the MPI A represents the OID for Curve25519. */ int openpgp_oid_is_cv25519 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); return openpgp_oidbuf_is_cv25519 (buf, (nbits+7)/8); } /* Return true if the MPI A represents the OID for Ed448. */ int openpgp_oid_is_ed448 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); return openpgp_oidbuf_is_ed448 (buf, (nbits+7)/8); } /* Return true if the MPI A represents the OID for X448. */ int openpgp_oid_is_cv448 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); return openpgp_oidbuf_is_cv448 (buf, (nbits+7)/8); } /* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL store the bit size of the curve there. Returns NULL for unknown curve names. If R_ALGO is not NULL and a specific ECC algorithm is required for this curve its OpenPGP algorithm number is stored there; otherwise 0 is stored which indicates that ECDSA or ECDH can be used. */ const char * openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo) { int i; unsigned int nbits = 0; const char *oidstr = NULL; int algo = 0; if (name) { for (i=0; oidtable[i].name; i++) if (!ascii_strcasecmp (oidtable[i].name, name) || (oidtable[i].alias && !ascii_strcasecmp (oidtable[i].alias, name))) { oidstr = oidtable[i].oidstr; nbits = oidtable[i].nbits; algo = oidtable[i].pubkey_algo; break; } if (!oidtable[i].name) { /* If not found assume the input is already an OID and check whether we support it. */ for (i=0; oidtable[i].name; i++) if (!ascii_strcasecmp (name, oidtable[i].oidstr)) { oidstr = oidtable[i].oidstr; nbits = oidtable[i].nbits; algo = oidtable[i].pubkey_algo; break; } } } if (r_nbits) *r_nbits = nbits; if (r_algo) *r_algo = algo; return oidstr; } /* Map an OpenPGP OID to the Libgcrypt curve name. Returns NULL for * unknown curve names. MODE defines which version of the curve name * is returned. For example: * * | OID | mode=0 | mode=1 | mode=2 | * |----------------------+-----------------+-----------------+----------| * | 1.2.840.10045.3.1.7 | nistp256 | NIST P-256 | nistp256 | * | 1.3.36.3.3.2.8.1.1.7 | brainpoolP256r1 | brainpoolP256r1 | bp256 | * * Thus mode 0 returns the name as commonly used gpg, mode 1 returns * the canonical name, and mode 2 prefers an abbreviated name over the * commonly used name. */ const char * openpgp_oid_to_curve (const char *oidstr, int mode) { int i; if (!oidstr) return NULL; for (i=0; oidtable[i].name; i++) if (!strcmp (oidtable[i].oidstr, oidstr)) { if (mode == 2) { if (oidtable[i].abbr) return oidtable[i].abbr; mode = 0; /* No abbreviation - fallback to mode 0. */ } return !mode && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; } return NULL; } /* Map an OpenPGP OID, name or alias to the Libgcrypt curve name. * Returns NULL for unknown curve names. Unless CANON is set we * prefer an alias name here which is more suitable for printing. */ const char * openpgp_oid_or_name_to_curve (const char *oidname, int canon) { int i; if (!oidname) return NULL; for (i=0; oidtable[i].name; i++) if (!ascii_strcasecmp (oidtable[i].oidstr, oidname) || !ascii_strcasecmp (oidtable[i].name, oidname) || (oidtable[i].alias && !ascii_strcasecmp (oidtable[i].alias, oidname))) return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; return NULL; } /* Return true if the curve with NAME is supported. */ static int curve_supported_p (const char *name) { int result = 0; gcry_sexp_t keyparms; if (!gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", name)) { result = !!gcry_pk_get_curve (keyparms, 0, NULL); gcry_sexp_release (keyparms); } return result; } /* Enumerate available and supported OpenPGP curves. The caller needs to set the integer variable at ITERP to zero and keep on calling this function until NULL is returned. */ const char * openpgp_enum_curves (int *iterp) { int idx = *iterp; while (idx >= 0 && idx < DIM (oidtable) && oidtable[idx].name) { if (curve_supported_p (oidtable[idx].name)) { *iterp = idx + 1; return oidtable[idx].alias? oidtable[idx].alias : oidtable[idx].name; } idx++; } *iterp = idx; return NULL; } /* Return the Libgcrypt name for the gpg curve NAME if supported. If * R_ALGO is not NULL the required OpenPGP public key algo or 0 is * stored at that address. If R_NBITS is not NULL the nominal bitsize * of the curves is stored there. NULL is returned if the curve is * not supported. */ const char * openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits) { int idx; if (r_algo) *r_algo = 0; if (r_nbits) *r_nbits = 0; for (idx = 0; idx < DIM (oidtable) && oidtable[idx].name; idx++) { if ((!ascii_strcasecmp (name, oidtable[idx].name) || (oidtable[idx].alias && !ascii_strcasecmp (name, (oidtable[idx].alias)))) && curve_supported_p (oidtable[idx].name)) { if (r_algo) *r_algo = oidtable[idx].pubkey_algo; if (r_nbits) *r_nbits = oidtable[idx].nbits; return oidtable[idx].name; } } return NULL; } /* Map a Gcrypt public key algorithm number to the used by OpenPGP. * Returns 0 for unknown gcry algorithm. */ pubkey_algo_t map_gcry_pk_to_openpgp (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_EDDSA: return PUBKEY_ALGO_EDDSA; case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; + case GCRY_PK_KEM: return PUBKEY_ALGO_KYBER; default: return algo < 110 ? (pubkey_algo_t)algo : 0; } } /* Map an OpenPGP public key algorithm number to the one used by * Libgcrypt. Returns 0 for unknown gcry algorithm. */ enum gcry_pk_algos map_openpgp_pk_to_gcry (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_EDDSA: return GCRY_PK_EDDSA; case PUBKEY_ALGO_ECDSA: return GCRY_PK_ECDSA; case PUBKEY_ALGO_ECDH: return GCRY_PK_ECDH; default: return algo < 110 ? (enum gcry_pk_algos)algo : 0; } } /* Return a string describing the public key algorithm and the * keysize. For elliptic curves the function prints the name of the * curve because the keysize is a property of the curve. ALGO is the * Gcrypt algorithm number, CURVE is either NULL or gives the OID of * the curve, NBITS is either 0 or the size for algorithms like RSA. * The returned string is taken from permanent table. Examples * for the output are: * * "rsa3072" - RSA with 3072 bit * "elg1024" - Elgamal with 1024 bit * "ed25519" - ECC using the curve Ed25519. * "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". * "E_1.3.6.1.4.1.11591.2.12242973" - ECC with a bogus OID. * "unknown_N" - Unknown OpenPGP algorithm N. * If N is > 110 this is a gcrypt algo. */ const char * get_keyalgo_string (enum gcry_pk_algos algo, unsigned int nbits, const char *curve) { const char *prefix; int i; char *name, *curvebuf; switch (algo) { case GCRY_PK_RSA: prefix = "rsa"; break; case GCRY_PK_ELG: prefix = "elg"; break; case GCRY_PK_DSA: prefix = "dsa"; break; case GCRY_PK_ECC: case GCRY_PK_ECDH: case GCRY_PK_ECDSA: case GCRY_PK_EDDSA: prefix = ""; break; default: prefix = NULL; break; } if (prefix && *prefix && nbits) { for (i=0; i < keyalgo_strings_used; i++) { if (keyalgo_strings[i].algo == algo && keyalgo_strings[i].nbits && keyalgo_strings[i].nbits == nbits) return keyalgo_strings[i].name; } /* Not yet in the table - add it. */ name = xasprintf ("%s%u", prefix, nbits); nbits = nbits? nbits : 1; /* No nbits - oops - use 1 instead. */ curvebuf = NULL; } else if (prefix && !*prefix) { const char *curvename; for (i=0; i < keyalgo_strings_used; i++) { if (keyalgo_strings[i].algo == algo && keyalgo_strings[i].curve && curve && !ascii_strcasecmp (keyalgo_strings[i].curve, curve)) return keyalgo_strings[i].name; } /* Not yet in the table - add it. */ curvename = openpgp_oid_or_name_to_curve (curve, 0); if (curvename) name = xasprintf ("%s", curvename); else if (curve) name = xasprintf ("E_%s", curve); else name = xasprintf ("E_error"); nbits = 0; curvebuf = curve? xstrdup (curve) : NULL; } else { for (i=0; i < keyalgo_strings_used; i++) { if (keyalgo_strings[i].algo == algo && !keyalgo_strings[i].nbits && !keyalgo_strings[i].curve) return keyalgo_strings[i].name; } /* Not yet in the table - add it. */ name = xasprintf ("unknown_%u", (unsigned int)algo); nbits = 0; curvebuf = NULL; } /* Store a new entry. This is a loop because of a possible nPth * thread switch during xrealloc. */ while (keyalgo_strings_used >= keyalgo_strings_size) { keyalgo_strings_size += 10; if (keyalgo_strings_size > 1024*1024) log_fatal ("%s: table getting too large - possible DoS\n", __func__); keyalgo_strings = xrealloc (keyalgo_strings, (keyalgo_strings_size * sizeof *keyalgo_strings)); } keyalgo_strings[keyalgo_strings_used].algo = algo; keyalgo_strings[keyalgo_strings_used].nbits = nbits; keyalgo_strings[keyalgo_strings_used].curve = curvebuf; keyalgo_strings[keyalgo_strings_used].name = name; keyalgo_strings_used++; return name; /* Note that this is in the table. */ } diff --git a/common/sexputil.c b/common/sexputil.c index c7471be85..e6fc84da0 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -1,1196 +1,1196 @@ /* sexputil.c - Utility functions for S-expressions. * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc. * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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 . */ /* This file implements a few utility functions useful when working with canonical encrypted S-expressions (i.e. not the S-exprssion objects from libgcrypt). */ #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "util.h" #include "tlv.h" #include "sexp-parse.h" #include "openpgpdefs.h" /* for pubkey_algo_t */ /* Return a malloced string with the S-expression CANON in advanced format. Returns NULL on error. */ static char * sexp_to_string (gcry_sexp_t sexp) { size_t n; char *result; if (!sexp) return NULL; n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); if (!n) return NULL; result = xtrymalloc (n); if (!result) return NULL; n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, n); if (!n) BUG (); return result; } /* Return a malloced string with the S-expression CANON in advanced format. Returns NULL on error. */ char * canon_sexp_to_string (const unsigned char *canon, size_t canonlen) { size_t n; gcry_sexp_t sexp; char *result; n = gcry_sexp_canon_len (canon, canonlen, NULL, NULL); if (!n) return NULL; if (gcry_sexp_sscan (&sexp, NULL, canon, n)) return NULL; result = sexp_to_string (sexp); gcry_sexp_release (sexp); return result; } /* Print the canonical encoded S-expression in SEXP in advanced format. SEXPLEN may be passed as 0 is SEXP is known to be valid. With TEXT of NULL print just the raw S-expression, with TEXT just an empty string, print a trailing linefeed, otherwise print an entire debug line. */ void log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen) { if (text && *text) log_debug ("%s ", text); if (sexp) { char *buf = canon_sexp_to_string (sexp, sexplen); log_printf ("%s", buf? buf : "[invalid S-expression]"); xfree (buf); } if (text) log_printf ("\n"); } /* Print the gcrypt S-expression SEXP in advanced format. With TEXT of NULL print just the raw S-expression, with TEXT just an empty string, print a trailing linefeed, otherwise print an entire debug line. */ void log_printsexp (const char *text, gcry_sexp_t sexp) { if (text && *text) log_debug ("%s ", text); if (sexp) { char *buf = sexp_to_string (sexp); log_printf ("%s", buf? buf : "[invalid S-expression]"); xfree (buf); } if (text) log_printf ("\n"); } /* Helper function to create a canonical encoded S-expression from a Libgcrypt S-expression object. The function returns 0 on success and the malloced canonical S-expression is stored at R_BUFFER and the allocated length at R_BUFLEN. On error an error code is returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the allocated buffer length is not required, NULL by be used for R_BUFLEN. */ gpg_error_t make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen) { size_t len; unsigned char *buf; *r_buffer = NULL; if (r_buflen) *r_buflen = 0;; len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); if (!len) return gpg_error (GPG_ERR_BUG); buf = xtrymalloc (len); if (!buf) return gpg_error_from_syserror (); len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len); if (!len) return gpg_error (GPG_ERR_BUG); *r_buffer = buf; if (r_buflen) *r_buflen = len; return 0; } /* Same as make_canon_sexp but pad the buffer to multiple of 64 bits. If SECURE is set, secure memory will be allocated. */ gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, unsigned char **r_buffer, size_t *r_buflen) { size_t len; unsigned char *buf; *r_buffer = NULL; if (r_buflen) *r_buflen = 0;; len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); if (!len) return gpg_error (GPG_ERR_BUG); len += (8 - len % 8) % 8; buf = secure? xtrycalloc_secure (1, len) : xtrycalloc (1, len); if (!buf) return gpg_error_from_syserror (); if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len)) return gpg_error (GPG_ERR_BUG); *r_buffer = buf; if (r_buflen) *r_buflen = len; return 0; } /* Return the so called "keygrip" which is the SHA-1 hash of the public key parameters expressed in a way dependend on the algorithm. KEY is expected to be an canonical encoded S-expression with a public or private key. KEYLEN is the length of that buffer. GRIP must be at least 20 bytes long. On success 0 is returned, on error an error code. */ gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip) { gpg_error_t err; gcry_sexp_t sexp; if (!grip) return gpg_error (GPG_ERR_INV_VALUE); err = gcry_sexp_sscan (&sexp, NULL, (const char *)key, keylen); if (err) return err; if (!gcry_pk_get_keygrip (sexp, grip)) err = gpg_error (GPG_ERR_INTERNAL); gcry_sexp_release (sexp); return err; } /* Compare two simple S-expressions like "(3:foo)". Returns 0 if they are identical or !0 if they are not. Note that this function can't be used for sorting. */ int cmp_simple_canon_sexp (const unsigned char *a_orig, const unsigned char *b_orig) { const char *a = (const char *)a_orig; const char *b = (const char *)b_orig; unsigned long n1, n2; char *endp; if (!a && !b) return 0; /* Both are NULL, they are identical. */ if (!a || !b) return 1; /* One is NULL, they are not identical. */ if (*a != '(' || *b != '(') log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); a++; n1 = strtoul (a, &endp, 10); a = endp; b++; n2 = strtoul (b, &endp, 10); b = endp; if (*a != ':' || *b != ':' ) log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); if (n1 != n2) return 1; /* Not the same. */ for (a++, b++; n1; n1--, a++, b++) if (*a != *b) return 1; /* Not the same. */ return 0; } /* Helper for cmp_canon_sexp. */ static int cmp_canon_sexp_def_tcmp (void *ctx, int depth, const unsigned char *aval, size_t alen, const unsigned char *bval, size_t blen) { (void)ctx; (void)depth; if (alen > blen) return 1; else if (alen < blen) return -1; else return memcmp (aval, bval, alen); } /* Compare the two canonical encoded s-expressions A with maximum * length ALEN and B with maximum length BLEN. * * Returns 0 if they match. * * If TCMP is NULL, this is not different really different from a * memcmp but does not consider any garbage after the last closing * parentheses. * * If TCMP is not NULL, it is expected to be a function to compare the * values of each token. TCMP is called for each token while parsing * the s-expressions until TCMP return a non-zero value. Here the CTX * receives the provided value TCMPCTX, DEPTH is the number of * currently open parentheses and (AVAL,ALEN) and (BVAL,BLEN) the * values of the current token. TCMP needs to return zero to indicate * that the tokens match. */ int cmp_canon_sexp (const unsigned char *a, size_t alen, const unsigned char *b, size_t blen, int (*tcmp)(void *ctx, int depth, const unsigned char *aval, size_t avallen, const unsigned char *bval, size_t bvallen), void *tcmpctx) { const unsigned char *a_buf, *a_tok; const unsigned char *b_buf, *b_tok; size_t a_buflen, a_toklen; size_t b_buflen, b_toklen; int a_depth, b_depth, ret; if ((!a && !b) || (!alen && !blen)) return 0; /* Both are NULL, they are identical. */ if (!a || !b) return !!a - !!b; /* One is NULL, they are not identical. */ if (*a != '(' || *b != '(') log_bug ("invalid S-exp in %s\n", __func__); if (!tcmp) tcmp = cmp_canon_sexp_def_tcmp; a_depth = 0; a_buf = a; a_buflen = alen; b_depth = 0; b_buf = b; b_buflen = blen; for (;;) { if (parse_sexp (&a_buf, &a_buflen, &a_depth, &a_tok, &a_toklen)) return -1; /* A is invalid. */ if (parse_sexp (&b_buf, &b_buflen, &b_depth, &b_tok, &b_toklen)) return -1; /* B is invalid. */ if (!a_depth && !b_depth) return 0; /* End of both expressions - they match. */ if (a_depth != b_depth) return a_depth - b_depth; /* Not the same structure */ if (!a_tok && !b_tok) ; /* parens */ else if (a_tok && b_tok) { ret = tcmp (tcmpctx, a_depth, a_tok, a_toklen, b_tok, b_toklen); if (ret) return ret; /* Mismatch */ } else /* One has a paren other has not. */ return !!a_tok - !!b_tok; } } /* Create a simple S-expression from the hex string at LINE. Returns a newly allocated buffer with that canonical encoded S-expression or NULL in case of an error. On return the number of characters scanned in LINE will be stored at NSCANNED. This functions stops converting at the first character not representing a hexdigit. Odd numbers of hex digits are allowed; a leading zero is then assumed. If no characters have been found, NULL is returned.*/ unsigned char * make_simple_sexp_from_hexstr (const char *line, size_t *nscanned) { size_t n, len; const char *s; unsigned char *buf; unsigned char *p; char numbuf[50], *numbufp; size_t numbuflen; for (n=0, s=line; hexdigitp (s); s++, n++) ; if (nscanned) *nscanned = n; if (!n) return NULL; len = ((n+1) & ~0x01)/2; numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen); buf = xtrymalloc (1 + numbuflen + len + 1 + 1); if (!buf) return NULL; buf[0] = '('; p = (unsigned char *)stpcpy ((char *)buf+1, numbufp); s = line; if ((n&1)) { *p++ = xtoi_1 (s); s++; n--; } for (; n > 1; n -=2, s += 2) *p++ = xtoi_2 (s); *p++ = ')'; *p = 0; /* (Not really needed.) */ return buf; } /* Return the hash algorithm from a KSBA sig-val. SIGVAL is a canonical encoded S-expression. Return 0 if the hash algorithm is not encoded in SIG-VAL or it is not supported by libgcrypt. */ int hash_algo_from_sigval (const unsigned char *sigval) { const unsigned char *s = sigval; size_t n; int depth; char buffer[50]; if (!s || *s != '(') return 0; /* Invalid S-expression. */ s++; n = snext (&s); if (!n) return 0; /* Invalid S-expression. */ if (!smatch (&s, n, "sig-val")) return 0; /* Not a sig-val. */ if (*s != '(') return 0; /* Invalid S-expression. */ s++; /* Skip over the algo+parameter list. */ depth = 1; if (sskip (&s, &depth) || depth) return 0; /* Invalid S-expression. */ if (*s != '(') return 0; /* No further list. */ /* Check whether this is (hash ALGO). */ s++; n = snext (&s); if (!n) return 0; /* Invalid S-expression. */ if (!smatch (&s, n, "hash")) return 0; /* Not a "hash" keyword. */ n = snext (&s); if (!n || n+1 >= sizeof (buffer)) return 0; /* Algorithm string is missing or too long. */ memcpy (buffer, s, n); buffer[n] = 0; return gcry_md_map_name (buffer); } /* Create a public key S-expression for an RSA public key from the modulus M with length MLEN and the public exponent E with length ELEN. Returns a newly allocated buffer of NULL in case of a memory allocation problem. If R_LEN is not NULL, the length of the canonical S-expression is stored there. */ unsigned char * make_canon_sexp_from_rsa_pk (const void *m_arg, size_t mlen, const void *e_arg, size_t elen, size_t *r_len) { const unsigned char *m = m_arg; const unsigned char *e = e_arg; int m_extra = 0; int e_extra = 0; char mlen_str[35]; char elen_str[35]; unsigned char *keybuf, *p; const char part1[] = "(10:public-key(3:rsa(1:n"; const char part2[] = ")(1:e"; const char part3[] = ")))"; /* Remove leading zeroes. */ for (; mlen && !*m; mlen--, m++) ; for (; elen && !*e; elen--, e++) ; /* Insert a leading zero if the number would be zero or interpreted as negative. */ if (!mlen || (m[0] & 0x80)) m_extra = 1; if (!elen || (e[0] & 0x80)) e_extra = 1; /* Build the S-expression. */ snprintf (mlen_str, sizeof mlen_str, "%u:", (unsigned int)mlen+m_extra); snprintf (elen_str, sizeof elen_str, "%u:", (unsigned int)elen+e_extra); keybuf = xtrymalloc (strlen (part1) + strlen (mlen_str) + mlen + m_extra + strlen (part2) + strlen (elen_str) + elen + e_extra + strlen (part3) + 1); if (!keybuf) return NULL; p = stpcpy (keybuf, part1); p = stpcpy (p, mlen_str); if (m_extra) *p++ = 0; memcpy (p, m, mlen); p += mlen; p = stpcpy (p, part2); p = stpcpy (p, elen_str); if (e_extra) *p++ = 0; memcpy (p, e, elen); p += elen; p = stpcpy (p, part3); if (r_len) *r_len = p - keybuf; return keybuf; } /* Return the parameters of a public RSA key expressed as an canonical encoded S-expression. */ gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_n, size_t *r_nlen, unsigned char const **r_e, size_t *r_elen) { gpg_error_t err; const unsigned char *buf, *tok; size_t buflen, toklen; int depth, last_depth1, last_depth2; const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; size_t rsa_n_len, rsa_e_len; *r_n = NULL; *r_nlen = 0; *r_e = NULL; *r_elen = 0; buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (!tok || !((toklen == 10 && !memcmp ("public-key", tok, toklen)) || (toklen == 11 && !memcmp ("private-key", tok, toklen)))) return gpg_error (GPG_ERR_BAD_PUBKEY); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) return gpg_error (GPG_ERR_DUP_VALUE); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip to the end of the list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) return err; } if (err) return err; if (!rsa_n || !rsa_n_len || !rsa_e || !rsa_e_len) return gpg_error (GPG_ERR_BAD_PUBKEY); *r_n = rsa_n; *r_nlen = rsa_n_len; *r_e = rsa_e; *r_elen = rsa_e_len; return 0; } /* Return the public key parameter Q of a public RSA or ECC key * expressed as an canonical encoded S-expression. */ gpg_error_t get_ecc_q_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_q, size_t *r_qlen) { gpg_error_t err; const unsigned char *buf, *tok; size_t buflen, toklen; int depth, last_depth1, last_depth2; const unsigned char *ecc_q = NULL; size_t ecc_q_len = 0; *r_q = NULL; *r_qlen = 0; buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) return gpg_error (GPG_ERR_BAD_PUBKEY); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 3 && !memcmp ("ecc", tok, toklen)) ; else if (tok && toklen == 5 && (!memcmp ("ecdsa", tok, toklen) || !memcmp ("eddsa", tok, toklen))) ; else return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) return gpg_error (GPG_ERR_DUP_VALUE); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && mpi) { *mpi = tok; *mpi_len = toklen; } } /* Skip to the end of the list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) return err; } if (err) return err; if (!ecc_q || !ecc_q_len) return gpg_error (GPG_ERR_BAD_PUBKEY); *r_q = ecc_q; *r_qlen = ecc_q_len; return 0; } /* Return an uncompressed point (X,Y) in P at R_BUF as a malloced * buffer with its byte length stored at R_BUFLEN. May not be used * for sensitive data. */ static gpg_error_t ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p, unsigned char **r_buf, unsigned int *r_buflen) { gpg_error_t err; int pbytes = (mpi_get_nbits (p)+7)/8; size_t n; unsigned char *buf, *ptr; *r_buf = NULL; *r_buflen = 0; buf = xtrymalloc (1 + 2*pbytes); if (!buf) return gpg_error_from_syserror (); *buf = 04; /* Uncompressed point. */ ptr = buf+1; err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x); if (err) { xfree (buf); return err; } if (n < pbytes) { memmove (ptr+(pbytes-n), ptr, n); memset (ptr, 0, (pbytes-n)); } ptr += pbytes; err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y); if (err) { xfree (buf); return err; } if (n < pbytes) { memmove (ptr+(pbytes-n), ptr, n); memset (ptr, 0, (pbytes-n)); } *r_buf = buf; *r_buflen = 1 + 2*pbytes; return 0; } /* Convert the ECC parameter Q in the canonical s-expression * (KEYDATA,KEYDATALEN) to uncompressed form. On success and if a * conversion was done, the new canonical encoded s-expression is * returned at (R_NEWKEYDAT,R_NEWKEYDATALEN); if a conversion was not * required (NULL,0) is stored there. On error an error code is * returned. The function may take any kind of key but will only do * the conversion for ECC curves where compression is supported. */ gpg_error_t uncompress_ecc_q_in_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char **r_newkeydata, size_t *r_newkeydatalen) { gpg_error_t err; const unsigned char *buf, *tok; size_t buflen, toklen, n; int depth, last_depth1, last_depth2; const unsigned char *q_ptr; /* Points to the value of "q". */ size_t q_ptrlen; /* Remaining length in KEYDATA. */ size_t q_toklen; /* Q's length including prefix. */ const unsigned char *curve_ptr; /* Points to the value of "curve". */ size_t curve_ptrlen; /* Remaining length in KEYDATA. */ gcry_mpi_t x, y; /* Point Q */ gcry_mpi_t p, a, b; /* Curve parameters. */ gcry_mpi_t x3, t, p1_4; /* Helper */ int y_bit; unsigned char *qvalue; /* Q in uncompressed form. */ unsigned int qvaluelen; unsigned char *dst; /* Helper */ char lenstr[35]; /* Helper for a length prefix. */ *r_newkeydata = NULL; *r_newkeydatalen = 0; buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (!tok) return gpg_error (GPG_ERR_BAD_PUBKEY); else if (toklen == 10 || !memcmp ("public-key", tok, toklen)) ; else if (toklen == 11 || !memcmp ("private-key", tok, toklen)) ; else if (toklen == 20 || !memcmp ("shadowed-private-key", tok, toklen)) ; else return gpg_error (GPG_ERR_BAD_PUBKEY); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 3 && !memcmp ("ecc", tok, toklen)) ; else if (tok && toklen == 5 && !memcmp ("ecdsa", tok, toklen)) ; else return 0; /* Other algo - no need for conversion. */ last_depth1 = depth; q_ptr = curve_ptr = NULL; q_ptrlen = 0; /*(silence cc warning)*/ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 1 && *tok == 'q' && !q_ptr) { q_ptr = buf; q_ptrlen = buflen; } else if (tok && toklen == 5 && !memcmp (tok, "curve", 5) && !curve_ptr) { curve_ptr = buf; curve_ptrlen = buflen; } if (q_ptr && curve_ptr) break; /* We got all what we need. */ /* Skip to the end of the list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) return err; } if (err) return err; if (!q_ptr) return 0; /* No Q - nothing to do. */ /* Get Q's value and check whether uncompressing is at all required. */ buf = q_ptr; buflen = q_ptrlen; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (toklen < 2 || !(*tok == 0x02 || *tok == 0x03)) return 0; /* Invalid length or not compressed. */ q_toklen = buf - q_ptr; /* We want the length with the prefix. */ /* Put the x-coordinate of q into X and remember the y bit */ y_bit = (*tok == 0x03); err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, tok+1, toklen-1, NULL); if (err) return err; /* For uncompressing we need to know the curve. */ if (!curve_ptr) { gcry_mpi_release (x); return gpg_error (GPG_ERR_INV_CURVE); } buf = curve_ptr; buflen = curve_ptrlen; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) { gcry_mpi_release (x); return err; } { char name[50]; gcry_sexp_t curveparam; if (toklen + 1 > sizeof name) { gcry_mpi_release (x); return gpg_error (GPG_ERR_TOO_LARGE); } mem2str (name, tok, toklen+1); curveparam = gcry_pk_get_param (GCRY_PK_ECC, name); if (!curveparam) { gcry_mpi_release (x); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } err = gcry_sexp_extract_param (curveparam, NULL, "pab", &p, &a, &b, NULL); gcry_sexp_release (curveparam); if (err) { gcry_mpi_release (x); return gpg_error (GPG_ERR_INTERNAL); } } if (!mpi_test_bit (p, 1)) { /* No support for point compression for this curve. */ gcry_mpi_release (x); gcry_mpi_release (p); gcry_mpi_release (a); gcry_mpi_release (b); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* * Recover Y. The Weierstrass curve: y^2 = x^3 + a*x + b */ x3 = mpi_new (0); t = mpi_new (0); p1_4 = mpi_new (0); y = mpi_new (0); /* Compute right hand side. */ mpi_powm (x3, x, GCRYMPI_CONST_THREE, p); mpi_mul (t, a, x); mpi_mod (t, t, p); mpi_add (t, t, b); mpi_mod (t, t, p); mpi_add (t, t, x3); mpi_mod (t, t, p); /* * When p mod 4 = 3, modular square root of A can be computed by * A^((p+1)/4) mod p */ /* Compute (p+1)/4 into p1_4 */ mpi_rshift (p1_4, p, 2); mpi_add_ui (p1_4, p1_4, 1); mpi_powm (y, t, p1_4, p); if (y_bit != mpi_test_bit (y, 0)) mpi_sub (y, p, y); gcry_mpi_release (p1_4); gcry_mpi_release (t); gcry_mpi_release (x3); gcry_mpi_release (a); gcry_mpi_release (b); err = ec2os (x, y, p, &qvalue, &qvaluelen); gcry_mpi_release (x); gcry_mpi_release (y); gcry_mpi_release (p); if (err) return err; snprintf (lenstr, sizeof lenstr, "%u:", (unsigned int)qvaluelen); /* Note that for simplicity we do not subtract the old length of Q * for the new buffer. */ *r_newkeydata = xtrymalloc (qvaluelen + strlen(lenstr) + qvaluelen); if (!*r_newkeydata) return gpg_error_from_syserror (); dst = *r_newkeydata; n = q_ptr - keydata; memcpy (dst, keydata, n); /* Copy first part of original data. */ dst += n; n = strlen (lenstr); memcpy (dst, lenstr, n); /* Copy new prefix of Q's value. */ dst += n; memcpy (dst, qvalue, qvaluelen); /* Copy new value of Q. */ dst += qvaluelen; log_assert (q_toklen < q_ptrlen); n = q_ptrlen - q_toklen; memcpy (dst, q_ptr + q_toklen, n);/* Copy rest of original data. */ dst += n; *r_newkeydatalen = dst - *r_newkeydata; xfree (qvalue); return 0; } /* Return the algo of a public KEY of SEXP. */ int get_pk_algo_from_key (gcry_sexp_t key) { gcry_sexp_t list; const char *s; size_t n; - char algoname[6]; + char algoname[10]; int algo = 0; list = gcry_sexp_nth (key, 1); if (!list) goto out; s = gcry_sexp_nth_data (list, 0, &n); if (!s) goto out; if (n >= sizeof (algoname)) goto out; memcpy (algoname, s, n); algoname[n] = 0; algo = gcry_pk_map_name (algoname); if (algo == GCRY_PK_ECC) { gcry_sexp_t l1; int i; l1 = gcry_sexp_find_token (list, "flags", 0); for (i = l1 ? gcry_sexp_length (l1)-1 : 0; i > 0; i--) { s = gcry_sexp_nth_data (l1, i, &n); if (!s) continue; /* Not a data element. */ if (n == 5 && !memcmp (s, "eddsa", 5)) { algo = GCRY_PK_EDDSA; break; } } gcry_sexp_release (l1); l1 = gcry_sexp_find_token (list, "curve", 0); s = gcry_sexp_nth_data (l1, 1, &n); if (n == 5 && !memcmp (s, "Ed448", 5)) algo = GCRY_PK_EDDSA; gcry_sexp_release (l1); } out: gcry_sexp_release (list); return algo; } /* This is a variant of get_pk_algo_from_key but takes an canonical * encoded S-expression as input. Returns a GCRYPT public key * identiier or 0 on error. */ int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen) { gcry_sexp_t sexp; int algo; if (gcry_sexp_sscan (&sexp, NULL, keydata, keydatalen)) return 0; algo = get_pk_algo_from_key (sexp); gcry_sexp_release (sexp); return algo; } /* Given the public key S_PKEY, return a new buffer with a descriptive * string for its algorithm. This function may return NULL on memory * error. If R_ALGOID is not NULL the gcrypt algo id is stored there. */ char * pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid) { const char *prefix; gcry_sexp_t l1; char *algoname; int algo; char *result; if (r_algoid) *r_algoid = 0; l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) l1 = gcry_sexp_find_token (s_pkey, "private-key", 0); if (!l1) return xtrystrdup ("E_no_key"); { gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = l_tmp; } algoname = gcry_sexp_nth_string (l1, 0); gcry_sexp_release (l1); if (!algoname) return xtrystrdup ("E_no_algo"); algo = gcry_pk_map_name (algoname); switch (algo) { case GCRY_PK_RSA: prefix = "rsa"; break; case GCRY_PK_ELG: prefix = "elg"; break; case GCRY_PK_DSA: prefix = "dsa"; break; case GCRY_PK_ECC: prefix = ""; break; default: prefix = NULL; break; } if (prefix && *prefix) result = xtryasprintf ("%s%u", prefix, gcry_pk_get_nbits (s_pkey)); else if (prefix) { const char *curve = gcry_pk_get_curve (s_pkey, 0, NULL); const char *name = openpgp_oid_to_curve (openpgp_curve_to_oid (curve, NULL, NULL), 0); if (name) result = xtrystrdup (name); else if (curve) result = xtryasprintf ("X_%s", curve); else result = xtrystrdup ("E_unknown"); } else result = xtryasprintf ("X_algo_%d", algo); if (r_algoid) *r_algoid = algo; xfree (algoname); return result; } /* Map a pubkey algo id from gcrypt to a string. This is the same as * gcry_pk_algo_name but makes sure that the ECC algo identifiers are * not all mapped to "ECC". */ const char * pubkey_algo_to_string (int algo) { if (algo == GCRY_PK_ECDSA) return "ECDSA"; else if (algo == GCRY_PK_ECDH) return "ECDH"; else if (algo == GCRY_PK_EDDSA) return "EdDSA"; else return gcry_pk_algo_name (algo); } /* Map a hash algo id from gcrypt to a string. This is the same as * gcry_md_algo_name but the returned string is lower case, as * expected by libksba and it avoids some overhead. */ const char * hash_algo_to_string (int algo) { static const struct { const char *name; int algo; } hashnames[] = { { "sha256", GCRY_MD_SHA256 }, { "sha512", GCRY_MD_SHA512 }, { "sha1", GCRY_MD_SHA1 }, { "sha384", GCRY_MD_SHA384 }, { "sha224", GCRY_MD_SHA224 }, { "sha3-224", GCRY_MD_SHA3_224 }, { "sha3-256", GCRY_MD_SHA3_256 }, { "sha3-384", GCRY_MD_SHA3_384 }, { "sha3-512", GCRY_MD_SHA3_512 }, { "ripemd160", GCRY_MD_RMD160 }, { "rmd160", GCRY_MD_RMD160 }, { "md2", GCRY_MD_MD2 }, { "md4", GCRY_MD_MD4 }, { "tiger", GCRY_MD_TIGER }, { "haval", GCRY_MD_HAVAL }, { "sm3", GCRY_MD_SM3 }, { "md5", GCRY_MD_MD5 } }; int i; for (i=0; i < DIM (hashnames); i++) if (algo == hashnames[i].algo) return hashnames[i].name; return "?"; } /* Map cipher modes to a string. */ const char * cipher_mode_to_string (int mode) { switch (mode) { case GCRY_CIPHER_MODE_CFB: return "CFB"; case GCRY_CIPHER_MODE_CBC: return "CBC"; case GCRY_CIPHER_MODE_GCM: return "GCM"; case GCRY_CIPHER_MODE_OCB: return "OCB"; case 14: return "EAX"; /* Only in gcrypt 1.9 */ default: return "[?]"; } } diff --git a/g10/call-agent.c b/g10/call-agent.c index e7046f7b2..a49c987fa 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1,3381 +1,3381 @@ /* call-agent.c - Divert GPG operations to the agent. * Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc. * Copyright (C) 2013-2015 Werner Koch * Copyright (C) 2020 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 #include #ifdef HAVE_LOCALE_H #include #endif #include "gpg.h" #include #include "../common/util.h" #include "../common/membuf.h" #include "options.h" #include "../common/i18n.h" #include "../common/asshelp.h" #include "../common/sysutils.h" #include "call-agent.h" #include "../common/status.h" #include "../common/shareddefs.h" #include "../common/host2net.h" #include "../common/ttyio.h" #define CONTROL_D ('D' - 'A' + 1) static assuan_context_t agent_ctx = NULL; static int did_early_card_test; struct confirm_parm_s { char *desc; char *ok; char *notok; }; struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; struct { u32 *keyid; u32 *mainkeyid; int pubkey_algo; } keyinfo; struct confirm_parm_s *confirm; }; struct cipher_parm_s { struct default_inq_parm_s *dflt; assuan_context_t ctx; unsigned char *ciphertext; size_t ciphertextlen; }; struct writecert_parm_s { struct default_inq_parm_s *dflt; const unsigned char *certdata; size_t certdatalen; }; struct writekey_parm_s { struct default_inq_parm_s *dflt; const unsigned char *keydata; size_t keydatalen; }; struct genkey_parm_s { struct default_inq_parm_s *dflt; const char *keyparms; const char *passphrase; }; struct import_key_parm_s { struct default_inq_parm_s *dflt; const void *key; size_t keylen; }; struct cache_nonce_parm_s { char **cache_nonce_addr; char **passwd_nonce_addr; }; static gpg_error_t learn_status_cb (void *opaque, const char *line); /* If RC is not 0, write an appropriate status message. */ static void status_sc_op_failure (int rc) { switch (gpg_err_code (rc)) { case 0: break; case GPG_ERR_CANCELED: case GPG_ERR_FULLY_CANCELED: write_status_text (STATUS_SC_OP_FAILURE, "1"); break; case GPG_ERR_BAD_PIN: case GPG_ERR_BAD_RESET_CODE: write_status_text (STATUS_SC_OP_FAILURE, "2"); break; default: write_status (STATUS_SC_OP_FAILURE); break; } } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t default_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct default_inq_parm_s *parm = opaque; const char *s; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { err = gpg_proxy_pinentry_notify (parm->ctrl, line); if (err) log_error (_("failed to proxy %s inquiry to client\n"), "PINENTRY_LAUNCHED"); /* We do not pass errors to avoid breaking other code. */ } else if ((has_leading_keyword (line, "PASSPHRASE") || has_leading_keyword (line, "NEW_PASSPHRASE")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) { assuan_begin_confidential (parm->ctx); if (have_static_passphrase ()) { s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else { char *pw; char buf[32]; if (parm->keyinfo.keyid) emit_status_need_passphrase (parm->ctrl, parm->keyinfo.keyid, parm->keyinfo.mainkeyid, parm->keyinfo.pubkey_algo); snprintf (buf, sizeof (buf), "%u", 100); write_status_text (STATUS_INQUIRE_MAXLEN, buf); pw = cpr_get_hidden ("passphrase.enter", _("Enter passphrase: ")); cpr_kill_prompt (); if (*pw == CONTROL_D && !pw[1]) err = gpg_error (GPG_ERR_CANCELED); else err = assuan_send_data (parm->ctx, pw, strlen (pw)); xfree (pw); } assuan_end_confidential (parm->ctx); } else if ((s = has_leading_keyword (line, "CONFIRM")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK && parm->confirm) { int ask = atoi (s); int yes; if (ask) { yes = cpr_get_answer_is_yes (NULL, parm->confirm->desc); if (yes) err = assuan_send_data (parm->ctx, NULL, 0); else err = gpg_error (GPG_ERR_NOT_CONFIRMED); } else { tty_printf ("%s", parm->confirm->desc); err = assuan_send_data (parm->ctx, NULL, 0); } } else log_debug ("ignoring gpg-agent inquiry '%s'\n", line); return err; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (assuan_context_t ctx, const char *servername, int mode) { return warn_server_version_mismatch (ctx, servername, mode, write_status_strings2, NULL, !opt.quiet); } #define FLAG_FOR_CARD_SUPPRESS_ERRORS 2 /* Try to connect to the agent via socket or fork it off and work by pipes. Handle the server's initial greeting */ static int start_agent (ctrl_t ctrl, int flag_for_card) { int rc; (void)ctrl; /* Not yet used. */ /* Fixme: We need a context for each thread or serialize the access to the agent. */ if (agent_ctx) rc = 0; else { rc = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, opt.autostart?ASSHELP_FLAG_AUTOSTART:0, opt.verbose, DBG_IPC, NULL, NULL); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { static int shown; if (!shown) { shown = 1; log_info (_("no gpg-agent running in this session\n")); } } else if (!rc && !(rc = warn_version_mismatch (agent_ctx, GPG_AGENT_NAME, 0))) { /* Tell the agent that we support Pinentry notifications. No error checking so that it will work also with older agents. */ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); /* Tell the agent about what version we are aware. This is here used to indirectly enable GPG_ERR_FULLY_CANCELED. */ assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0", NULL, NULL, NULL, NULL, NULL, NULL); /* Pass on the pinentry mode. */ if (opt.pinentry_mode) { char *tmp = xasprintf ("OPTION pinentry-mode=%s", str_pinentry_mode (opt.pinentry_mode)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) { log_error ("setting pinentry mode '%s' failed: %s\n", str_pinentry_mode (opt.pinentry_mode), gpg_strerror (rc)); write_status_error ("set_pinentry_mode", rc); } } /* Pass on the request origin. */ if (opt.request_origin) { char *tmp = xasprintf ("OPTION pretend-request-origin=%s", str_request_origin (opt.request_origin)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) { log_error ("setting request origin '%s' failed: %s\n", str_request_origin (opt.request_origin), gpg_strerror (rc)); write_status_error ("set_request_origin", rc); } } /* In DE_VS mode under Windows we require that the JENT RNG * is active. */ #ifdef HAVE_W32_SYSTEM if (!rc && opt.compliance == CO_DE_VS) { if (assuan_transact (agent_ctx, "GETINFO jent_active", NULL, NULL, NULL, NULL, NULL, NULL)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), GPG_AGENT_NAME, gnupg_compliance_option_string (opt.compliance)); write_status_error ("random-compliance", rc); } } #endif /*HAVE_W32_SYSTEM*/ } } if (!rc && flag_for_card && !did_early_card_test) { /* Request the serial number of the card for an early test. */ struct agent_card_info_s info; memset (&info, 0, sizeof info); if (!(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) rc = warn_version_mismatch (agent_ctx, SCDAEMON_NAME, 2); if (!rc) rc = assuan_transact (agent_ctx, opt.flags.use_only_openpgp_card? "SCD SERIALNO openpgp" : "SCD SERIALNO", NULL, NULL, NULL, NULL, learn_status_cb, &info); if (rc && !(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) { switch (gpg_err_code (rc)) { case GPG_ERR_NOT_SUPPORTED: case GPG_ERR_NO_SCDAEMON: write_status_text (STATUS_CARDCTRL, "6"); break; case GPG_ERR_OBJ_TERM_STATE: write_status_text (STATUS_CARDCTRL, "7"); break; default: write_status_text (STATUS_CARDCTRL, "4"); log_info ("selecting card failed: %s\n", gpg_strerror (rc)); break; } } if (!rc && is_status_enabled () && info.serialno) { char *buf; buf = xasprintf ("3 %s", info.serialno); write_status_text (STATUS_CARDCTRL, buf); xfree (buf); } agent_release_card_info (&info); if (!rc) did_early_card_test = 1; } return rc; } /* Return a new malloced string by unescaping the string S. Escaping is percent escaping and '+'/space mapping. A binary nul will silently be replaced by a 0xFF. Function returns NULL to indicate an out of memory status. */ static char * unescape_status_string (const unsigned char *s) { return percent_plus_unescape (s, 0xff); } /* Take a 20 or 32 byte hexencoded string and put it into the provided * FPRLEN byte long buffer FPR in binary format. Returns the actual * used length of the FPR buffer or 0 on error. */ static unsigned int unhexify_fpr (const char *hexstr, unsigned char *fpr, unsigned int fprlen) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if ((*s && *s != ' ') || !(n == 40 || n == 64)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s && n < fprlen; s += 2, n++) fpr[n] = xtoi_2 (s); return (n == 20 || n == 32)? n : 0; } /* Take the serial number from LINE and return it verbatim in a newly allocated string. We make sure that only hex characters are returned. */ static char * store_serialno (const char *line) { const char *s; char *p; for (s=line; hexdigitp (s); s++) ; p = xtrymalloc (s + 1 - line); if (p) { memcpy (p, line, s-line); p[s-line] = 0; } return p; } /* This is a dummy data line callback. */ static gpg_error_t dummy_data_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; (void)buffer; (void)length; return 0; } /* A simple callback used to return the serialnumber of a card. */ static gpg_error_t get_serialno_cb (void *opaque, const char *line) { char **serialno = opaque; const char *keyword = line; const char *s; int keywordlen, n; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { if (*serialno) return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */ for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1)|| !(spacep (s) || !*s) ) return gpg_error (GPG_ERR_ASS_PARAMETER); *serialno = xtrymalloc (n+1); if (!*serialno) return out_of_core (); memcpy (*serialno, line, n); (*serialno)[n] = 0; } return 0; } /* Release the card info structure INFO. */ void agent_release_card_info (struct agent_card_info_s *info) { int i; if (!info) return; xfree (info->reader); info->reader = NULL; xfree (info->manufacturer_name); info->manufacturer_name = NULL; xfree (info->serialno); info->serialno = NULL; xfree (info->apptype); info->apptype = NULL; xfree (info->disp_name); info->disp_name = NULL; xfree (info->disp_lang); info->disp_lang = NULL; xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->login_data); info->login_data = NULL; info->cafpr1len = info->cafpr2len = info->cafpr3len = 0; info->fpr1len = info->fpr2len = info->fpr3len = 0; for (i=0; i < DIM(info->private_do); i++) { xfree (info->private_do[i]); info->private_do[i] = NULL; } for (i=0; i < DIM(info->supported_keyalgo); i++) { free_strlist (info->supported_keyalgo[i]); info->supported_keyalgo[i] = NULL; } } static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct agent_card_info_s *parm = opaque; const char *keyword = line; int keywordlen; int i; char *endp; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 6 && !memcmp (keyword, "READER", keywordlen)) { xfree (parm->reader); parm->reader = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (parm->serialno); parm->serialno = store_serialno (line); parm->is_v2 = (strlen (parm->serialno) >= 16 && (xtoi_2 (parm->serialno+12) == 0 /* Yubikey */ || xtoi_2 (parm->serialno+12) >= 2)); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { xfree (parm->apptype); parm->apptype = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "APPVERSION", keywordlen)) { unsigned int val = 0; sscanf (line, "%x", &val); parm->appversion = val; } else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) { xfree (parm->disp_name); parm->disp_name = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen)) { xfree (parm->disp_lang); parm->disp_lang = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen)) { parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0; } else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen)) { xfree (parm->pubkey_url); parm->pubkey_url = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen)) { xfree (parm->login_data); parm->login_data = unescape_status_string (line); } else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen)) { parm->sig_counter = strtoul (line, NULL, 0); } else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen)) { char *p, *buf; buf = p = unescape_status_string (line); if (buf) { while (spacep (p)) p++; parm->chv1_cached = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; for (i=0; *p && i < 3; i++) { parm->chvmaxlen[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } for (i=0; *p && i < 3; i++) { parm->chvretry[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } xfree (buf); } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "ki")) parm->extcap.ki = abool; else if (!strcmp (p, "aac")) parm->extcap.aac = abool; else if (!strcmp (p, "bt")) parm->extcap.bt = abool; else if (!strcmp (p, "kdf")) parm->extcap.kdf = abool; else if (!strcmp (p, "si")) parm->status_indicator = strtoul (p2, NULL, 10); } } xfree (buf); } } else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1len = unhexify_fpr (line, parm->fpr1, sizeof parm->fpr1); else if (no == 2) parm->fpr2len = unhexify_fpr (line, parm->fpr2, sizeof parm->fpr2); else if (no == 3) parm->fpr3len = unhexify_fpr (line, parm->fpr3, sizeof parm->fpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen)) { int no = atoi (line); while (* line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1time = strtoul (line, NULL, 10); else if (no == 2) parm->fpr2time = strtoul (line, NULL, 10); else if (no == 3) parm->fpr3time = strtoul (line, NULL, 10); } else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { const char *hexgrp = line; int no; while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (strncmp (line, "OPENPGP.", 8)) ; else if ((no = atoi (line+8)) == 1) unhexify_fpr (hexgrp, parm->grp1, sizeof parm->grp1); else if (no == 2) unhexify_fpr (hexgrp, parm->grp2, sizeof parm->grp2); else if (no == 3) unhexify_fpr (hexgrp, parm->grp3, sizeof parm->grp3); } else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->cafpr1len = unhexify_fpr (line, parm->cafpr1,sizeof parm->cafpr1); else if (no == 2) parm->cafpr2len = unhexify_fpr (line, parm->cafpr2,sizeof parm->cafpr2); else if (no == 3) parm->cafpr3len = unhexify_fpr (line, parm->cafpr3,sizeof parm->cafpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen)) { int keyno = 0; int algo = PUBKEY_ALGO_RSA; int n = 0; sscanf (line, "%d %d %n", &keyno, &algo, &n); keyno--; if (keyno < 0 || keyno >= DIM (parm->key_attr)) return 0; parm->key_attr[keyno].algo = algo; if (algo == PUBKEY_ALGO_RSA) parm->key_attr[keyno].nbits = strtoul (line+n+3, NULL, 10); else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) parm->key_attr[keyno].curve = openpgp_is_curve_supported (line + n, NULL, NULL); } else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11) && strchr("1234", keyword[11])) { int no = keyword[11] - '1'; log_assert (no >= 0 && no <= 3); xfree (parm->private_do[no]); parm->private_do[no] = unescape_status_string (line); } else if (keywordlen == 12 && !memcmp (keyword, "MANUFACTURER", 12)) { xfree (parm->manufacturer_name); parm->manufacturer_name = NULL; parm->manufacturer_id = strtoul (line, &endp, 0); while (endp && spacep (endp)) endp++; if (endp && *endp) parm->manufacturer_name = xstrdup (endp); } else if (keywordlen == 3 && !memcmp (keyword, "KDF", 3)) { unsigned char *data = unescape_status_string (line); if (data[2] != 0x03) parm->kdf_do_enabled = 0; else if (data[22] != 0x85) parm->kdf_do_enabled = 1; else parm->kdf_do_enabled = 2; xfree (data); } else if (keywordlen == 5 && !memcmp (keyword, "UIF-", 4) && strchr("123", keyword[4])) { unsigned char *data; int no = keyword[4] - '1'; log_assert (no >= 0 && no <= 2); data = unescape_status_string (line); parm->uif[no] = (data[0] != 0xff); xfree (data); } else if (keywordlen == 13 && !memcmp (keyword, "KEY-ATTR-INFO", 13)) { if (!strncmp (line, "OPENPGP.", 8)) { int no; line += 8; no = atoi (line); if (no >= 1 && no <= 3) { no--; line++; while (spacep (line)) line++; append_to_strlist (&parm->supported_keyalgo[no], xstrdup (line)); } } /* Skip when it's not "OPENPGP.[123]". */ } return 0; } /* Call the scdaemon to learn about a smartcard. Note that in * contradiction to the function's name, gpg-agent's LEARN command is * used and not the low-level "SCD LEARN". * Used by: * card-util.c * keyedit_menu * card_store_key_with_backup (Woth force to remove secret key data) */ int agent_scd_learn (struct agent_card_info_s *info, int force) { int rc; struct default_inq_parm_s parm; struct agent_card_info_s dummyinfo; if (!info) info = &dummyinfo; memset (info, 0, sizeof *info); memset (&parm, 0, sizeof parm); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, force ? "LEARN --sendinfo --force" : "LEARN --sendinfo", dummy_data_cb, NULL, default_inq_cb, &parm, learn_status_cb, info); /* Also try to get the key attributes. */ if (!rc) agent_scd_getattr ("KEY-ATTR", info); if (info == &dummyinfo) agent_release_card_info (info); return rc; } struct keypairinfo_cb_parm_s { keypair_info_t kpinfo; keypair_info_t *kpinfo_tail; }; /* Callback for the agent_scd_keypairinfo function. */ static gpg_error_t scd_keypairinfo_status_cb (void *opaque, const char *line) { struct keypairinfo_cb_parm_s *parm = opaque; gpg_error_t err = 0; const char *keyword = line; int keywordlen; char *line_buffer = NULL; keypair_info_t kpi = NULL; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { /* The format of such a line is: * KEYPAIRINFO [usage] [keytime] [algostr] */ const char *fields[4]; int nfields; const char *hexgrp, *keyref, *usage; time_t atime; u32 keytime; line_buffer = xtrystrdup (line); if (!line_buffer) { err = gpg_error_from_syserror (); goto leave; } if ((nfields = split_fields (line_buffer, fields, DIM (fields))) < 2) goto leave; /* not enough args - invalid status line - ignore */ hexgrp = fields[0]; keyref = fields[1]; if (nfields > 2) usage = fields[2]; else usage = ""; if (nfields > 3) { atime = parse_timestamp (fields[3], NULL); if (atime == (time_t)(-1)) atime = 0; keytime = atime; } else keytime = 0; kpi = xtrycalloc (1, sizeof *kpi); if (!kpi) { err = gpg_error_from_syserror (); goto leave; } if (*hexgrp == 'X' && !hexgrp[1]) *kpi->keygrip = 0; /* No hexgrip. */ else if (strlen (hexgrp) == 2*KEYGRIP_LEN) mem2str (kpi->keygrip, hexgrp, sizeof kpi->keygrip); else { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } if (!*keyref) { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } kpi->idstr = xtrystrdup (keyref); if (!kpi->idstr) { err = gpg_error_from_syserror (); goto leave; } /* Parse and set the usage. */ for (; *usage; usage++) { switch (*usage) { case 's': kpi->usage |= GCRY_PK_USAGE_SIGN; break; case 'c': kpi->usage |= GCRY_PK_USAGE_CERT; break; case 'a': kpi->usage |= GCRY_PK_USAGE_AUTH; break; case 'e': kpi->usage |= GCRY_PK_USAGE_ENCR; break; } } kpi->keytime = keytime; /* Append to the list. */ *parm->kpinfo_tail = kpi; parm->kpinfo_tail = &kpi->next; kpi = NULL; } leave: free_keypair_info (kpi); xfree (line_buffer); return err; } /* Read the keypairinfo lines of the current card directly from * scdaemon. The list is returned as a string made up of the keygrip, * a space and the keyref. The flags of the string carry the usage * bits. If KEYREF is not NULL, only a single string is returned * which matches the given keyref. */ gpg_error_t agent_scd_keypairinfo (ctrl_t ctrl, const char *keyref, keypair_info_t *r_list) { gpg_error_t err; struct keypairinfo_cb_parm_s parm; struct default_inq_parm_s inq_parm; char line[ASSUAN_LINELENGTH]; *r_list = NULL; err= start_agent (ctrl, 1); if (err) return err; memset (&inq_parm, 0, sizeof inq_parm); inq_parm.ctx = agent_ctx; parm.kpinfo = NULL; parm.kpinfo_tail = &parm.kpinfo; if (keyref) snprintf (line, DIM(line), "SCD READKEY --info-only %s", keyref); else snprintf (line, DIM(line), "SCD LEARN --keypairinfo"); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, scd_keypairinfo_status_cb, &parm); if (!err && !parm.kpinfo) err = gpg_error (GPG_ERR_NO_DATA); if (err) free_keypair_info (parm.kpinfo); else *r_list = parm.kpinfo; return err; } /* Send an APDU to the current card. On success the status word is * stored at R_SW unless R_SQ is NULL. With HEXAPDU being NULL only a * RESET command is send to scd. HEXAPDU may also be one of theseo * special strings: * * "undefined" :: Send the command "SCD SERIALNO undefined" * "lock" :: Send the command "SCD LOCK --wait" * "trylock" :: Send the command "SCD LOCK" * "unlock" :: Send the command "SCD UNLOCK" * "reset-keep-lock" :: Send the command "SCD RESET --keep-lock" * * Used by: * card-util.c */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) { gpg_error_t err; /* Start the agent but not with the card flag so that we do not autoselect the openpgp application. */ err = start_agent (NULL, 0); if (err) return err; if (!hexapdu) { err = assuan_transact (agent_ctx, "SCD RESET", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "reset-keep-lock")) { err = assuan_transact (agent_ctx, "SCD RESET --keep-lock", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "lock")) { err = assuan_transact (agent_ctx, "SCD LOCK --wait", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "trylock")) { err = assuan_transact (agent_ctx, "SCD LOCK", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "unlock")) { err = assuan_transact (agent_ctx, "SCD UNLOCK", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "undefined")) { err = assuan_transact (agent_ctx, "SCD SERIALNO undefined", NULL, NULL, NULL, NULL, NULL, NULL); } else { char line[ASSUAN_LINELENGTH]; membuf_t mb; unsigned char *data; size_t datalen; init_membuf (&mb, 256); snprintf (line, DIM(line), "SCD APDU %s", hexapdu); err = assuan_transact (agent_ctx, line, put_membuf_cb, &mb, NULL, NULL, NULL, NULL); if (!err) { data = get_membuf (&mb, &datalen); if (!data) err = gpg_error_from_syserror (); else if (datalen < 2) /* Ooops */ err = gpg_error (GPG_ERR_CARD); else { *r_sw = buf16_to_uint (data+datalen-2); } xfree (data); } } return err; } int agent_keytotpm (ctrl_t ctrl, const char *hexgrip) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; snprintf(line, DIM(line), "KEYTOTPM %s\n", hexgrip); if (strchr (hexgrip, ',')) { log_error ("storing a part of a dual key is not yet supported\n"); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } rc = start_agent (ctrl, 0); if (rc) return rc; parm.ctx = agent_ctx; parm.ctrl = ctrl; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); if (rc) log_log (GPGRT_LOGLVL_ERROR, _("error from TPM: %s\n"), gpg_strerror (rc)); return rc; } /* Used by: * card_store_subkey * card_store_key_with_backup */ int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp, const char *ecdh_param_str) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (strchr (hexgrip, ',')) { log_error ("storing a part of a dual key is not yet supported\n"); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s%s%s", force?"--force ": "", hexgrip, serialno, keyno, timestamp, ecdh_param_str? " ":"", ecdh_param_str? ecdh_param_str:""); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Object used with the agent_scd_getattr_one. */ struct getattr_one_parm_s { const char *keyword; /* Keyword to look for. */ char *data; /* Malloced and unescaped data. */ gpg_error_t err; /* Error code or 0 on success. */ }; /* Callback for agent_scd_getattr_one. */ static gpg_error_t getattr_one_status_cb (void *opaque, const char *line) { struct getattr_one_parm_s *parm = opaque; const char *s; if (parm->data) return 0; /* We want only the first occurrence. */ if ((s=has_leading_keyword (line, parm->keyword))) { parm->data = percent_plus_unescape (s, 0xff); if (!parm->data) parm->err = gpg_error_from_syserror (); } return 0; } /* Simplified version of agent_scd_getattr. This function returns * only the first occurrence of the attribute NAME and stores it at * R_VALUE. A nul in the result is silennly replaced by 0xff. On * error NULL is stored at R_VALUE. */ gpg_error_t agent_scd_getattr_one (const char *name, char **r_value) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inqparm; struct getattr_one_parm_s parm; *r_value = NULL; if (!*name) return gpg_error (GPG_ERR_INV_VALUE); memset (&inqparm, 0, sizeof inqparm); inqparm.ctx = agent_ctx; memset (&parm, 0, sizeof parm); parm.keyword = name; /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); stpcpy (stpcpy (line, "SCD GETATTR "), name); err = start_agent (NULL, 1); if (err) return err; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inqparm, getattr_one_status_cb, &parm); if (!err && parm.err) err = parm.err; else if (!err && !parm.data) err = gpg_error (GPG_ERR_NO_DATA); if (!err) *r_value = parm.data; else xfree (parm.data); return err; } /* Call the agent to retrieve a data object. This function returns * the data in the same structure as used by the learn command. It is * allowed to update such a structure using this command. * * Used by: * build_sk_list * enum_secret_keys * get_signature_count * card-util.c * generate_keypair (KEY-ATTR) * card_store_key_with_backup (SERIALNO) * generate_card_subkeypair (KEY-ATTR) */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (!*name) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); stpcpy (stpcpy (line, "SCD GETATTR "), name); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, learn_status_cb, info); if (!rc && !strcmp (name, "KEY-FPR")) { /* Let the agent create the shadow keys if not yet done. */ if (info->fpr1len) assuan_transact (agent_ctx, "READKEY --card --no-data -- $SIGNKEYID", NULL, NULL, NULL, NULL, NULL, NULL); if (info->fpr2len) assuan_transact (agent_ctx, "READKEY --card --no-data -- $ENCRKEYID", NULL, NULL, NULL, NULL, NULL, NULL); } return rc; } /* Send an setattr command to the SCdaemon. * Used by: * card-util.c */ gpg_error_t agent_scd_setattr (const char *name, const void *value_arg, size_t valuelen) { gpg_error_t err; const unsigned char *value = value_arg; char line[ASSUAN_LINELENGTH]; char *p; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (!*name || !valuelen) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); p = stpcpy (stpcpy (line, "SCD SETATTR "), name); *p++ = ' '; for (; valuelen; value++, valuelen--) { if (p >= line + DIM(line)-5 ) return gpg_error (GPG_ERR_TOO_LARGE); if (*value < ' ' || *value == '+' || *value == '%') { sprintf (p, "%%%02X", *value); p += 3; } else if (*value == ' ') *p++ = '+'; else *p++ = *value; } *p = 0; err = start_agent (NULL, 1); if (!err) { parm.ctx = agent_ctx; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); } status_sc_op_failure (err); return err; } /* Handle a CERTDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the END command. */ static gpg_error_t inq_writecert_parms (void *opaque, const char *line) { int rc; struct writecert_parm_s *parm = opaque; if (has_leading_keyword (line, "CERTDATA")) { rc = assuan_send_data (parm->dflt->ctx, parm->certdata, parm->certdatalen); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Send a WRITECERT command to the SCdaemon. * Used by: * card-util.c */ int agent_scd_writecert (const char *certidstr, const unsigned char *certdata, size_t certdatalen) { int rc; char line[ASSUAN_LINELENGTH]; struct writecert_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; memset (&parms, 0, sizeof parms); snprintf (line, DIM(line), "SCD WRITECERT %s", certidstr); dfltparm.ctx = agent_ctx; parms.dflt = &dfltparm; parms.certdata = certdata; parms.certdatalen = certdatalen; rc = assuan_transact (agent_ctx, line, NULL, NULL, inq_writecert_parms, &parms, NULL, NULL); return rc; } /* Status callback for the SCD GENKEY command. */ static gpg_error_t scd_genkey_cb (void *opaque, const char *line) { u32 *createtime = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen)) { *createtime = (u32)strtoul (line, NULL, 10); } else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen)) { write_status_text (STATUS_PROGRESS, line); } return 0; } /* Send a GENKEY command to the SCdaemon. If *CREATETIME is not 0, * the value will be passed to SCDAEMON with --timestamp option so that * the key is created with this. Otherwise, timestamp was generated by * SCDEAMON. On success, creation time is stored back to * CREATETIME. * Used by: * gen_card_key */ int agent_scd_genkey (int keyno, int force, u32 *createtime) { int rc; char line[ASSUAN_LINELENGTH]; gnupg_isotime_t tbuf; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; if (*createtime) epoch2isotime (tbuf, *createtime); else *tbuf = 0; snprintf (line, DIM(line), "SCD GENKEY %s%s %s %d", *tbuf? "--timestamp=":"", tbuf, force? "--force":"", keyno); dfltparm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, scd_genkey_cb, createtime); status_sc_op_failure (rc); return rc; } /* Return the serial number of the card or an appropriate error. The * serial number is returned as a hexstring. With DEMAND the active * card is switched to the card with that serialno. * Used by: * card-util.c * build_sk_list * enum_secret_keys */ int agent_scd_serialno (char **r_serialno, const char *demand) { int err; char *serialno = NULL; char line[ASSUAN_LINELENGTH]; if (r_serialno) *r_serialno = NULL; err = start_agent (NULL, (1 | FLAG_FOR_CARD_SUPPRESS_ERRORS)); if (err) return err; if (!demand) strcpy (line, "SCD SERIALNO"); else snprintf (line, DIM(line), "SCD SERIALNO --demand=%s", demand); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, get_serialno_cb, &serialno); if (err) { xfree (serialno); return err; } if (r_serialno) *r_serialno = serialno; else xfree (serialno); return 0; } /* Send a READCERT command to the SCdaemon. * Used by: * card-util.c */ int agent_scd_readcert (const char *certidstr, void **r_buf, size_t *r_buflen) { int rc; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); *r_buf = NULL; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; init_membuf (&data, 2048); snprintf (line, DIM(line), "SCD READCERT %s", certidstr); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) return gpg_error (GPG_ERR_ENOMEM); return 0; } /* Callback for the agent_scd_readkey function. */ static gpg_error_t readkey_status_cb (void *opaque, const char *line) { u32 *keytimep = opaque; gpg_error_t err = 0; const char *args; char *line_buffer = NULL; /* FIXME: Get that info from the KEYPAIRINFO line. */ if ((args = has_leading_keyword (line, "KEYPAIRINFO")) && !*keytimep) { /* The format of such a line is: * KEYPAIRINFO [usage] [keytime] * * Note that we use only the first valid KEYPAIRINFO line. More * lines are possible if a second card carries the same key. */ const char *fields[4]; int nfields; time_t atime; line_buffer = xtrystrdup (line); if (!line_buffer) { err = gpg_error_from_syserror (); goto leave; } if ((nfields = split_fields (line_buffer, fields, DIM (fields))) < 4) goto leave; /* not enough args - ignore */ if (nfields > 3) { atime = parse_timestamp (fields[3], NULL); if (atime == (time_t)(-1)) atime = 0; *keytimep = atime; } else *keytimep = 0; } leave: xfree (line_buffer); return err; } /* This is a variant of agent_readkey which sends a READKEY command * directly Scdaemon. On success a new s-expression is stored at * R_RESULT. If R_KEYTIME is not NULL the key cresation time of an * OpenPGP card is stored there - if that is not known 0 is stored. * In the latter case it is allowed to pass NULL for R_RESULT. */ gpg_error_t agent_scd_readkey (ctrl_t ctrl, const char *keyrefstr, gcry_sexp_t *r_result, u32 *r_keytime) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; unsigned char *buf; size_t len, buflen; struct default_inq_parm_s dfltparm; u32 keytime; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctx = agent_ctx; if (r_result) *r_result = NULL; if (r_keytime) *r_keytime = 0; err = start_agent (ctrl, 1); if (err) return err; init_membuf (&data, 1024); snprintf (line, DIM(line), "SCD READKEY --info%s -- %s", r_result? "":"-only", keyrefstr); keytime = 0; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, readkey_status_cb, &keytime); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &buflen); if (!buf) return gpg_error_from_syserror (); if (r_result) err = gcry_sexp_new (r_result, buf, buflen, 0); else err = 0; xfree (buf); if (!err && r_keytime) *r_keytime = keytime; return err; } struct card_cardlist_parm_s { int error; strlist_t list; }; /* Callback function for agent_card_cardlist. */ static gpg_error_t card_cardlist_cb (void *opaque, const char *line) { struct card_cardlist_parm_s *parm = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { const char *s; int n; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1) || *s) parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); else add_to_strlist (&parm->list, line); } return 0; } /* Return a list of currently available cards. * Used by: * card-util.c * skclist.c */ int agent_scd_cardlist (strlist_t *result) { int err; char line[ASSUAN_LINELENGTH]; struct card_cardlist_parm_s parm; memset (&parm, 0, sizeof parm); *result = NULL; err = start_agent (NULL, 1 | FLAG_FOR_CARD_SUPPRESS_ERRORS); if (err) return err; strcpy (line, "SCD GETINFO card_list"); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, card_cardlist_cb, &parm); if (!err && parm.error) err = parm.error; if (!err) *result = parm.list; else free_strlist (parm.list); return 0; } /* Make the app APPNAME the one on the card. This is sometimes * required to make sure no other process has switched a card to * another application. The only useful APPNAME is "openpgp". */ gpg_error_t agent_scd_switchapp (const char *appname) { int err; char line[ASSUAN_LINELENGTH]; if (appname && !*appname) appname = NULL; err = start_agent (NULL, (1 | FLAG_FOR_CARD_SUPPRESS_ERRORS)); if (err) return err; snprintf (line, DIM(line), "SCD SWITCHAPP --%s%s", appname? " ":"", appname? appname:""); return assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); } struct card_keyinfo_parm_s { int error; keypair_info_t list; }; /* Callback function for agent_card_keylist. */ static gpg_error_t card_keyinfo_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct card_keyinfo_parm_s *parm = opaque; const char *keyword = line; int keywordlen; keypair_info_t keyinfo = NULL; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 7 && !memcmp (keyword, "KEYINFO", keywordlen)) { const char *s; int n; keypair_info_t *l_p = &parm->list; while ((*l_p)) l_p = &(*l_p)->next; keyinfo = xtrycalloc (1, sizeof *keyinfo); if (!keyinfo) goto alloc_error; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (n != 40) goto parm_error; memcpy (keyinfo->keygrip, line, 40); keyinfo->keygrip[40] = 0; line = s; if (!*line) goto parm_error; while (spacep (line)) line++; if (*line++ != 'T') goto parm_error; if (!*line) goto parm_error; while (spacep (line)) line++; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n) goto parm_error; keyinfo->serialno = xtrymalloc (n+1); if (!keyinfo->serialno) goto alloc_error; memcpy (keyinfo->serialno, line, n); keyinfo->serialno[n] = 0; line = s; if (!*line) goto parm_error; while (spacep (line)) line++; if (!*line) goto parm_error; keyinfo->idstr = xtrystrdup (line); if (!keyinfo->idstr) goto alloc_error; *l_p = keyinfo; } return err; alloc_error: xfree (keyinfo); if (!parm->error) parm->error = gpg_error_from_syserror (); return 0; parm_error: xfree (keyinfo); if (!parm->error) parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); return 0; } /* Free a keypair info list. */ void free_keypair_info (keypair_info_t l) { keypair_info_t l_next; for (; l; l = l_next) { l_next = l->next; xfree (l->serialno); xfree (l->idstr); xfree (l); } } /* Call the scdaemon to check if a key of KEYGRIP is available, or retrieve list of available keys on cards. With CAP, we can limit keys with specified capability. On success, the allocated structure is stored at RESULT. On error, an error code is returned and NULL is stored at RESULT. */ gpg_error_t agent_scd_keyinfo (const char *keygrip, int cap, keypair_info_t *result) { int err; struct card_keyinfo_parm_s parm; char line[ASSUAN_LINELENGTH]; char *list_option; *result = NULL; switch (cap) { case 0: list_option = "--list"; break; case GCRY_PK_USAGE_SIGN: list_option = "--list=sign"; break; case GCRY_PK_USAGE_ENCR: list_option = "--list=encr"; break; case GCRY_PK_USAGE_AUTH: list_option = "--list=auth"; break; default: return gpg_error (GPG_ERR_INV_VALUE); } memset (&parm, 0, sizeof parm); snprintf (line, sizeof line, "SCD KEYINFO %s", keygrip ? keygrip : list_option); err = start_agent (NULL, 1 | FLAG_FOR_CARD_SUPPRESS_ERRORS); if (err) return err; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, card_keyinfo_cb, &parm); if (!err && parm.error) err = parm.error; if (!err) *result = parm.list; else free_keypair_info (parm.list); return err; } /* Change the PIN of an OpenPGP card or reset the retry counter. * CHVNO 1: Change the PIN * 2: For v1 cards: Same as 1. * For v2 cards: Reset the PIN using the Reset Code. * 3: Change the admin PIN * 101: Set a new PIN and reset the retry counter * 102: For v1 cars: Same as 101. * For v2 cards: Set a new Reset Code. * SERIALNO is not used. * Used by: * card-util.c */ int agent_scd_change_pin (int chvno, const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; const char *reset = ""; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; if (chvno >= 100) reset = "--reset"; chvno %= 100; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "SCD PASSWD %s %d", reset, chvno); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Perform a CHECKPIN operation. SERIALNO should be the serial * number of the card - optionally followed by the fingerprint; * however the fingerprint is ignored here. * Used by: * card-util.c */ int agent_scd_checkpin (const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "SCD CHECKPIN %s", serialno); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Note: All strings shall be UTF-8. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate error code returned. Only called from passphrase.c:passphrase_get - see there for more comments on this ugly API. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int newsymkey, int repeat, int check, char **r_passphrase) { int rc; char line[ASSUAN_LINELENGTH]; char *arg1 = NULL; char *arg2 = NULL; char *arg3 = NULL; char *arg4 = NULL; membuf_t data; struct default_inq_parm_s dfltparm; int have_newsymkey, wasconf; memset (&dfltparm, 0, sizeof dfltparm); *r_passphrase = NULL; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; /* Check that the gpg-agent understands the repeat option. */ if (assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE repeat", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); have_newsymkey = !(assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE newsymkey", NULL, NULL, NULL, NULL, NULL, NULL)); if (cache_id && *cache_id) if (!(arg1 = percent_plus_escape (cache_id))) goto no_mem; if (err_msg && *err_msg) if (!(arg2 = percent_plus_escape (err_msg))) goto no_mem; if (prompt && *prompt) if (!(arg3 = percent_plus_escape (prompt))) goto no_mem; if (desc_msg && *desc_msg) if (!(arg4 = percent_plus_escape (desc_msg))) goto no_mem; /* CHECK && REPEAT or NEWSYMKEY is here an indication that a new * passphrase for symmetric encryption is requested; if the agent * supports this we enable the modern API by also passing --newsymkey. */ snprintf (line, DIM(line), "GET_PASSPHRASE --data --repeat=%d%s%s -- %s %s %s %s", repeat, ((repeat && check) || newsymkey)? " --check":"", (have_newsymkey && newsymkey)? " --newsymkey":"", arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", arg4? arg4:"X"); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); init_membuf_secure (&data, 64); wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); assuan_begin_confidential (agent_ctx); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (!wasconf) assuan_end_confidential (agent_ctx); if (rc) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) rc = gpg_error_from_syserror (); } return rc; no_mem: rc = gpg_error_from_syserror (); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); return rc; } gpg_error_t agent_clear_passphrase (const char *cache_id) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); if (!cache_id || !*cache_id) return 0; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id); return assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); } /* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpg_agent_get_confirmation (const char *desc) { int rc; char *tmp; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; tmp = percent_plus_escape (desc); if (!tmp) return gpg_error_from_syserror (); snprintf (line, DIM(line), "GET_CONFIRMATION %s", tmp); xfree (tmp); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); return rc; } /* Return the S2K iteration count as computed by gpg-agent. On error * print a warning and return a default value. */ unsigned long agent_get_s2k_count (void) { gpg_error_t err; membuf_t data; char *buf; unsigned long count = 0; err = start_agent (NULL, 0); if (err) goto leave; init_membuf (&data, 32); err = assuan_transact (agent_ctx, "GETINFO s2k_count", put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); buf = get_membuf (&data, NULL); if (!buf) err = gpg_error_from_syserror (); else { count = strtoul (buf, NULL, 10); xfree (buf); } } leave: if (err || count < 65536) { /* Don't print an error if an older agent is used. */ if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER) log_error (_("problem with the agent: %s\n"), gpg_strerror (err)); /* Default to 65536 which was used up to 2.0.13. */ count = 65536; } return count; } struct keyinfo_data_parm_s { char *serialno; int is_smartcard; int passphrase_cached; int cleartext; int card_available; }; static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { struct keyinfo_data_parm_s *data = opaque; char *s; if ((s = has_leading_keyword (line, "KEYINFO")) && data) { /* Parse the arguments: * 0 1 2 3 4 5 * * * 6 7 8 * */ const char *fields[9]; if (split_fields (s, fields, DIM (fields)) == 9) { data->is_smartcard = (fields[1][0] == 'T'); if (data->is_smartcard && !data->serialno && strcmp (fields[2], "-")) data->serialno = xtrystrdup (fields[2]); /* '1' for cached */ data->passphrase_cached = (fields[4][0] == '1'); /* 'P' for protected, 'C' for clear */ data->cleartext = (fields[5][0] == 'C'); /* 'A' for card is available */ data->card_available = (fields[8][0] == 'A'); } } return 0; } /* Ask the agent whether a secret key for the given public key is * available. Returns 0 if not available. Bigger value is preferred. * Will never return a value less than 0. Defined return values are: * 0 := No key or error * 1 := Key available * 2 := Key available on a smartcard * 3 := Key available and passphrase cached * 4 := Key available on current smartcard */ int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *hexgrip, *p; struct keyinfo_data_parm_s keyinfo; int result, result2; memset (&keyinfo, 0, sizeof keyinfo); err = start_agent (ctrl, 0); if (err) return 0; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return 0; if ((p=strchr (hexgrip, ','))) *p++ = 0; snprintf (line, sizeof line, "KEYINFO %s", hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) result = 0; else if (keyinfo.card_available) result = 4; else if (keyinfo.passphrase_cached) result = 3; else if (keyinfo.is_smartcard) result = 2; else result = 1; if (!p) { xfree (hexgrip); return result; /* Not a dual algo - we are ready. */ } /* Now check the second keygrip. */ memset (&keyinfo, 0, sizeof keyinfo); snprintf (line, sizeof line, "KEYINFO %s", p); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) result2 = 0; else if (keyinfo.card_available) result2 = 4; else if (keyinfo.passphrase_cached) result2 = 3; else if (keyinfo.is_smartcard) result2 = 2; else result2 = 1; xfree (hexgrip); if (result == result2) return result; /* Both keys have the same status. */ else if (!result && result2) return 0; /* Only first key available - return no key. */ else if (result && !result2) return 0; /* Only second key not availabale - return no key. */ else if (result == 4 || result == 2) return result; /* First key on card - don't care where the second is. */ else return result; } /* Ask the agent whether a secret key is available for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ gpg_error_t agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *p; kbnode_t kbctx, node; int nkeys; /* (always zero in secret_keygrips mode) */ unsigned char grip[KEYGRIP_LEN]; unsigned char grip2[KEYGRIP_LEN]; int grip2_valid; const unsigned char *s; unsigned int n; err = start_agent (ctrl, 0); if (err) return err; /* If we have not yet issued a "HAVEKEY --list" do that now. We use * a more or less arbitray limit of 1000 keys. */ if (ctrl && !ctrl->secret_keygrips && !ctrl->no_more_secret_keygrips) { membuf_t data; init_membuf (&data, 4096); err = assuan_transact (agent_ctx, "HAVEKEY --list=1000", put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { ctrl->secret_keygrips = get_membuf (&data, &ctrl->secret_keygrips_len); if (!ctrl->secret_keygrips) err = gpg_error_from_syserror (); if ((ctrl->secret_keygrips_len % 20)) { err = gpg_error (GPG_ERR_INV_DATA); xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = NULL; } } if (err) { if (opt.quiet) log_info ("problem with fast path key listing: %s - ignored\n", gpg_strerror (err)); err = 0; } /* We want to do this only once. */ ctrl->no_more_secret_keygrips = 1; } err = gpg_error (GPG_ERR_NO_SECKEY); /* Just in case no key was found in KEYBLOCK. */ p = stpcpy (line, "HAVEKEY"); for (kbctx=NULL, nkeys=0; (node = walk_kbnode (keyblock, &kbctx, 0)); ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { if (ctrl && ctrl->secret_keygrips) { /* We got an array with all secret keygrips. Check this. */ err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); if (err && gpg_err_code (err) != GPG_ERR_FALSE) return err; grip2_valid = !err; for (s=ctrl->secret_keygrips, n = 0; n < ctrl->secret_keygrips_len; s += 20, n += 20) { if (!memcmp (s, grip, 20)) return 0; if (grip2_valid && !memcmp (s, grip2, 20)) return 0; } err = gpg_error (GPG_ERR_NO_SECKEY); /* Keep on looping over the keyblock. Never bump nkeys. */ } else { if (nkeys && ((p - line) + 4*KEYGRIP_LEN+1+1) > (ASSUAN_LINELENGTH - 2)) { err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err != gpg_err_code (GPG_ERR_NO_SECKEY)) break; /* Seckey available or unexpected error - ready. */ p = stpcpy (line, "HAVEKEY"); nkeys = 0; } err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); if (err && gpg_err_code (err) != GPG_ERR_FALSE) return err; if (!err) /* Add the second keygrip from dual algos. */ { *p++ = ' '; bin2hex (grip2, 20, p); p += 40; nkeys++; } } } if (!err && nkeys) err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. if r_cleartext is not NULL, the referenced int will be set to 1 if the agent's copy of the key is stored in the clear, or 0 otherwise */ gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno, int *r_cleartext) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct keyinfo_data_parm_s keyinfo; - char *s; + const char *s; memset (&keyinfo, 0,sizeof keyinfo); *r_serialno = NULL; err = start_agent (ctrl, 0); if (err) return err; /* FIXME: Support dual keys. Maybe under the assumption that the * first key might be on a card. */ if (!hexkeygrip) return gpg_error (GPG_ERR_INV_VALUE); s = strchr (hexkeygrip, ','); if (!s) s = hexkeygrip + strlen (hexkeygrip); if (s - hexkeygrip != 40) return gpg_error (GPG_ERR_INV_VALUE); /* Note that for a dual algo we only get info for the first key. * FIXME: We need to see how we can show the status of the second * key in a key listing. */ snprintf (line, DIM(line), "KEYINFO %.40s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); if (!err && keyinfo.serialno) { /* Sanity check for bad characters. */ if (strpbrk (keyinfo.serialno, ":\n\r")) err = GPG_ERR_INV_VALUE; } if (err) xfree (keyinfo.serialno); else { *r_serialno = keyinfo.serialno; if (r_cleartext) *r_cleartext = keyinfo.cleartext; } return err; } /* Status callback for agent_import_key, agent_export_key and agent_genkey. */ static gpg_error_t cache_nonce_status_cb (void *opaque, const char *line) { struct cache_nonce_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "CACHE_NONCE"))) { if (parm->cache_nonce_addr) { xfree (*parm->cache_nonce_addr); *parm->cache_nonce_addr = xtrystrdup (s); } } else if ((s = has_leading_keyword (line, "PASSWD_NONCE"))) { if (parm->passwd_nonce_addr) { xfree (*parm->passwd_nonce_addr); *parm->passwd_nonce_addr = xtrystrdup (s); } } else if ((s = has_leading_keyword (line, "PROGRESS"))) { if (opt.enable_progress_filter) write_status_text (STATUS_PROGRESS, s); } return 0; } /* Handle a KEYPARMS inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_genkey_parms (void *opaque, const char *line) { struct genkey_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYPARAM")) { err = assuan_send_data (parm->dflt->ctx, parm->keyparms, strlen (parm->keyparms)); } else if (has_leading_keyword (line, "NEWPASSWD") && parm->passphrase) { err = assuan_send_data (parm->dflt->ctx, parm->passphrase, strlen (parm->passphrase)); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to generate a new key. KEYPARMS is the usual S-expression giving the parameters of the key. gpg-agent passes it gcry_pk_genkey. If NO_PROTECTION is true the agent is advised not to protect the generated key. If NO_PROTECTION is not set and PASSPHRASE is not NULL the agent is requested to protect the key with that passphrase instead of asking for one. TIMESTAMP is the creation time of the key or zero. */ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, char **passwd_nonce_addr, const char *keyparms, int no_protection, const char *passphrase, time_t timestamp, gcry_sexp_t *r_pubkey) { gpg_error_t err; struct genkey_parm_s gk_parm; struct cache_nonce_parm_s cn_parm; struct default_inq_parm_s dfltparm; membuf_t data; size_t len; unsigned char *buf; char timestamparg[16 + 16]; /* The 2nd 16 is sizeof(gnupg_isotime_t) */ char line[ASSUAN_LINELENGTH]; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; /* Do not use our cache of secret keygrips anymore - this command * would otherwise requiring to update that cache. */ if (ctrl && ctrl->secret_keygrips) { xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = 0; } if (timestamp) { strcpy (timestamparg, " --timestamp="); epoch2isotime (timestamparg+13, timestamp); } else *timestamparg = 0; if (passwd_nonce_addr && *passwd_nonce_addr) ; /* A RESET would flush the passwd nonce cache. */ else { err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } init_membuf (&data, 1024); gk_parm.dflt = &dfltparm; gk_parm.keyparms = keyparms; gk_parm.passphrase = passphrase; snprintf (line, sizeof line, "GENKEY%s%s%s%s%s%s", *timestamparg? timestamparg : "", no_protection? " --no-protection" : passphrase ? " --inq-passwd" : /* */ "", passwd_nonce_addr && *passwd_nonce_addr? " --passwd-nonce=":"", passwd_nonce_addr && *passwd_nonce_addr? *passwd_nonce_addr:"", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, inq_genkey_parms, &gk_parm, cache_nonce_status_cb, &cn_parm); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_pubkey, NULL, buf, len); xfree (buf); } return err; } /* Call the agent to read the public key part for a given keygrip. * Values from FROMCARD: * 0 - Standard * 1 - The key is read from the current card * via the agent and a stub file is created. */ gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, unsigned char **r_pubkey) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; if (fromcard) snprintf (line, DIM(line), "READKEY --card -- %s", hexkeygrip); else snprintf (line, DIM(line), "READKEY -- %s", hexkeygrip); init_membuf (&data, 1024); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Call the agent to do a sign operation using the key identified by the hex string KEYGRIP. DESC is a description of the key to be displayed if the agent needs to ask for the PIN. DIGEST and DIGESTLEN is the hash value to sign and DIGESTALGO the algorithm id used to compute the digest. If CACHE_NONCE is used the agent is advised to first try a passphrase associated with that nonce. */ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, unsigned char *digest, size_t digestlen, int digestalgo, gcry_sexp_t *r_sigval) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; *r_sigval = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, DIM(line), "SIGKEY %s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, sizeof line, "SETHASH %d ", digestalgo); bin2hex (digest, digestlen, line + strlen (line)); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; init_membuf (&data, 1024); snprintf (line, sizeof line, "PKSIGN%s%s", cache_nonce? " -- ":"", cache_nonce? cache_nonce:""); if (DBG_CLOCK) log_clock ("enter signing"); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (DBG_CLOCK) log_clock ("leave signing"); if (err) xfree (get_membuf (&data, NULL)); else { unsigned char *buf; size_t len; buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_sigval, NULL, buf, len); xfree (buf); } } return err; } /* Handle a CIPHERTEXT inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the END. */ static gpg_error_t inq_ciphertext_cb (void *opaque, const char *line) { struct cipher_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "CIPHERTEXT")) { assuan_begin_confidential (parm->ctx); rc = assuan_send_data (parm->dflt->ctx, parm->ciphertext, parm->ciphertextlen); assuan_end_confidential (parm->ctx); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Check whether there is any padding info from the agent. */ static gpg_error_t padding_info_cb (void *opaque, const char *line) { int *r_padding = opaque; const char *s; if ((s=has_leading_keyword (line, "PADDING"))) { *r_padding = atoi (s); } return 0; } /* Call the agent to do a decrypt operation using the key identified by the hex string KEYGRIP and the input data S_CIPHERTEXT. On the success the decoded value is stored verbatim at R_BUF and its length at R_BUF; the callers needs to release it. KEYID, MAINKEYID and PUBKEY_ALGO are used to construct additional promots or status messages. The padding information is stored at R_PADDING with -1 for not known. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, gcry_sexp_t s_ciphertext, unsigned char **r_buf, size_t *r_buflen, int *r_padding) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t n, len; char *p, *buf, *endp; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, sizeof line, "SETKEY %s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } init_membuf_secure (&data, 1024); { struct cipher_parm_s parm; parm.dflt = &dfltparm; parm.ctx = agent_ctx; err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); if (err) return err; err = assuan_transact (agent_ctx, "PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); xfree (parm.ciphertext); } if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); if (len == 0 || *buf != '(') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } if (len < 12 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)" */ { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } while (buf[len-1] == 0) len--; if (buf[len-1] != ')') return gpg_error (GPG_ERR_INV_SEXP); len--; /* Drop the final close-paren. */ p = buf + 8; /* Skip leading parenthesis and the value tag. */ len -= 8; /* Count only the data of the second part. */ n = strtoul (p, &endp, 10); if (!n || *endp != ':') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } endp++; if (endp-p+n > len) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */ } memmove (buf, endp, n); *r_buflen = n; *r_buf = buf; return 0; } /* Retrieve a key encryption key from the agent. With FOREXPORT true the key shall be used for export, with false for import. On success the new key is stored at R_KEY and its length at R_KEKLEN. */ gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_kek = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "KEYWRAP_KEY %s", forexport? "--export":"--import"); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_kek = buf; *r_keklen = len; return 0; } /* Handle the inquiry for an IMPORT_KEY command. */ static gpg_error_t inq_import_key_parms (void *opaque, const char *line) { struct import_key_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYDATA")) { err = assuan_send_data (parm->dflt->ctx, parm->key, parm->keylen); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to import a key into the agent. */ gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr, const void *key, size_t keylen, int unattended, int force, u32 *keyid, u32 *mainkeyid, int pubkey_algo, u32 timestamp) { gpg_error_t err; struct import_key_parm_s parm; struct cache_nonce_parm_s cn_parm; char timestamparg[16 + 16]; /* The 2nd 16 is sizeof(gnupg_isotime_t) */ char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; /* Do not use our cache of secret keygrips anymore - this command * would otherwise requiring to update that cache. */ if (ctrl && ctrl->secret_keygrips) { xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = 0; } if (timestamp) { strcpy (timestamparg, " --timestamp="); epoch2isotime (timestamparg+13, timestamp); } else *timestamparg = 0; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } parm.dflt = &dfltparm; parm.key = key; parm.keylen = keylen; snprintf (line, sizeof line, "IMPORT_KEY%s%s%s%s%s", *timestamparg? timestamparg : "", unattended? " --unattended":"", force? " --force":"", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, NULL, NULL, inq_import_key_parms, &parm, cache_nonce_status_cb, &cn_parm); return err; } /* Receive a secret key from the agent. HEXKEYGRIP is the hexified keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). if OPENPGP_PROTECTED is not zero, ensure that the key material is returned in RFC 4880-compatible passphrased-protected form; if instead MODE1003 is not zero the raw gpg-agent private key format is requested (either protected or unprotected). If CACHE_NONCE_ADDR is not NULL the agent is advised to first try a passphrase associated with that nonce. On success the key is stored as a canonical S-expression at R_RESULT and R_RESULTLEN. */ gpg_error_t agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int openpgp_protected, int mode1003, char **cache_nonce_addr, unsigned char **r_result, size_t *r_resultlen, u32 *keyid, u32 *mainkeyid, int pubkey_algo) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; *r_result = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; /* Check that the gpg-agent supports the --mode1003 option. */ if (mode1003 && assuan_transact (agent_ctx, "GETINFO cmd_has_option EXPORT_KEY mode1003", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line), "EXPORT_KEY %s%s%s %s", mode1003? "--mode1003" : openpgp_protected ? "--openpgp ":"", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); init_membuf_secure (&data, 1024); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_result = buf; *r_resultlen = len; return 0; } /* Status callback for handling confirmation. */ static gpg_error_t confirm_status_cb (void *opaque, const char *line) { struct confirm_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "SETDESC"))) { xfree (parm->desc); parm->desc = unescape_status_string (s); } else if ((s = has_leading_keyword (line, "SETOK"))) { xfree (parm->ok); parm->ok = unescape_status_string (s); } else if ((s = has_leading_keyword (line, "SETNOTOK"))) { xfree (parm->notok); parm->notok = unescape_status_string (s); } return 0; } /* Ask the agent to delete the key identified by HEXKEYGRIP. If DESC is not NULL, display DESC instead of the default description message. If FORCE is true the agent is advised not to ask for confirmation. */ gpg_error_t agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int force) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; struct confirm_parm_s confirm_parm; memset (&confirm_parm, 0, sizeof confirm_parm); memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.confirm = &confirm_parm; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } /* FIXME: Shall we add support to DELETE_KEY for dual keys? */ snprintf (line, DIM(line), "DELETE_KEY%s %s", force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, confirm_status_cb, &confirm_parm); xfree (confirm_parm.desc); xfree (confirm_parm.ok); xfree (confirm_parm.notok); return err; } /* Ask the agent to change the passphrase of the key identified by * HEXKEYGRIP. If DESC is not NULL, display DESC instead of the * default description message. If CACHE_NONCE_ADDR is not NULL the * agent is advised to first try a passphrase associated with that * nonce. If PASSWD_NONCE_ADDR is not NULL the agent will try to use * the passphrase associated with that nonce for the new passphrase. * If VERIFY is true the passphrase is only verified. */ gpg_error_t agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int verify, char **cache_nonce_addr, char **passwd_nonce_addr) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } if (verify) snprintf (line, DIM(line), "PASSWD %s%s --verify %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); else snprintf (line, DIM(line), "PASSWD %s%s %s%s %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", passwd_nonce_addr && *passwd_nonce_addr? "--passwd-nonce=":"", passwd_nonce_addr && *passwd_nonce_addr? *passwd_nonce_addr:"", hexkeygrip); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = passwd_nonce_addr; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); return err; } /* Enable or disable the ephemeral mode. In ephemeral mode keys are * created,searched and used in a per-session key store and not in the * on-disk file. Set ENABLE to 1 to enable this mode, to 0 to disable * this mode and to -1 to only query the current mode. If R_PREVIOUS * is given the previously used state of the ephemeral mode is stored * at that address. */ gpg_error_t agent_set_ephemeral_mode (ctrl_t ctrl, int enable, int *r_previous) { gpg_error_t err; err = start_agent (ctrl, 0); if (err) goto leave; if (r_previous) { err = assuan_transact (agent_ctx, "GETINFO ephemeral", NULL, NULL, NULL, NULL, NULL, NULL); if (!err) *r_previous = 1; else if (gpg_err_code (err) == GPG_ERR_FALSE) *r_previous = 0; else goto leave; } /* Skip setting if we are only querying or if the mode is already set. */ if (enable == -1 || (r_previous && !!*r_previous == !!enable)) err = 0; else err = assuan_transact (agent_ctx, enable? "OPTION ephemeral=1" : "OPTION ephemeral=0", NULL, NULL, NULL, NULL, NULL, NULL); leave: return err; } /* Return the version reported by gpg-agent. */ gpg_error_t agent_get_version (ctrl_t ctrl, char **r_version) { gpg_error_t err; err = start_agent (ctrl, 0); if (err) return err; err = get_assuan_server_version (agent_ctx, 0, r_version); return err; } diff --git a/g10/keygen.c b/g10/keygen.c index 6426f7e4f..c2acf74c3 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,6688 +1,6760 @@ /* keygen.c - Generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2015, 2016, 2017, 2018 Werner Koch - * Copyright (C) 2020 g10 Code GmbH + * Copyright (C) 2020, 2024 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 #include #include #include #include "gpg.h" #include "../common/util.h" #include "main.h" #include "packet.h" #include "../common/ttyio.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "../common/status.h" #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "pkglue.h" #include "../common/shareddefs.h" #include "../common/host2net.h" #include "../common/mbox-util.h" /* The default algorithms. If you change them, you should ensure the value is inside the bounds enforced by ask_keysize and gen_xxx. See also get_keysize_range which encodes the allowed ranges. The default answer in ask_algo also needs to be adjusted. */ #define DEFAULT_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr" #define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr" /* When generating keys using the streamlined key generation dialog, use this as a default expiration interval. */ const char *default_expiration_interval = "3y"; /* Flag bits used during key generation. */ #define KEYGEN_FLAG_NO_PROTECTION 1 #define KEYGEN_FLAG_TRANSIENT_KEY 2 #define KEYGEN_FLAG_CREATE_V5_KEY 4 /* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 enum para_name { pKEYTYPE, pKEYLENGTH, pKEYCURVE, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, pSUBKEYCURVE, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, pPREFERENCES, pREVOKER, pUSERID, pCREATIONDATE, pKEYCREATIONDATE, /* Same in seconds since epoch. */ pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ pSUBKEYCREATIONDATE, pSUBKEYEXPIREDATE, pSUBKEYEXPIRE, /* in n seconds */ pAUTHKEYCREATIONDATE, /* Not yet used. */ pPASSPHRASE, pSERIALNO, pCARDBACKUPKEY, pHANDLE, pKEYSERVER, pKEYGRIP, pSUBKEYGRIP, pVERSION, /* Desired version of the key packet. */ pSUBVERSION, /* Ditto for the subpacket. */ pCARDKEY /* The keygrips have been taken from active card (bool). */ }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { u32 expire; u32 creation; int abool; unsigned int usage; struct revocation_key revkey; char value[1]; } u; }; struct output_control_s { int lnr; int dryrun; unsigned int keygen_flags; int use_files; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t *afx; } pub; }; /* An object to help communicating with the actual key generation * code. */ struct common_gen_cb_parm_s { /* This variable set to the result of agent_genkey. The callback * may take a copy of this so that the result can be used after we * are back from the deep key generation call stack. */ gcry_sexp_t genkey_result; /* For a dual algorithms the result of the second algorithm * (e.g. Kyber). */ gcry_sexp_t genkey_result2; }; typedef struct common_gen_cb_parm_s *common_gen_cb_parm_t; /* FIXME: These globals vars are ugly. And using MAX_PREFS even for * aeads is useless, given that we don't expects more than a very few * algorithms. */ static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static byte aead_prefs[MAX_PREFS]; static int naead_prefs; static int mdc_available; static int ks_modify; static int aead_available; static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, const char *expirestr, int *r_algo, unsigned int *r_usage, u32 *r_expire, unsigned int *r_nbits, const char **r_curve, int *r_version, char **r_keygrip, u32 *r_keytime); static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, struct output_control_s *outctrl, int card ); static int write_keyblock (iobuf_t out, kbnode_t node); static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval, int *keygen_flags); static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max); /* Return the algo string for a default new key. */ const char * get_default_pubkey_algo (void) { if (opt.def_new_key_algo) { if (*opt.def_new_key_algo && !strchr (opt.def_new_key_algo, ':')) return opt.def_new_key_algo; /* To avoid checking that option every time we delay that until * here. The only thing we really need to make sure is that * there is no colon in the string so that the --gpgconf-list * command won't mess up its output. */ log_info (_("invalid value for option '%s'\n"), "--default-new-key-algo"); } return DEFAULT_STD_KEY_PARAM; } static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; if (!handle) handle = ""; buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); p = buf; if (letter || pk) { *p++ = letter; if (pk) { *p++ = ' '; fingerprint_from_pk (pk, array, &n); s = array; /* Fixme: Use bin2hex */ for (i=0; i < n ; i++, s++, p += 2) snprintf (p, 3, "%02X", *s); } } if (*handle) { *p++ = ' '; for (i=0; handle[i] && i < 100; i++) *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; } *p = 0; write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, buf); xfree (buf); } static void print_status_key_not_created (const char *handle) { print_status_key_created (0, NULL, handle); } static gpg_error_t write_uid (kbnode_t root, const char *s) { PACKET *pkt = NULL; size_t n = strlen (s); if (n > MAX_UID_PACKET_LENGTH - 10) return gpg_error (GPG_ERR_INV_USER_ID); pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy (pkt->pkt.user_id->name, s); add_kbnode (root, new_kbnode (pkt)); return 0; } static void do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[2] = { 0, 0 }; /* The spec says that all primary keys MUST be able to certify. */ if ( sig->sig_class != 0x18 ) buf[0] |= 0x01; if (use & PUBKEY_USAGE_SIG) buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; if (use & PUBKEY_USAGE_GROUP) buf[0] |= 0x80; if (use & PUBKEY_USAGE_RENC) buf[1] |= 0x04; if (use & PUBKEY_USAGE_TIME) buf[1] |= 0x08; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, buf[1]? 2:1); } int keygen_add_key_expire (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if (pk->expiredate) { if (pk->expiredate > pk->timestamp) u = pk->expiredate - pk->timestamp; else u = 1; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } else { /* Make sure we don't leave a key expiration subpacket lying around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); } return 0; } /* Add the key usage (i.e. key flags) in SIG from the public keys * pubkey_usage field. OPAQUE has the public key. */ int keygen_add_key_flags (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); return 0; } int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { keygen_add_key_flags (sig, opaque); return keygen_add_key_expire (sig, opaque); } static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { int i; for (i=0; i < *nbuf; i++ ) if (buf[i] == val) { log_info (_("preference '%s' duplicated\n"), item); return -1; } if (*nbuf >= MAX_PREFS) { if(type==1) log_info(_("too many cipher preferences\n")); else if(type==2) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); else if(type==4) log_info(_("too many AEAD preferences\n")); else BUG(); return -1; } buf[(*nbuf)++] = val; return 0; } /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual * cipher/hash/compress names. Use NULL to set the default * preferences. Returns: 0 = okay */ int keygen_set_std_prefs (const char *string,int personal) { byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS], aead[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, naead=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ char dummy_string[25*4+1]; /* Enough for 25 items. */ if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) string=opt.def_preference_list; else { int any_compress = 0; dummy_string[0]='\0'; /* The rationale why we use the order AES256,192,128 is for compatibility reasons with PGP. If gpg would define AES128 first, we would get the somewhat confusing situation: gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ /* Make sure we do not add more than 15 items here, as we could overflow the size of dummy_string. We currently have at most 12. */ if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES256) ) strcat(dummy_string,"S9 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES192) ) strcat(dummy_string,"S8 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES) ) strcat(dummy_string,"S7 "); strcat(dummy_string,"S2 "); /* 3DES */ if (!openpgp_aead_test_algo (AEAD_ALGO_OCB)) strcat(dummy_string,"A2 "); if (personal) { /* The default internal hash algo order is: * SHA-256, SHA-384, SHA-512, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); } else { /* The default advertised hash algo order is: * SHA-512, SHA-384, SHA-256, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); } if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224)) strcat (dummy_string, "H11 "); strcat (dummy_string, "H2 "); /* SHA-1 */ if(!check_compress_algo(COMPRESS_ALGO_ZLIB)) { strcat(dummy_string,"Z2 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) { strcat(dummy_string,"Z3 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_ZIP)) { strcat(dummy_string,"Z1 "); any_compress = 1; } /* In case we have no compress algo at all, declare that we prefer no compression. */ if (!any_compress) strcat(dummy_string,"Z0 "); /* Remove the trailing space. */ if (*dummy_string && dummy_string[strlen (dummy_string)-1] == ' ') dummy_string[strlen (dummy_string)-1] = 0; string=dummy_string; } } else if (!ascii_strcasecmp (string, "none")) string = ""; if(strlen(string)) { char *prefstringbuf; char *tok, *prefstring; /* We need a writable string. */ prefstring = prefstringbuf = xstrdup (string); while((tok=strsep(&prefstring," ,"))) { if (!*tok) ; else if((val=string_to_cipher_algo (tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } else if((val=string_to_digest_algo (tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; } else if((val=string_to_compress_algo(tok))>-1) { if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } else if ((val=string_to_aead_algo (tok))) { if (set_one_pref (val, 4, tok, aead, &naead)) rc = -1; } else if (!ascii_strcasecmp(tok, "mdc") || !ascii_strcasecmp(tok, "[mdc]")) mdc=1; else if (!ascii_strcasecmp(tok, "no-mdc") || !ascii_strcasecmp(tok, "[no-mdc]")) mdc=0; else if (!ascii_strcasecmp(tok, "ks-modify") || !ascii_strcasecmp(tok, "[ks-modify]")) modify=1; else if (!ascii_strcasecmp(tok,"no-ks-modify") || !ascii_strcasecmp(tok,"[no-ks-modify]")) modify=0; else if (!ascii_strcasecmp(tok,"aead") || !ascii_strcasecmp(tok,"[aead]")) { /* Ignore because this is set from the preferences but * shown in the in the preferences/features list. */ } else { log_info (_("invalid item '%s' in preference string\n"),tok); rc=-1; } } xfree (prefstringbuf); } if(!rc) { if(personal) { if(personal==PREFTYPE_SYM) { xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; else { int i; opt.personal_cipher_prefs= xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; iref=1; uid->prefs = xmalloc ((sizeof(prefitem_t *)* (nsym_prefs+naead_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;iprefs[j].type=PREFTYPE_SYM; uid->prefs[j].value=sym_prefs[i]; } for (i=0; i < naead_prefs; i++, j++) { uid->prefs[j].type = PREFTYPE_AEAD; uid->prefs[j].value = aead_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_HASH; uid->prefs[j].value=hash_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_ZIP; uid->prefs[j].value=zip_prefs[i]; } uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; uid->flags.mdc = mdc_available; uid->flags.aead = aead_available; uid->flags.ks_modify = ks_modify; return uid; } static void add_feature_mdc (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig, 1, SIGSUBPKT_FEATURES, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x01; /* MDC feature */ else buf[0] &= ~0x01; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_feature_aead (PKT_signature *sig, int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig, 1, SIGSUBPKT_FEATURES, &n ); if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02)))) return; /* Already set or cleared */ if (!s || !n) { /* Create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if (enabled) buf[0] |= 0x02; /* AEAD supported */ else buf[0] &= ~0x02; /* Are there any bits set? */ for (i=0; i < n; i++) if (buf[i]) break; if (i == n) delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_feature_v5 (PKT_signature *sig, int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig, 1, SIGSUBPKT_FEATURES, &n ); if (s && n && ((enabled && (s[0] & 0x04)) || (!enabled && !(s[0] & 0x04)))) return; /* Already set or cleared */ if (!s || !n) { /* Create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if (enabled) buf[0] |= 0x04; /* v5 key supported */ else buf[0] &= ~0x04; /* Are there any bits set? */ for (i=0; i < n; i++) if (buf[i]) break; if (i == n) delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_keyserver_modify (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; /* The keyserver modify flag is a negative flag (i.e. no-modify) */ enabled=!enabled; s = parse_sig_subpkt (sig, 1, SIGSUBPKT_KS_FLAGS, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x80; /* no-modify flag */ else buf[0] &= ~0x80; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); else build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); xfree (buf); } int keygen_upd_std_prefs (PKT_signature *sig, void *opaque) { (void)opaque; if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } if (naead_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, aead_prefs, naead_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD); } if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); } if (nzip_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); add_feature_aead (sig, aead_available); add_feature_v5 (sig, 1); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire (sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url (sig,NULL); return 0; } int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; if(!url) url=opt.def_keyserver_url; if(url) build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); else delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } int keygen_add_notations(PKT_signature *sig,void *opaque) { struct notation *notation; /* We always start clean */ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); sig->flags.notation=0; for(notation=opaque;notation;notation=notation->next) if(!notation->flags.ignore) { unsigned char *buf; unsigned int n1,n2; n1=strlen(notation->name); if(notation->altvalue) n2=strlen(notation->altvalue); else if(notation->bdat) n2=notation->blen; else n2=strlen(notation->value); buf = xmalloc( 8 + n1 + n2 ); /* human readable or not */ buf[0] = notation->bdat?0:0x80; buf[1] = buf[2] = buf[3] = 0; buf[4] = n1 >> 8; buf[5] = n1; buf[6] = n2 >> 8; buf[7] = n2; memcpy(buf+8, notation->name, n1 ); if(notation->altvalue) memcpy(buf+8+n1, notation->altvalue, n2 ); else if(notation->bdat) memcpy(buf+8+n1, notation->bdat, n2 ); else memcpy(buf+8+n1, notation->value, n2 ); build_sig_subpkt( sig, SIGSUBPKT_NOTATION | (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), buf, 8+n1+n2 ); xfree(buf); } return 0; } int keygen_add_revkey (PKT_signature *sig, void *opaque) { struct revocation_key *revkey = opaque; byte buf[2+MAX_FINGERPRINT_LEN]; log_assert (revkey->fprlen <= MAX_FINGERPRINT_LEN); buf[0] = revkey->class; buf[1] = revkey->algid; memcpy (buf + 2, revkey->fpr, revkey->fprlen); memset (buf + 2 + revkey->fprlen, 0, sizeof (revkey->fpr) - revkey->fprlen); build_sig_subpkt (sig, SIGSUBPKT_REV_KEY, buf, 2+revkey->fprlen); /* All sigs with revocation keys set are nonrevocable. */ sig->flags.revocable = 0; buf[0] = 0; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); parse_revkeys (sig); return 0; } /* Create a back-signature. If TIMESTAMP is not NULL, use it for the signature creation time. */ 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 err; PKT_signature *backsig; cache_public_key (sub_pk); err = make_keysig_packet (ctrl, &backsig, pk, NULL, sub_pk, sub_psk, 0x19, timestamp, 0, NULL, NULL, cache_nonce); if (err) log_error ("make_keysig_packet failed for backsig: %s\n", gpg_strerror (err)); else { /* Get it into a binary packed form. */ IOBUF backsig_out = iobuf_temp(); PACKET backsig_pkt; init_packet (&backsig_pkt); backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; err = build_packet (backsig_out, &backsig_pkt); free_packet (&backsig_pkt, NULL); if (err) log_error ("build_packet failed for backsig: %s\n", gpg_strerror (err)); else { size_t pktlen = 0; byte *buf = iobuf_get_temp_buffer (backsig_out); /* Remove the packet header. */ if(buf[0]&0x40) { if (buf[1] < 192) { pktlen = buf[1]; buf += 2; } else if(buf[1] < 224) { pktlen = (buf[1]-192)*256; pktlen += buf[2]+192; buf += 3; } else if (buf[1] == 255) { pktlen = buf32_to_size_t (buf+2); buf += 6; } else BUG (); } else { int mark = 1; switch (buf[0]&3) { case 3: BUG (); break; case 2: pktlen = (size_t)buf[mark++] << 24; pktlen |= buf[mark++] << 16; /* fall through */ case 1: pktlen |= buf[mark++] << 8; /* fall through */ case 0: pktlen |= buf[mark++]; } buf += mark; } /* Now make the binary blob into a subpacket. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); iobuf_close (backsig_out); } } return err; } /* Write a direct key signature to the first key in ROOT using the key PSK. REVKEY is describes the direct key signature and TIMESTAMP is the timestamp to set on the signature. */ static gpg_error_t write_direct_sig (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, struct revocation_key *revkey, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing direct signature\n")); /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, NULL,NULL, psk, 0x1F, timestamp, 0, keygen_add_revkey, revkey, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err) ); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write a self-signature to the first user id in ROOT using the key PSK. USE and TIMESTAMP give the extra data we need for the signature. */ static gpg_error_t write_selfsigs (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing self signature\n")); /* Get the uid packet from the list. */ node = find_kbnode (root, PKT_USER_ID); if (!node) BUG(); /* No user id packet in tree. */ uid = node->pkt->pkt.user_id; /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pk = node->pkt->pkt.public_key; /* The usage has not yet been set - do it now. */ pk->pubkey_usage = use; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, psk, 0x13, timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err)); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write the key binding signature. If TIMESTAMP is not NULL use the signature creation time. PRI_PSK is the key use for signing. SUB_PSK is a key used to create a back-signature; that one is only used if USE has the PUBKEY_USAGE_SIG capability. */ static int write_keybinding (ctrl_t ctrl, kbnode_t root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; if (opt.verbose) log_info(_("writing key binding signature\n")); /* Get the primary pk packet from the tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pri_pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the * signature creation is able to retrieve the public key. */ cache_public_key (pri_pk); /* Find the last subkey. */ sub_pk = NULL; for (node = root; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; } if (!sub_pk) BUG(); /* Make the signature. */ sub_pk->pubkey_usage = use; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, timestamp, 0, keygen_add_key_flags_and_expire, sub_pk, cache_nonce); if (err) { log_error ("make_keysig_packeto failed: %s\n", gpg_strerror (err)); return err; } /* Make a backsig. */ if (use & PUBKEY_USAGE_SIG) { err = make_backsig (ctrl, sig, pri_pk, sub_pk, sub_psk, timestamp, cache_nonce); if (err) return err; } pkt = xmalloc_clear ( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt) ); return err; } /* Returns true if SEXP specified the curve ED448 or X448. */ static int curve_is_448 (gcry_sexp_t sexp) { gcry_sexp_t list, l2; char *curve; int result; list = gcry_sexp_find_token (sexp, "public-key", 0); if (!list) return 0; /* Not a public key. */ l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return 0; /* Bad public key. */ l2 = gcry_sexp_find_token (list, "curve", 0); gcry_sexp_release (list); if (!l2) return 0; /* No curve parameter. */ curve = gcry_sexp_nth_string (l2, 1); gcry_sexp_release (l2); if (!curve) return 0; /* Bad curve parameter. */ result = (!ascii_strcasecmp (curve, "X448") || !ascii_strcasecmp (curve, "Ed448") || !ascii_strcasecmp (curve, "cv448")); xfree (curve); return result; } /* Extract the parameters in OpenPGP format from SEXP and put them * into the caller provided ARRAY. SEXP2 is used to provide the * parameters for dual algorithm (e.g. Kyber). */ static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, gcry_sexp_t sexp2, int algo) { gpg_error_t err; gcry_sexp_t list, l2; char *curve = NULL; int i; const char *oidstr; unsigned int nbits; array[0] = NULL; array[1] = NULL; array[2] = NULL; list = gcry_sexp_find_token (sexp, "public-key", 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); l2 = gcry_sexp_find_token (list, "curve", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } curve = gcry_sexp_nth_string (l2, 1); if (!curve) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } gcry_sexp_release (l2); oidstr = openpgp_curve_to_oid (curve, &nbits, NULL); if (!oidstr) { /* That can't happen because we used one of the curves gpg_curve_to_oid knows about. */ err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; err = sexp_extract_param_sos (list, "q", &array[1]); if (err) goto leave; gcry_sexp_release (list); list = NULL; if (algo == PUBKEY_ALGO_KYBER) { if (!sexp2) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } list = gcry_sexp_find_token (sexp2, "public-key", 0); if (!list) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } l2 = gcry_sexp_find_token (list, "p", 1); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } array[2] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); gcry_sexp_release (l2); if (!array[2]) { err = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; } } else if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) { err = gpg_error_from_syserror (); goto leave; } } leave: xfree (curve); gcry_sexp_release (list); if (err) { for (i=0; i < 3; i++) { gcry_mpi_release (array[i]); array[i] = NULL; } } return err; } /* Extract key parameters from SEXP and store them in ARRAY. ELEMS is a string where each character denotes a parameter name. TOPNAME is the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) { gcry_sexp_t list, l2; const char *s; int i, idx; int rc = 0; list = gcry_sexp_find_token (sexp, topname, 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); for (idx=0,s=elems; *s; s++, idx++) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { rc = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; } } gcry_sexp_release (list); leave: if (rc) { for (i=0; itimestamp = timestamp; pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo); + else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); + xfree (hexkeygrip_buffer); return 0; } /* Common code for the key generation function gen_xxx. The optional * (COMMON_GEN_CB,COMMON_GEN_CB_PARM) can be used as communication * object. A KEYPARMS2 forces the use of a dual key (e.g. Kyber+ECC). */ static int common_gen (const char *keyparms, const char *keyparms2, int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { int err; PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; gcry_sexp_t s_key2 = NULL; err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), passphrase, timestamp, &s_key); if (err) { log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); return err; } if (keyparms2) { err = agent_genkey (NULL, NULL, NULL, keyparms2, 1 /* No protection */, NULL, timestamp, &s_key2); if (err) { log_error ("agent_genkey failed for second algo: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); return err; } } if (common_gen_cb && common_gen_cb_parm) { common_gen_cb_parm->genkey_result = s_key; common_gen_cb_parm->genkey_result2 = s_key2; err = common_gen_cb (common_gen_cb_parm); common_gen_cb_parm->genkey_result = NULL; common_gen_cb_parm->genkey_result2 = NULL; if (err) { gcry_sexp_release (s_key); gcry_sexp_release (s_key2); return err; } } pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); return err; } pk->timestamp = timestamp; pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_KYBER) err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } gcry_sexp_release (s_key); gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* * Generate an Elgamal key. */ static int gen_elg (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { int err; char *keyparms; char nbitsstr[35]; log_assert (is_ELGAMAL (algo)); if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } /* Note that we use transient-key only if no-protection has also been enabled. */ snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)%s))", algo == GCRY_PK_ELG_E ? "openpgp-elg" : algo == GCRY_PK_ELG ? "elg" : "x-oops" , strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, NULL, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); xfree (keyparms); } return err; } /* * Generate an DSA key */ static gpg_error_t gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { int err; unsigned int qbits; char *keyparms; char nbitsstr[35]; char qbitsstr[35]; if (nbits < 768) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if ( nbits > 3072 ) { nbits = 3072; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* To comply with FIPS rules we round up to the next value unless in expert mode. */ if (!opt.expert && nbits > 1024 && (nbits % 1024)) { nbits = ((nbits + 1023) / 1024) * 1024; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* Figure out a q size based on the key size. FIPS 180-3 says: L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ if (nbits > 2047) qbits = 256; else if ( nbits > 1024) qbits = 224; else qbits = 160; if (qbits != 160 ) log_info (_("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n")); snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); keyparms = xtryasprintf ("(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, strlen (qbitsstr), qbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, NULL, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); xfree (keyparms); } return err; } /* * Generate an ECC key. * Note that KEYGEN_FLAGS might be updated by this function to * indicate the forced creation of a v5 key. */ static gpg_error_t gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int *keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { gpg_error_t err; char *keyparms; log_assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Map the displayed short forms of some curves to their canonical * names. */ if (!ascii_strcasecmp (curve, "cv25519")) curve = "Curve25519"; else if (!ascii_strcasecmp (curve, "ed25519")) curve = "Ed25519"; else if (!ascii_strcasecmp (curve, "cv448")) curve = "X448"; else if (!ascii_strcasecmp (curve, "ed448")) curve = "Ed448"; /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ if (algo == PUBKEY_ALGO_EDDSA && !strcmp (curve, "Ed25519")) { keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else if (algo == PUBKEY_ALGO_EDDSA && !strcmp (curve, "Ed448")) { *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "Curve25519")) { keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "X448")) { *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else { keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, NULL, algo, "", pub_root, timestamp, expireval, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); xfree (keyparms); } return err; } /* Generate a dual ECC+Kyber key. Note that KEYGEN_FLAGS will be * updated by this function to indicate the forced creation of a v5 * key. */ static gpg_error_t gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int *keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { gpg_error_t err; char *keyparms1; const char *keyparms2; log_assert (algo == PUBKEY_ALGO_KYBER); if (nbits == 768) keyparms2 = "(genkey(kyber768))"; else if (nbits == 1024) keyparms2 = "(genkey(kyber1024))"; else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (!strcmp (curve, "Curve25519")) { keyparms1 = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else if (!strcmp (curve, "X448")) { keyparms1 = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } else /* Should we use the compressed format? Check smartcard support. */ { keyparms1 = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", strlen (curve), curve, (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } if (!keyparms1) err = gpg_error_from_syserror (); else { err = common_gen (keyparms1, keyparms2, algo, "", pub_root, timestamp, expireval, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); xfree (keyparms1); } return err; } /* * Generate an RSA key. */ static int gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { int err; char *keyparms; char nbitsstr[35]; const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); log_assert (is_RSA(algo)); if (!nbits) nbits = get_keysize_range (algo, NULL, NULL); if (nbits < 1024) { nbits = 3072; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > maxsize) { nbits = maxsize; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, NULL, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); xfree (keyparms); } return err; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !digitp(s) ) return 0; for( s++; *s; s++) if( !digitp(s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } static void print_key_flags(int flags) { if(flags&PUBKEY_USAGE_SIG) tty_printf("%s ",_("Sign")); if(flags&PUBKEY_USAGE_CERT) tty_printf("%s ",_("Certify")); if(flags&PUBKEY_USAGE_ENC) tty_printf("%s ",_("Encrypt")); if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); } /* Ask for the key flags and return them. CURRENT gives the current * usage which should normally be given as 0. MASK gives the allowed * flags. */ unsigned int ask_key_flags_with_mask (int algo, int subkey, unsigned int current, unsigned int mask) { /* TRANSLATORS: Please use only plain ASCII characters for the * translation. If this is not possible use single digits. The * string needs to 8 bytes long. Here is a description of the * functions: * * s = Toggle signing capability * e = Toggle encryption capability * a = Toggle authentication capability * q = Finish */ const char *togglers = _("SsEeAaQq"); char *answer = NULL; const char *s; unsigned int possible; if ( strlen(togglers) != 8 ) { tty_printf ("NOTE: Bad translation at %s:%d. " "Please report.\n", __FILE__, __LINE__); togglers = "11223300"; } /* Mask the possible usage flags. This is for example used for a * card based key. For ECDH we need to allows additional usages if * they are provided. */ possible = (openpgp_pk_algo_usage (algo) & mask); if (algo == PUBKEY_ALGO_ECDH) possible |= (current & (PUBKEY_USAGE_ENC |PUBKEY_USAGE_CERT |PUBKEY_USAGE_SIG |PUBKEY_USAGE_AUTH)); /* However, only primary keys may certify. */ if (subkey) possible &= ~PUBKEY_USAGE_CERT; /* Preload the current set with the possible set, without * authentication if CURRENT is 0. If CURRENT is non-zero we mask * with all possible usages. */ if (current) current &= possible; else current = (possible&~PUBKEY_USAGE_AUTH); for (;;) { tty_printf("\n"); tty_printf(_("Possible actions for this %s key: "), (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) ? "ECC" : openpgp_pk_algo_name (algo)); print_key_flags(possible); tty_printf("\n"); tty_printf(_("Current allowed actions: ")); print_key_flags(current); tty_printf("\n\n"); if(possible&PUBKEY_USAGE_SIG) tty_printf(_(" (%c) Toggle the sign capability\n"), togglers[0]); if(possible&PUBKEY_USAGE_ENC) tty_printf(_(" (%c) Toggle the encrypt capability\n"), togglers[2]); if(possible&PUBKEY_USAGE_AUTH) tty_printf(_(" (%c) Toggle the authenticate capability\n"), togglers[4]); tty_printf(_(" (%c) Finished\n"),togglers[6]); tty_printf("\n"); xfree(answer); answer = cpr_get("keygen.flags",_("Your selection? ")); cpr_kill_prompt(); if (*answer == '=') { /* Hack to allow direct entry of the capabilities. */ current = 0; for (s=answer+1; *s; s++) { if ((*s == 's' || *s == 'S') && (possible&PUBKEY_USAGE_SIG)) current |= PUBKEY_USAGE_SIG; else if ((*s == 'e' || *s == 'E') && (possible&PUBKEY_USAGE_ENC)) current |= PUBKEY_USAGE_ENC; else if ((*s == 'a' || *s == 'A') && (possible&PUBKEY_USAGE_AUTH)) current |= PUBKEY_USAGE_AUTH; else if (!subkey && *s == 'c') { /* Accept 'c' for the primary key because USAGE_CERT will be set anyway. This is for folks who want to experiment with a cert-only primary key. */ current |= PUBKEY_USAGE_CERT; } } break; } else if (strlen(answer)>1) tty_printf(_("Invalid selection.\n")); else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) break; else if((*answer==togglers[0] || *answer==togglers[1]) && possible&PUBKEY_USAGE_SIG) { if(current&PUBKEY_USAGE_SIG) current&=~PUBKEY_USAGE_SIG; else current|=PUBKEY_USAGE_SIG; } else if((*answer==togglers[2] || *answer==togglers[3]) && possible&PUBKEY_USAGE_ENC) { if(current&PUBKEY_USAGE_ENC) current&=~PUBKEY_USAGE_ENC; else current|=PUBKEY_USAGE_ENC; } else if((*answer==togglers[4] || *answer==togglers[5]) && possible&PUBKEY_USAGE_AUTH) { if(current&PUBKEY_USAGE_AUTH) current&=~PUBKEY_USAGE_AUTH; else current|=PUBKEY_USAGE_AUTH; } else tty_printf(_("Invalid selection.\n")); } xfree(answer); return current; } unsigned int ask_key_flags (int algo, int subkey, unsigned int current) { return ask_key_flags_with_mask (algo, subkey, current, ~0); } /* Check whether we have a key for the key with HEXGRIP. Returns 0 if there is no such key or the OpenPGP algo number for the key. */ static int check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; unsigned char *public; size_t publiclen; int algo; if (hexgrip[0] == '&') hexgrip++; err = agent_readkey (ctrl, 0, hexgrip, &public); if (err) return 0; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); algo = get_pk_algo_from_canon_sexp (public, publiclen); xfree (public); return map_gcry_pk_to_openpgp (algo); } /* Ask for an algorithm. The function returns the algorithm id to * create. If ADDMODE is false the function won't show an option to * create the primary and subkey combined and won't set R_USAGE * either. If a combined algorithm has been selected, the subkey * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the * user has the choice to enter the keygrip of an existing key. That * keygrip is then stored at this address. The caller needs to free * it. If R_CARDKEY is not NULL and the keygrip has been taken from * an active card, true is stored there; if R_KEYTIME is not NULL the * creation time of that key is then stored there. */ static int ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, char **r_keygrip, int *r_cardkey, u32 *r_keytime) { gpg_error_t err; char *keygrip = NULL; u32 keytime = 0; char *answer = NULL; int cardkey = 0; int algo; int dummy_algo; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; tty_printf (_("Please select what kind of key you want:\n")); #if GPG_USE_RSA if (!addmode) tty_printf (_(" (%d) RSA and RSA%s\n"), 1, ""); #endif if (!addmode && opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA and Elgamal%s\n"), 2, ""); if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA (sign only)%s\n"), 3, ""); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (sign only)%s\n"), 4, ""); #endif if (addmode) { if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) Elgamal (encrypt only)%s\n"), 5, ""); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (encrypt only)%s\n"), 6, ""); #endif } if (opt.expert) { if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA (set your own capabilities)%s\n"), 7, ""); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (set your own capabilities)%s\n"), 8, ""); #endif } #if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA if (!addmode) tty_printf (_(" (%d) ECC (sign and encrypt)%s\n"), 9, _(" *default*") ); tty_printf (_(" (%d) ECC (sign only)\n"), 10 ); if (opt.expert) tty_printf (_(" (%d) ECC (set your own capabilities)%s\n"), 11, ""); if (addmode) tty_printf (_(" (%d) ECC (encrypt only)%s\n"), 12, ""); #endif if (opt.expert && r_keygrip) tty_printf (_(" (%d) Existing key%s\n"), 13, ""); if (r_keygrip) tty_printf (_(" (%d) Existing key from card%s\n"), 14, ""); for (;;) { *r_usage = 0; *r_subkey_algo = 0; xfree (answer); answer = cpr_get ("keygen.algo", _("Your selection? ")); cpr_kill_prompt (); algo = *answer? atoi (answer) : 9; /* Default algo is 9 */ if (opt.compliance == CO_DE_VS && (algo == 2 || algo == 3 || algo == 5 || algo == 7)) { tty_printf (_("Invalid selection.\n")); } else if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode) { algo = PUBKEY_ALGO_RSA; *r_subkey_algo = PUBKEY_ALGO_RSA; break; } else if ((algo == 2 || !strcmp (answer, "dsa+elg")) && !addmode) { algo = PUBKEY_ALGO_DSA; *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if (algo == 3 || !strcmp (answer, "dsa")) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 4 || !strcmp (answer, "rsa/s")) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 5 || !strcmp (answer, "elg")) && addmode) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 6 || !strcmp (answer, "rsa/e")) && addmode) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert) { algo = PUBKEY_ALGO_DSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert) { algo = PUBKEY_ALGO_RSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 9 || !strcmp (answer, "ecc+ecc")) && !addmode) { algo = PUBKEY_ALGO_ECDSA; *r_subkey_algo = PUBKEY_ALGO_ECDH; break; } else if ((algo == 10 || !strcmp (answer, "ecc/s"))) { algo = PUBKEY_ALGO_ECDSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 11 || !strcmp (answer, "ecc/*")) && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 12 || !strcmp (answer, "ecc/e")) && addmode) { algo = PUBKEY_ALGO_ECDH; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 13 || !strcmp (answer, "keygrip")) && opt.expert && r_keygrip) { for (;;) { xfree (answer); answer = cpr_get ("keygen.keygrip", _("Enter the keygrip: ")); cpr_kill_prompt (); trim_spaces (answer); if (!*answer) { xfree (answer); answer = NULL; continue; } - if (strlen (answer) != 40 && + if (strlen (answer) == 40+1+40 && answer[40]==',') + { + int algo1, algo2; + + answer[40] = 0; + algo1 = check_keygrip (ctrl, answer); + algo2 = check_keygrip (ctrl, answer+41); + answer[40] = ','; + if (algo1 == PUBKEY_ALGO_ECDH && algo2 == PUBKEY_ALGO_KYBER) + { + algo = PUBKEY_ALGO_KYBER; + break; + } + else if (!algo1 || !algo2) + tty_printf (_("No key with this keygrip\n")); + else + tty_printf ("Invalid combination for dual algo (%d,%d)\n", + algo1, algo2); + } + else if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(algo = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 14 || !strcmp (answer, "cardkey")) && r_keygrip) { char *serialno; keypair_info_t keypairlist, kpi; int count, selection; err = agent_scd_serialno (&serialno, NULL); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto ask_again; } tty_printf (_("Serial number of the card: %s\n"), serialno); xfree (serialno); err = agent_scd_keypairinfo (ctrl, NULL, &keypairlist); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto ask_again; } do { char *authkeyref, *encrkeyref, *signkeyref; agent_scd_getattr_one ("$AUTHKEYID", &authkeyref); agent_scd_getattr_one ("$ENCRKEYID", &encrkeyref); agent_scd_getattr_one ("$SIGNKEYID", &signkeyref); tty_printf (_("Available keys:\n")); for (count=1, kpi=keypairlist; kpi; kpi = kpi->next, count++) { gcry_sexp_t s_pkey; char *algostr = NULL; enum gcry_pk_algos algoid = 0; const char *keyref = kpi->idstr; int any = 0; if (keyref && !agent_scd_readkey (ctrl, keyref, &s_pkey, NULL)) { algostr = pubkey_algo_string (s_pkey, &algoid); gcry_sexp_release (s_pkey); } /* We need to tweak the algo in case GCRY_PK_ECC is * returned because pubkey_algo_string is not aware * of the OpenPGP algo mapping. We need to * distinguish between ECDH and ECDSA but we can do * that only if we got usage flags. * Note: Keep this in sync with parse_key_parameter_part. */ if (algoid == GCRY_PK_ECC && algostr) { if (!strcmp (algostr, "ed25519")) kpi->algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "ed448")) kpi->algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "cv25519")) kpi->algo = PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "cv448")) kpi->algo = PUBKEY_ALGO_ECDH; else if ((kpi->usage & GCRY_PK_USAGE_ENCR)) kpi->algo = PUBKEY_ALGO_ECDH; else kpi->algo = PUBKEY_ALGO_ECDSA; } else kpi->algo = map_gcry_pk_to_openpgp (algoid); tty_printf (" (%d) %s %s %s", count, kpi->keygrip, keyref, algostr); if ((kpi->usage & GCRY_PK_USAGE_CERT)) { tty_printf ("%scert", any?",":" ("); any = 1; } if ((kpi->usage & GCRY_PK_USAGE_SIGN)) { tty_printf ("%ssign%s", any?",":" (", (signkeyref && keyref && !strcmp (signkeyref, keyref))? "*":""); any = 1; } if ((kpi->usage & GCRY_PK_USAGE_AUTH)) { tty_printf ("%sauth%s", any?",":" (", (authkeyref && keyref && !strcmp (authkeyref, keyref))? "*":""); any = 1; } if ((kpi->usage & GCRY_PK_USAGE_ENCR)) { tty_printf ("%sencr%s", any?",":" (", (encrkeyref && keyref && !strcmp (encrkeyref, keyref))? "*":""); any = 1; } tty_printf ("%s\n", any?")":""); xfree (algostr); } xfree (answer); answer = cpr_get ("keygen.cardkey", _("Your selection? ")); cpr_kill_prompt (); trim_spaces (answer); selection = atoi (answer); xfree (authkeyref); xfree (encrkeyref); xfree (signkeyref); } while (!(selection > 0 && selection < count)); for (count=1,kpi=keypairlist; kpi; kpi = kpi->next, count++) if (count == selection) break; if (!kpi) { /* Just in case COUNT is zero (no keys). */ free_keypair_info (keypairlist); goto ask_again; } xfree (keygrip); keygrip = xstrdup (kpi->keygrip); cardkey = 1; algo = kpi->algo; keytime = kpi->keytime; /* In expert mode allow to change the usage flags. */ if (opt.expert) *r_usage = ask_key_flags_with_mask (algo, addmode, kpi->usage, kpi->usage); else { *r_usage = kpi->usage; if (addmode) *r_usage &= ~GCRY_PK_USAGE_CERT; } free_keypair_info (keypairlist); break; } else tty_printf (_("Invalid selection.\n")); ask_again: ; } xfree(answer); if (r_keygrip) *r_keygrip = keygrip; if (r_cardkey) *r_cardkey = cardkey; if (r_keytime) *r_keytime = keytime; return algo; } static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max) { unsigned int def; unsigned int dummy1, dummy2; if (!min) min = &dummy1; if (!max) max = &dummy2; switch(algo) { case PUBKEY_ALGO_DSA: *min = opt.expert? 768 : 1024; *max=3072; def=2048; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: *min=256; *max=521; def=256; break; case PUBKEY_ALGO_EDDSA: *min=255; *max=441; def=255; break; case PUBKEY_ALGO_KYBER: *min = 768; *max = 1024; def = 768; break; default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; def = 3072; break; } return def; } /* Return a fixed up keysize depending on ALGO. */ static unsigned int fixup_keysize (unsigned int nbits, int algo, int silent) { unsigned int orig_nbits = nbits; if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; } else if (algo == PUBKEY_ALGO_EDDSA) { if (nbits < 256) nbits = 255; else nbits = 441; } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { if (nbits < 256) nbits = 256; else if (nbits < 384) nbits = 384; else nbits = 521; } else if (algo == PUBKEY_ALGO_KYBER) { /* (in reality the numbers are not bits) */ if (nbits < 768) nbits = 768; else if (nbits > 1024) nbits = 1024; } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; } if (!silent && orig_nbits != nbits) tty_printf (_("rounded to %u bits\n"), nbits); return nbits; } /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { unsigned int nbits; unsigned int min, def, max; int for_subkey = !!primary_keysize; int autocomp = 0; def = get_keysize_range (algo, &min, &max); if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) nbits = 3072; /* For performance reasons we don't support more than 3072 bit DSA. However we won't see this case anyway because DSA can't be used as an encryption subkey ;-). */ else nbits = primary_keysize; autocomp = 1; goto leave; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); for (;;) { char *prompt, *answer; if (for_subkey) prompt = xasprintf (_("What keysize do you want " "for the subkey? (%u) "), def); else prompt = xasprintf (_("What keysize do you want? (%u) "), def); answer = cpr_get ("keygen.size", prompt); cpr_kill_prompt (); nbits = *answer? atoi (answer): def; xfree(prompt); xfree(answer); if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), openpgp_pk_algo_name (algo), min, max); else break; } tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: nbits = fixup_keysize (nbits, algo, autocomp); return nbits; } /* Ask for the curve. ALGO is the selected algorithm which this function may adjust. Returns a const string of the name of the curve. */ const char * ask_curve (int *algo, int *subkey_algo, const char *current) { /* NB: We always use a complete algo list so that we have stable numbers in the menu regardless on how Gpg was configured. */ struct { const char *name; const char* eddsa_curve; /* Corresponding EdDSA curve. */ const char *pretty_name; unsigned int supported : 1; /* Supported by gpg. */ unsigned int de_vs : 1; /* Allowed in CO_DE_VS. */ unsigned int expert_only : 1; /* Only with --expert */ unsigned int available : 1; /* Available in Libycrypt (runtime checked) */ } curves[] = { #if GPG_USE_ECDSA || GPG_USE_ECDH # define MY_USE_ECDSADH 1 #else # define MY_USE_ECDSADH 0 #endif { "Curve25519", "Ed25519", "Curve 25519", !!GPG_USE_EDDSA, 0, 0, 0 }, { "X448", "Ed448", "Curve 448", !!GPG_USE_EDDSA, 0, 1, 0 }, { "NIST P-256", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, { "NIST P-384", NULL, NULL, MY_USE_ECDSADH, 0, 0, 0 }, { "NIST P-521", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, { "brainpoolP256r1", NULL, "Brainpool P-256", MY_USE_ECDSADH, 1, 0, 0 }, { "brainpoolP384r1", NULL, "Brainpool P-384", MY_USE_ECDSADH, 1, 1, 0 }, { "brainpoolP512r1", NULL, "Brainpool P-512", MY_USE_ECDSADH, 1, 1, 0 }, { "secp256k1", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, }; #undef MY_USE_ECDSADH int idx; char *answer; const char *result = NULL; gcry_sexp_t keyparms; tty_printf (_("Please select which elliptic curve you want:\n")); keyparms = NULL; for (idx=0; idx < DIM(curves); idx++) { int rc; curves[idx].available = 0; if (!curves[idx].supported) continue; if (opt.compliance==CO_DE_VS) { if (!curves[idx].de_vs) continue; /* Not allowed. */ } else if (!opt.expert && curves[idx].expert_only) continue; /* We need to switch from the ECDH name of the curve to the EDDSA name of the curve if we want a signing key. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].eddsa_curve? curves[idx].eddsa_curve /**/ : curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; if (subkey_algo && curves[idx].eddsa_curve) { /* Both Curve 25519 (or 448) keys are to be created. Check that Libgcrypt also supports the real Curve25519 (or 448). */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; } curves[idx].available = 1; tty_printf (" (%d) %s%s\n", idx + 1, curves[idx].pretty_name? curves[idx].pretty_name:curves[idx].name, idx == 0? _(" *default*"):""); } gcry_sexp_release (keyparms); for (;;) { answer = cpr_get ("keygen.curve", _("Your selection? ")); cpr_kill_prompt (); idx = *answer? atoi (answer) : 1; if (!*answer && current) { xfree(answer); return NULL; } else if (*answer && !idx) { /* See whether the user entered the name of the curve. */ for (idx=0; idx < DIM(curves); idx++) { if (!opt.expert && curves[idx].expert_only) continue; if (!stricmp (curves[idx].name, answer) || (curves[idx].pretty_name && !stricmp (curves[idx].pretty_name, answer))) break; } if (idx == DIM(curves)) idx = -1; } else idx--; xfree(answer); answer = NULL; if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) tty_printf (_("Invalid selection.\n")); else { /* If the user selected a signing algorithm and Curve25519 we need to set the algo to EdDSA and update the curve name. If switching away from EdDSA, we need to set the algo back to ECDSA. */ if (*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) { if (curves[idx].eddsa_curve) { if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) *subkey_algo = PUBKEY_ALGO_EDDSA; *algo = PUBKEY_ALGO_EDDSA; result = curves[idx].eddsa_curve; } else { if (subkey_algo && *subkey_algo == PUBKEY_ALGO_EDDSA) *subkey_algo = PUBKEY_ALGO_ECDSA; *algo = PUBKEY_ALGO_ECDSA; result = curves[idx].name; } } else result = curves[idx].name; break; } } if (!result) result = curves[0].name; return result; } /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. * This isn't perfect since scan_isodatestr returns unix time, and * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. * Because of this, we only permit setting expirations up to 2106, but * OpenPGP could theoretically allow up to 2242. I think we'll all * just cope for the next few years until we get a 64-bit time_t or * similar. */ static u32 parse_expire_string_with_ct (const char *string, u32 creation_time) { int mult; u32 seconds; u32 abs_date = 0; time_t tt; uint64_t tmp64; u32 curtime; if (creation_time == (u32)-1) curtime = make_timestamp (); else curtime = creation_time; if (!string || !*string || !strcmp (string, "none") || !strcmp (string, "never") || !strcmp (string, "-")) seconds = 0; else if (!strncmp (string, "seconds=", 8)) seconds = scan_secondsstr (string+8); else if ((abs_date = scan_isodatestr(string)) && (abs_date+86400/2) > curtime) seconds = (abs_date+86400/2) - curtime; else if ((tt = isotime2epoch_u64 (string)) != (uint64_t)(-1)) { tmp64 = tt - curtime; if (tmp64 >= (u32)(-1)) seconds = (u32)(-1) - 1; /* cap value. */ else seconds = (u32)tmp64; } else if ((mult = check_valid_days (string))) { tmp64 = scan_secondsstr (string) * 86400L * mult; if (tmp64 >= (u32)(-1)) seconds = (u32)(-1) - 1; /* cap value. */ else seconds = (u32)tmp64; } else seconds = (u32)(-1); return seconds; } u32 parse_expire_string ( const char *string ) { return parse_expire_string_with_ct (string, (u32)-1); } /* Parse a Creation-Date string which is either "1986-04-26" or "19860426T042640". Returns 0 on error. */ static u32 parse_creation_string (const char *string) { u32 seconds; if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = scan_secondsstr (string+8); else if ( !(seconds = scan_isodatestr (string))) { uint64_t tmp = isotime2epoch_u64 (string); if (tmp == (uint64_t)(-1)) seconds = 0; else if (tmp > (u32)(-1)) seconds = 0; else seconds = tmp; } return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 ask_expire_interval(int object,const char *def_expire) { u32 interval; char *answer; switch(object) { case 0: if(def_expire) BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); break; case 1: if(!def_expire) BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " = signature expires in n days\n" " w = signature expires in n weeks\n" " m = signature expires in n months\n" " y = signature expires in n years\n")); break; default: BUG(); } /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { u32 curtime; xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else { char *prompt; prompt = xasprintf (_("Signature is valid for? (%s) "), def_expire); answer = cpr_get("siggen.valid",prompt); xfree(prompt); if(*answer=='\0') { xfree (answer); answer = xstrdup (def_expire); } } cpr_kill_prompt(); trim_spaces(answer); curtime = make_timestamp (); interval = parse_expire_string( answer ); if( interval == (u32)-1 ) { tty_printf(_("invalid value\n")); continue; } if( !interval ) { tty_printf((object==0) ? _("Key does not expire at all\n") : _("Signature does not expire at all\n")); } else { tty_printf(object==0 ? _("Key expires at %s\n") : _("Signature expires at %s\n"), asctimestamp((ulong)(curtime + interval) ) ); #if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T) if ( (time_t)((ulong)(curtime+interval)) < 0 ) tty_printf (_("Your system can't display dates beyond 2038.\n" "However, it will be correctly handled up to" " 2106.\n")); else #endif /*SIZEOF_TIME_T*/ if ( (time_t)((unsigned long)(curtime+interval)) < curtime ) { tty_printf (_("invalid value\n")); continue; } } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct? (y/N) ")) ) break; } xfree(answer); return interval; } u32 ask_expiredate (void) { u32 x = ask_expire_interval(0,NULL); return x? make_timestamp() + x : 0; } static PKT_user_id * uid_from_string (const char *string) { size_t n; PKT_user_id *uid; n = strlen (string); uid = xmalloc_clear (sizeof *uid + n); uid->len = n; strcpy (uid->name, string); uid->ref = 1; return uid; } /* Return true if the user id UID already exists in the keyblock. */ static int uid_already_in_keyblock (kbnode_t keyblock, const char *uid) { PKT_user_id *uidpkt = uid_from_string (uid); kbnode_t node; int result = 0; for (node=keyblock; node && !result; node=node->next) if (!is_deleted_kbnode (node) && node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id)) result = 1; free_user_id (uidpkt); return result; } /* Ask for a user ID. With a MODE of 1 an extra help prompt is printed for use during a new key creation. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. IF FULL is not set some prompts are not shown. */ static char * ask_user_id (int mode, int full, KBNODE keyblock) { char *answer; char *aname, *acomment, *amail, *uid; if ( !mode ) { /* TRANSLATORS: This is the new string telling the user what gpg is now going to do (i.e. ask for the parts of the user ID). Note that if you do not translate this string, a different string will be used, which might still have a correct translation. */ const char *s1 = N_("\n" "GnuPG needs to construct a user ID to identify your key.\n" "\n"); const char *s2 = _(s1); if (!strcmp (s1, s2)) { /* There is no translation for the string thus we to use the old info text. gettext has no way to tell whether a translation is actually available, thus we need to to compare again. */ /* TRANSLATORS: This string is in general not anymore used but you should keep your existing translation. In case the new string is not translated this old string will be used. */ const char *s3 = N_("\n" "You need a user ID to identify your key; " "the software constructs the user ID\n" "from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n"); const char *s4 = _(s3); if (strcmp (s3, s4)) s2 = s3; /* A translation exists - use it. */ } tty_printf ("%s", s2) ; } uid = aname = acomment = amail = NULL; for(;;) { char *p; int fail=0; if( !aname ) { for(;;) { xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( opt.allow_freeform_uid ) break; if( strpbrk( aname, "<>" ) ) { tty_printf(_("Invalid character in name\n")); tty_printf(_("The characters '%s' and '%s' may not " "appear in name\n"), "<", ">"); } else break; } } if( !amail ) { for(;;) { xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ else if ( !is_valid_mailbox (amail) ) tty_printf(_("Not a valid email address\n")); else break; } } if (!acomment) { if (full) { for(;;) { xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } else { xfree (acomment); acomment = xstrdup (""); } } xfree(uid); uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); if (!*aname && *amail && !*acomment && !random_is_faked ()) { /* Empty name and comment but with mail address. Use simplified form with only the non-angle-bracketed mail address. */ p = stpcpy (p, amail); } else { p = stpcpy (p, aname ); if (*acomment) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if (*amail) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); } /* Append a warning if the RNG is switched into fake mode. */ if ( random_is_faked () ) strcpy(p, " (insecure!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the '%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); if( !*amail && !opt.allow_freeform_uid && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } if (!fail && keyblock) { if (uid_already_in_keyblock (keyblock, uid)) { tty_printf (_("Such a user ID already exists on this key!\n")); fail = 1; } } for(;;) { /* TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. n = Change name c = Change comment e = Change email o = Okay (ready, continue) q = Quit */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = xstrdup (ansstr + (fail?8:6)); answer[1] = 0; } else if (full) { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } else { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (E)mail, or (Q)uit? ") : _("Change (N)ame, (E)mail, or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { if( fail ) { tty_printf(_("Please correct the error first\n")); } else { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; xfree(uid); uid = NULL; break; } xfree(answer); } xfree(answer); if (!amail && !acomment) break; xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); xfree( uid ); uid = p; } return uid; } /* Basic key generation. Here we divert to the actual generation * routines based on the requested algorithm. KEYGEN_FLAGS might be * updated by this function. */ static int do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expiredate, int is_subkey, int *keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), common_gen_cb_parm_t common_gen_cb_parm) { gpg_error_t err; /* Fixme: The entropy collecting message should be moved to a libgcrypt progress handler. */ if (!opt.batch) tty_printf (_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if (algo == PUBKEY_ALGO_ELGAMAL_E) err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_KYBER) err = gen_kyber (algo, nbits, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); else BUG(); return err; } /* Generate a new user id packet or return NULL if canceled. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. If UIDSTR is not NULL the user is not asked but UIDSTR is used to create the user id packet; if the user id already exists NULL is returned. UIDSTR is expected to be utf-8 encoded and should have already been checked for a valid length etc. */ PKT_user_id * generate_user_id (KBNODE keyblock, const char *uidstr) { PKT_user_id *uid; char *p; if (uidstr) { if (uid_already_in_keyblock (keyblock, uidstr)) return NULL; /* Already exists. */ uid = uid_from_string (uidstr); } else { p = ask_user_id (1, 1, keyblock); if (!p) return NULL; /* Canceled. */ uid = uid_from_string (p); xfree (p); } return uid; } /* Helper for parse_key_parameter_part_parameter_string for one part of the * specification string; i.e. ALGO/FLAGS. If STRING is NULL or empty * success is returned. On error an error code is returned. Note * that STRING may be modified by this function. NULL may be passed * for any parameter. FOR_SUBKEY shall be true if this is used as a * subkey. If CLEAR_CERT is set a default CERT usage will be cleared; * this is useful if for example the default algorithm is used for a * subkey. If R_KEYVERSION is not NULL it will receive the version of * the key; this is currently 4 but can be changed with the flag "v5" * to create a v5 key. If R_KEYTIME is not NULL and the key has been * taken from active OpenPGP card, its creation time is stored * there. */ static gpg_error_t parse_key_parameter_part (ctrl_t ctrl, char *string, int for_subkey, int clear_cert, int *r_algo, unsigned int *r_size, unsigned int *r_keyuse, char const **r_curve, int *r_keyversion, char **r_keygrip, u32 *r_keytime) { gpg_error_t err; char *flags; int algo; char *endp; const char *curve = NULL; int ecdh_or_ecdsa = 0; unsigned int size; int keyuse; int keyversion = 0; /* Not specified. */ int i; const char *s; int from_card = 0; char *keygrip = NULL; u32 keytime = 0; int is_448 = 0; int is_pqc = 0; if (!string || !*string) return 0; /* Success. */ flags = strchr (string, '/'); if (flags) *flags++ = 0; algo = 0; if (!ascii_strcasecmp (string, "card")) from_card = 1; else if (strlen (string) >= 3 && (digitp (string+3) || !string[3])) { if (!ascii_memcasecmp (string, "rsa", 3)) algo = PUBKEY_ALGO_RSA; else if (!ascii_memcasecmp (string, "dsa", 3)) algo = PUBKEY_ALGO_DSA; else if (!ascii_memcasecmp (string, "elg", 3)) algo = PUBKEY_ALGO_ELGAMAL_E; } if (from_card) ; /* We need the flags before we can figure out the key to use. */ else if (algo) { /* This is one of the algos parsed above (rsa, dsa, or elg). */ if (!string[3]) size = get_keysize_range (algo, NULL, NULL); else { size = strtoul (string+3, &endp, 10); if (size < 512 || size > 16384 || *endp) return gpg_error (GPG_ERR_INV_VALUE); } } else if (!ascii_strcasecmp (string, "kyber")) { /* Get the curve and check that it can technically be used * (i.e. everything except the EdXXXX curves. */ curve = openpgp_is_curve_supported ("brainpoolP384r1", &algo, NULL); if (!curve || algo == PUBKEY_ALGO_EDDSA) return gpg_error (GPG_ERR_UNKNOWN_CURVE); algo = PUBKEY_ALGO_KYBER; size = 768; is_pqc = 1; } else if (!ascii_strcasecmp (string, "dil3")) { algo = PUBKEY_ALGO_DIL3_25519; is_pqc = 1; } else if (!ascii_strcasecmp (string, "dil5")) { algo = PUBKEY_ALGO_DIL5_448; is_pqc = 1; } else if (!ascii_strcasecmp (string, "sphinx") || !ascii_strcasecmp (string, "sphinx_sha2")) { algo = PUBKEY_ALGO_SPHINX_SHA2; is_pqc = 1; } else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) { if (!algo) { algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */ ecdh_or_ecdsa = 1; /* We may need to switch the algo. */ } if (curve && (!strcmp (curve, "X448") || !strcmp (curve, "Ed448"))) is_448 = 1; } else return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Parse the flags. */ keyuse = 0; if (flags) { char **tokens = NULL; tokens = strtokenize (flags, ","); if (!tokens) return gpg_error_from_syserror (); for (i=0; (s = tokens[i]); i++) { if (!*s) ; else if (!ascii_strcasecmp (s, "sign")) keyuse |= PUBKEY_USAGE_SIG; else if (!ascii_strcasecmp (s, "encrypt") || !ascii_strcasecmp (s, "encr")) keyuse |= PUBKEY_USAGE_ENC; else if (!ascii_strcasecmp (s, "auth")) keyuse |= PUBKEY_USAGE_AUTH; else if (!ascii_strcasecmp (s, "cert")) keyuse |= PUBKEY_USAGE_CERT; else if (!ascii_strcasecmp (s, "ecdsa") && !from_card) { if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) algo = PUBKEY_ALGO_ECDSA; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } ecdh_or_ecdsa = 0; } else if (!ascii_strcasecmp (s, "ecdh") && !from_card) { if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) algo = PUBKEY_ALGO_ECDH; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } ecdh_or_ecdsa = 0; } else if (!ascii_strcasecmp (s, "eddsa") && !from_card) { /* Not required but we allow it for consistency. */ if (algo == PUBKEY_ALGO_EDDSA) ; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } } else if (!ascii_strcasecmp (s, "v5")) keyversion = 5; else if (!ascii_strcasecmp (s, "v4")) keyversion = 4; else { xfree (tokens); return gpg_error (GPG_ERR_UNKNOWN_FLAG); } } xfree (tokens); } /* If not yet decided switch between ecdh and ecdsa unless we want * to read the algo from the current card. */ if (from_card) { keypair_info_t keypairlist, kpi; char *reqkeyref; if (!keyuse) keyuse = (for_subkey? PUBKEY_USAGE_ENC /* */ : (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)); /* Access the card to make sure we have one and to show the S/N. */ { char *serialno; err = agent_scd_serialno (&serialno, NULL); if (err) { log_error (_("error reading the card: %s\n"), gpg_strerror (err)); return err; } if (!opt.quiet) log_info (_("Serial number of the card: %s\n"), serialno); xfree (serialno); } err = agent_scd_keypairinfo (ctrl, NULL, &keypairlist); if (err) { log_error (_("error reading the card: %s\n"), gpg_strerror (err)); return err; } agent_scd_getattr_one ((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)) ? "$SIGNKEYID":"$ENCRKEYID", &reqkeyref); algo = 0; /* Should already be the case. */ for (kpi=keypairlist; kpi && !algo; kpi = kpi->next) { gcry_sexp_t s_pkey; char *algostr = NULL; enum gcry_pk_algos algoid = 0; const char *keyref = kpi->idstr; if (!reqkeyref) continue; /* Card does not provide the info (skip all). */ if (!keyref) continue; /* Ooops. */ if (strcmp (reqkeyref, keyref)) continue; /* This is not the requested keyref. */ if ((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)) && (kpi->usage & (GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_CERT))) ; /* Okay */ else if ((keyuse & PUBKEY_USAGE_ENC) && (kpi->usage & GCRY_PK_USAGE_ENCR)) ; /* Okay */ else continue; /* Not usable for us. */ if (agent_scd_readkey (ctrl, keyref, &s_pkey, NULL)) continue; /* Could not read the key. */ algostr = pubkey_algo_string (s_pkey, &algoid); gcry_sexp_release (s_pkey); /* Map to OpenPGP algo number. * We need to tweak the algo in case GCRY_PK_ECC is * returned because pubkey_algo_string is not aware * of the OpenPGP algo mapping. We need to * distinguish between ECDH and ECDSA but we can do * that only if we got usage flags. * Note: Keep this in sync with ask_algo. */ if (algoid == GCRY_PK_ECC && algostr) { if (!strcmp (algostr, "ed25519")) algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "ed448")) { algo = PUBKEY_ALGO_EDDSA; is_448 = 1; } else if (!strcmp (algostr, "cv25519")) algo = PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "cv448")) { algo = PUBKEY_ALGO_ECDH; is_448 = 1; } else if ((kpi->usage & GCRY_PK_USAGE_ENCR)) algo = PUBKEY_ALGO_ECDH; else algo = PUBKEY_ALGO_ECDSA; } else algo = map_gcry_pk_to_openpgp (algoid); xfree (algostr); xfree (keygrip); keygrip = xtrystrdup (kpi->keygrip); if (!keygrip) { err = gpg_error_from_syserror (); xfree (reqkeyref); free_keypair_info (keypairlist); return err; } keytime = kpi->keytime; } xfree (reqkeyref); free_keypair_info (keypairlist); if (!algo || !keygrip) { err = gpg_error (GPG_ERR_PUBKEY_ALGO); log_error ("no usable key on the card: %s\n", gpg_strerror (err)); xfree (keygrip); return err; } } else if (ecdh_or_ecdsa && keyuse) algo = (keyuse & PUBKEY_USAGE_ENC)? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; else if (ecdh_or_ecdsa) algo = for_subkey? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; /* Set or fix key usage. */ if (!keyuse) { if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_DSA) keyuse = PUBKEY_USAGE_SIG; else if (algo == PUBKEY_ALGO_RSA) keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG; else keyuse = PUBKEY_USAGE_ENC; } else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_DSA) { keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption. */ } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ELGAMAL_E) { keyuse = PUBKEY_USAGE_ENC; /* Allow only encryption. */ } /* Make sure a primary key can certify. */ if (!for_subkey) keyuse |= PUBKEY_USAGE_CERT; /* But if requested remove th cert usage. */ if (clear_cert) keyuse &= ~PUBKEY_USAGE_CERT; /* Check that usage is actually possible. */ if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) && !pubkey_get_nsig (algo)) || ((keyuse & PUBKEY_USAGE_ENC) && !pubkey_get_nenc (algo)) || (for_subkey && (keyuse & PUBKEY_USAGE_CERT))) { xfree (keygrip); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Ed448, X448 and the PQC algos must only be used as v5 keys. */ if (is_448 || is_pqc) { if (keyversion == 4) log_info (_("WARNING: v4 is specified, but overridden by v5.\n")); keyversion = 5; } else if (keyversion == 0) keyversion = 4; /* Return values. */ if (r_algo) *r_algo = algo; if (r_size) { unsigned int min, def, max; /* Make sure the keysize is in the allowed range. */ def = get_keysize_range (algo, &min, &max); if (!size) size = def; else if (size < min) size = min; else if (size > max) size = max; *r_size = fixup_keysize (size, algo, 1); } if (r_keyuse) *r_keyuse = keyuse; if (r_curve) *r_curve = curve; if (r_keyversion) *r_keyversion = keyversion; if (r_keygrip) *r_keygrip = keygrip; else xfree (keygrip); if (r_keytime) *r_keytime = keytime; return 0; } /* Parse and return the standard key generation parameter. * The string is expected to be in this format: * * ALGO[/FLAGS][+SUBALGO[/FLAGS]] * * Here ALGO is a string in the same format as printed by the * keylisting. For example: * * rsa3072 := RSA with 3072 bit. * dsa2048 := DSA with 2048 bit. * elg2048 := Elgamal with 2048 bit. * ed25519 := EDDSA using curve Ed25519. * ed448 := EDDSA using curve Ed448. * cv25519 := ECDH using curve Curve25519. * cv448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 * kyber := Kyber with the default parameters * ky768_bp384 := Kyber-768 with BrainpoolP256r1 as second algo * * All strings with an unknown prefix are considered an elliptic * curve. Curves which have no implicit algorithm require that FLAGS * is given to select whether ECDSA or ECDH is used; this can either * be done using an algorithm keyword or usage keywords. * * FLAGS is a comma delimited string of keywords: * * cert := Allow usage Certify * sign := Allow usage Sign * encr := Allow usage Encrypt * auth := Allow usage Authentication * encrypt := Alias for "encr" * ecdsa := Use algorithm ECDSA. * eddsa := Use algorithm EdDSA. * ecdh := Use algorithm ECDH. * v5 := Create version 5 key * * There are several defaults and fallbacks depending on the * algorithm. PART can be used to select which part of STRING is * used: * -1 := Both parts * 0 := Only the part of the primary key * 1 := If there is one part parse that one, if there are * two parts parse the part which best matches the * SUGGESTED_USE or in case that can't be evaluated the second part. * Always return using the args for the primary key (R_ALGO,....). * */ gpg_error_t parse_key_parameter_string (ctrl_t ctrl, const char *string, int part, unsigned int suggested_use, int *r_algo, unsigned int *r_size, unsigned int *r_keyuse, char const **r_curve, int *r_version, char **r_keygrip, u32 *r_keytime, int *r_subalgo, unsigned int *r_subsize, unsigned int *r_subkeyuse, char const **r_subcurve, int *r_subversion, char **r_subkeygrip, u32 *r_subkeytime) { gpg_error_t err = 0; char *primary, *secondary; if (r_algo) *r_algo = 0; if (r_size) *r_size = 0; if (r_keyuse) *r_keyuse = 0; if (r_curve) *r_curve = NULL; if (r_version) *r_version = 4; if (r_keygrip) *r_keygrip = NULL; if (r_keytime) *r_keytime = 0; if (r_subalgo) *r_subalgo = 0; if (r_subsize) *r_subsize = 0; if (r_subkeyuse) *r_subkeyuse = 0; if (r_subcurve) *r_subcurve = NULL; if (r_subversion) *r_subversion = 4; if (r_subkeygrip) *r_subkeygrip = NULL; if (r_subkeytime) *r_subkeytime = 0; if (!string || !*string || !ascii_strcasecmp (string, "default") || !strcmp (string, "-")) string = get_default_pubkey_algo (); else if (!ascii_strcasecmp (string, "future-default") || !ascii_strcasecmp (string, "futuredefault")) string = FUTURE_STD_KEY_PARAM; else if (!ascii_strcasecmp (string, "card")) string = "card/cert,sign+card/encr"; primary = xstrdup (string); secondary = strchr (primary, '+'); if (secondary) *secondary++ = 0; if (part == -1 || part == 0) { err = parse_key_parameter_part (ctrl, primary, 0, 0, r_algo, r_size, r_keyuse, r_curve, r_version, r_keygrip, r_keytime); if (!err && part == -1) err = parse_key_parameter_part (ctrl, secondary, 1, 0, r_subalgo, r_subsize, r_subkeyuse, r_subcurve, r_subversion, r_subkeygrip, r_subkeytime); } else if (part == 1) { /* If we have SECONDARY, use that part. If there is only one * part consider this to be the subkey algo. In case a * SUGGESTED_USE has been given and the usage of the secondary * part does not match SUGGESTED_USE try again using the primary * part. Note that when falling back to the primary key we need * to force clearing the cert usage. */ if (secondary) { err = parse_key_parameter_part (ctrl, secondary, 1, 0, r_algo, r_size, r_keyuse, r_curve, r_version, r_keygrip, r_keytime); if (!err && suggested_use && r_keyuse && !(suggested_use & *r_keyuse)) err = parse_key_parameter_part (ctrl, primary, 1, 1 /*(clear cert)*/, r_algo, r_size, r_keyuse, r_curve, r_version, r_keygrip, r_keytime); } else err = parse_key_parameter_part (ctrl, primary, 1, 0, r_algo, r_size, r_keyuse, r_curve, r_version, r_keygrip, r_keytime); } xfree (primary); return err; } /* Append R to the linked list PARA. */ static void append_to_parameter (struct para_data_s *para, struct para_data_s *r) { log_assert (para); while (para->next) para = para->next; para->next = r; } /* Release the parameter list R. */ static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; if (r->key == pPASSPHRASE && *r->u.value) wipememory (r->u.value, strlen (r->u.value)); xfree (r); } } /* Return the N-th parameter of name KEY from PARA. An IDX of 0 * returns the first and so on. */ static struct para_data_s * get_parameter_idx (struct para_data_s *para, enum para_name key, unsigned int idx) { struct para_data_s *r; for(r = para; r; r = r->next) if (r->key == key) { if (!idx) return r; idx--; } return NULL; } /* Return the first parameter of name KEY from PARA. */ static struct para_data_s * get_parameter (struct para_data_s *para, enum para_name key) { return get_parameter_idx (para, key, 0); } static const char * get_parameter_value( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return (r && *r->u.value)? r->u.value : NULL; } /* This is similar to get_parameter_value but also returns the empty string. This is required so that quick_generate_keypair can use an empty Passphrase to specify no-protection. */ static const char * get_parameter_passphrase (struct para_data_s *para) { struct para_data_s *r = get_parameter (para, pPASSPHRASE); return r ? r->u.value : NULL; } static int get_parameter_algo (ctrl_t ctrl, struct para_data_s *para, enum para_name key, int *r_default) { int i; struct para_data_s *r = get_parameter( para, key ); if (r_default) *r_default = 0; if (!r) return -1; /* Note that we need to handle the ECC algorithms specified as strings directly because Libgcrypt folds them all to ECC. */ if (!ascii_strcasecmp (r->u.value, "default")) { /* Note: If you change this default algo, remember to change it * also in gpg.c:gpgconf_list. */ /* FIXME: We only allow the algo here and have a separate thing * for the curve etc. That is a ugly but demanded for backward * compatibility with the batch key generation. It would be * better to make full use of parse_key_parameter_string. */ parse_key_parameter_string (ctrl, NULL, 0, 0, &i, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (r_default) *r_default = 1; } else if (digitp (r->u.value)) i = atoi( r->u.value ); else if (!strcmp (r->u.value, "ELG-E") || !strcmp (r->u.value, "ELG")) i = PUBKEY_ALGO_ELGAMAL_E; else if (!ascii_strcasecmp (r->u.value, "EdDSA")) i = PUBKEY_ALGO_EDDSA; else if (!ascii_strcasecmp (r->u.value, "ECDSA")) i = PUBKEY_ALGO_ECDSA; else if (!ascii_strcasecmp (r->u.value, "ECDH")) i = PUBKEY_ALGO_ECDH; else i = map_gcry_pk_to_openpgp (gcry_pk_map_name (r->u.value)); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; } /* Parse a usage string. The usage keywords "auth", "sign", "encr" * may be delimited by space, tab, or comma. On error -1 is returned * instead of the usage flags. */ static int parse_usagestr (const char *usagestr) { gpg_error_t err; char **tokens = NULL; const char *s; int i; unsigned int use = 0; tokens = strtokenize (usagestr, " \t,"); if (!tokens) { err = gpg_error_from_syserror (); log_error ("strtokenize failed: %s\n", gpg_strerror (err)); return -1; } for (i=0; (s = tokens[i]); i++) { if (!*s) ; else if (!ascii_strcasecmp (s, "sign")) use |= PUBKEY_USAGE_SIG; else if (!ascii_strcasecmp (s, "encrypt") || !ascii_strcasecmp (s, "encr")) use |= PUBKEY_USAGE_ENC; else if (!ascii_strcasecmp (s, "auth")) use |= PUBKEY_USAGE_AUTH; else if (!ascii_strcasecmp (s, "cert")) use |= PUBKEY_USAGE_CERT; else if (!ascii_strcasecmp (s, "renc")) use |= PUBKEY_USAGE_RENC; else if (!ascii_strcasecmp (s, "time")) use |= PUBKEY_USAGE_TIME; else if (!ascii_strcasecmp (s, "group")) use |= PUBKEY_USAGE_GROUP; else { xfree (tokens); return -1; /* error */ } } xfree (tokens); return use; } /* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. */ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); int i; if (!r) return 0; /* none (this is an optional parameter)*/ i = parse_usagestr (r->u.value); if (i == -1) { log_error ("%s:%d: invalid usage list\n", fname, r->lnr ); return -1; /* error */ } r->u.usage = i; return 1; } /* Parse the revocation key specified by NAME, check that the public * key exists (so that we can get the required public key algorithm), * and return a parameter wit the revocation key information. On * error print a diagnostic and return NULL. */ static struct para_data_s * prepare_desig_revoker (ctrl_t ctrl, const char *name) { gpg_error_t err; struct para_data_s *para = NULL; KEYDB_SEARCH_DESC desc; int sensitive = 0; struct revocation_key revkey; PKT_public_key *revoker_pk = NULL; size_t fprlen; if (!ascii_strncasecmp (name, "sensitive:", 10) && !spacep (name+10)) { name += 10; sensitive = 1; } if (classify_user_id (name, &desc, 1) || desc.mode != KEYDB_SEARCH_MODE_FPR) { log_info (_("\"%s\" is not a fingerprint\n"), name); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } revoker_pk = xcalloc (1, sizeof *revoker_pk); revoker_pk->req_usage = PUBKEY_USAGE_CERT; err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, NULL, revoker_pk, name, NULL, NULL, 1); if (err) goto leave; fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); if (fprlen != 20 && fprlen != 32) { log_info (_("cannot appoint a PGP 2.x style key as a " "designated revoker\n")); err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); goto leave; } revkey.fprlen = fprlen; revkey.class = 0x80; if (sensitive) revkey.class |= 0x40; revkey.algid = revoker_pk->pubkey_algo; para = xcalloc (1, sizeof *para); para->key = pREVOKER; memcpy (¶->u.revkey, &revkey, sizeof revkey); leave: if (err) log_error ("invalid revocation key '%s': %s\n", name, gpg_strerror (err)); free_public_key (revoker_pk); return para; } /* Parse a pREVOKER parameter into its dedicated parts. */ static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); struct revocation_key revkey; char *pn; int i; if( !r ) return 0; /* none (this is an optional parameter) */ pn = r->u.value; revkey.class=0x80; revkey.algid=atoi(pn); if(!revkey.algid) goto fail; /* Skip to the fpr */ while(*pn && *pn!=':') pn++; if(*pn!=':') goto fail; pn++; for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); return 0; fail: log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); return -1; /* error */ } static u32 get_parameter_u32( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); if( !r ) return 0; if (r->key == pKEYCREATIONDATE || r->key == pSUBKEYCREATIONDATE || r->key == pAUTHKEYCREATIONDATE) return r->u.creation; if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; return (unsigned int)strtoul( r->u.value, NULL, 10 ); } static unsigned int get_parameter_uint( struct para_data_s *para, enum para_name key ) { return get_parameter_u32( para, key ); } static struct revocation_key * get_parameter_revkey (struct para_data_s *para, enum para_name key, unsigned int idx) { struct para_data_s *r = get_parameter_idx (para, key, idx); return r? &r->u.revkey : NULL; } static int get_parameter_bool (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key); return (r && r->u.abool); } static int proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; size_t n; char *p; strlist_t sl; int is_default = 0; int have_user_id = 0; int err, algo; u32 creation_time = (u32)-1; /* Check that we have all required parameters. */ r = get_parameter( para, pKEYTYPE ); if(r) { algo = get_parameter_algo (ctrl, para, pKEYTYPE, &is_default); if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } } else { log_error ("%s: no Key-Type specified\n",fname); return -1; } err = parse_parameter_usage (fname, para, pKEYUSAGE); if (!err) { /* Default to algo capabilities if key-usage is not provided and no default algorithm has been requested. */ r = xmalloc_clear(sizeof(*r)); r->key = pKEYUSAGE; r->u.usage = (is_default ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG) : openpgp_pk_algo_usage(algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", fname, r->lnr, algo); return -1; } } is_default = 0; r = get_parameter( para, pSUBKEYTYPE ); if(r) { algo = get_parameter_algo (ctrl, para, pSUBKEYTYPE, &is_default); if (openpgp_pk_test_algo (algo)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); if (!err) { /* Default to algo capabilities if subkey-usage is not provided. Take care not to include RENC. */ r = xmalloc_clear (sizeof(*r)); r->key = pSUBKEYUSAGE; r->u.usage = (is_default ? PUBKEY_USAGE_ENC : (openpgp_pk_algo_usage (algo) & ~PUBKEY_USAGE_RENC) ); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pSUBKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Subkey-Usage not allowed" " for algo %d\n", fname, r->lnr, algo); return -1; } } } if( get_parameter_value( para, pUSERID ) ) have_user_id=1; else { /* create the formatted user ID */ s1 = get_parameter_value( para, pNAMEREAL ); s2 = get_parameter_value( para, pNAMECOMMENT ); s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); r = xmalloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) p = stpcpy(p, s1 ); if( s2 ) p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); if( s3 ) { /* If we have only the email part, do not add the space * and the angle brackets. */ if (*r->u.value) p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); else p = stpcpy (p, s3); } append_to_parameter (para, r); have_user_id=1; } } if(!have_user_id) { log_error("%s: no User-ID specified\n",fname); return -1; } /* Set preferences, if any. */ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); /* Set keyserver, if any. */ s1=get_parameter_value( para, pKEYSERVER ); if(s1) { struct keyserver_spec *spec; spec = parse_keyserver_uri (s1, 1); if(spec) { free_keyserver_spec(spec); opt.def_keyserver_url=s1; } else { r = get_parameter (para, pKEYSERVER); log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); return -1; } } /* Set revoker from parameter file, if any. Must be done first so * that we don't find a parameter set via prepare_desig_revoker. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; /* Check and appened revokers from the config file. */ for (sl = opt.desig_revokers; sl; sl = sl->next) { r = prepare_desig_revoker (ctrl, sl->d); if (!r) return -1; append_to_parameter (para, r); } /* Make KEYCREATIONDATE from Creation-Date. We ignore this if the * key has been taken from a card and a keycreationtime has already * been set. This is so that we don't generate a key with a * fingerprint different from the one stored on the OpenPGP card. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value && !(get_parameter_bool (para, pCARDKEY) && get_parameter_u32 (para, pKEYCREATIONDATE))) { creation_time = parse_creation_string (r->u.value); if (!creation_time) { log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); return -1; } r->u.creation = creation_time; r->key = pKEYCREATIONDATE; /* Change that entry. */ } /* Make KEYEXPIRE from Expire-Date. */ r = get_parameter( para, pEXPIREDATE ); if( r && *r->u.value ) { u32 seconds; seconds = parse_expire_string_with_ct (r->u.value, creation_time); if( seconds == (u32)-1 ) { log_error("%s:%d: invalid expire date\n", fname, r->lnr ); return -1; } r->u.expire = seconds; r->key = pKEYEXPIRE; /* change that entry */ /* Make SUBKEYEXPIRE from Subkey-Expire-Date, if any. */ r = get_parameter( para, pSUBKEYEXPIREDATE ); if( r && *r->u.value ) { seconds = parse_expire_string_with_ct (r->u.value, creation_time); if( seconds == (u32)-1 ) { log_error("%s:%d: invalid subkey expire date\n", fname, r->lnr ); return -1; } r->key = pSUBKEYEXPIRE; /* change that entry */ r->u.expire = seconds; } else { /* Or else, set Expire-Date for the subkey */ r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = seconds; append_to_parameter (para, r); } } do_generate_keypair (ctrl, para, outctrl, card ); return 0; } /**************** * Kludge to allow non interactive key generation controlled * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void read_parameter_file (ctrl_t ctrl, const char *fname ) { static struct { const char *name; enum para_name key; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, { "Subkey-Curve", pSUBKEYCURVE }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, { "Name-Comment", pNAMECOMMENT }, { "Expire-Date", pEXPIREDATE }, { "Subkey-Expire-Date", pSUBKEYEXPIREDATE }, { "Creation-Date", pCREATIONDATE }, { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, { "Handle", pHANDLE }, { "Keyserver", pKEYSERVER }, { "Keygrip", pKEYGRIP }, { "Key-Grip", pKEYGRIP }, { "Subkey-grip", pSUBKEYGRIP }, { "Key-Version", pVERSION }, { "Subkey-Version", pSUBVERSION }, { NULL, 0 } }; IOBUF fp; byte *line; unsigned int maxlen, nline; char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; int i; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); outctrl.pub.afx = new_armor_context (); if( !fname || !*fname) fname = "-"; fp = iobuf_open (fname); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; gpg_err_set_errno (EPERM); } if (!fp) { log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); return; } iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL); lnr = 0; err = NULL; para = NULL; maxlen = 1024; line = NULL; nline = 0; while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; if( !maxlen ) { err = "line too long"; break; } for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-protection" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_NO_PROTECTION; else if( !ascii_strcasecmp( keyword, "%transient-key" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_TRANSIENT_KEY; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.pub.newfname ); outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } else if( !ascii_strcasecmp( keyword, "%secring" ) ) { /* Ignore this command. */ } else log_info("skipping control '%s' (%s)\n", keyword, value ); continue; } if( !(p = strchr( p, ':' )) || p == keyword ) { err = "missing colon"; break; } if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; break; } value = p; trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { err = "unknown keyword"; break; } if( keywords[i].key != pKEYTYPE && !para ) { err = "parameter block does not start with \"Key-Type\""; break; } if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else { for( r = para; r; r = r->next ) { if( r->key == keywords[i].key ) break; } if( r ) { err = "duplicate keyword"; break; } } if ((keywords[i].key == pVERSION || keywords[i].key == pSUBVERSION)) ; /* Ignore version. */ else { r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); r->next = para; para = r; } } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); else if( iobuf_error (fp) ) { log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl.pub.fname); xfree( outctrl.pub.fname ); xfree( outctrl.pub.newfname ); } xfree (line); release_parameter_list( para ); iobuf_close (fp); release_armor_context (outctrl.pub.afx); } /* Helper for quick_generate_keypair. */ static struct para_data_s * quickgen_set_para (struct para_data_s *para, int for_subkey, int algo, int nbits, const char *curve, unsigned int use, int version, const char *keygrip, u32 keytime) { struct para_data_s *r; r = xmalloc_clear (sizeof *r + 50); r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; if (use) snprintf (r->u.value, 30, "%s%s%s%s%s%s%s", (use & PUBKEY_USAGE_ENC)? "encr " : "", (use & PUBKEY_USAGE_SIG)? "sign " : "", (use & PUBKEY_USAGE_AUTH)? "auth " : "", (use & PUBKEY_USAGE_CERT)? "cert " : "", (use & PUBKEY_USAGE_RENC)? "renc " : "", (use & PUBKEY_USAGE_TIME)? "time " : "", (use & PUBKEY_USAGE_GROUP)?"group ": ""); else strcpy (r->u.value, for_subkey ? "encr" : "sign"); r->next = para; para = r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYTYPE : pKEYTYPE; snprintf (r->u.value, 20, "%d", algo); r->next = para; para = r; if (keygrip) { r = xmalloc_clear (sizeof *r + strlen (keygrip)); r->key = for_subkey? pSUBKEYGRIP : pKEYGRIP; strcpy (r->u.value, keygrip); r->next = para; para = r; } else if (curve) { r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = for_subkey? pSUBKEYCURVE : pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYLENGTH : pKEYLENGTH; sprintf (r->u.value, "%u", nbits); r->next = para; para = r; } r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBVERSION : pVERSION; snprintf (r->u.value, 20, "%d", version); r->next = para; para = r; if (keytime) { r = xmalloc_clear (sizeof *r); r->key = for_subkey? pSUBKEYCREATIONDATE : pKEYCREATIONDATE; r->u.creation = keytime; r->next = para; para = r; } return para; } /* * Unattended generation of a standard key. */ void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; int use_tty; memset (&outctrl, 0, sizeof outctrl); use_tty = (!opt.batch && !opt.answer_yes && !*algostr && !*usagestr && !*expirestr && !cpr_enabled () && gnupg_isatty (fileno (stdin)) && gnupg_isatty (fileno (stdout)) && gnupg_isatty (fileno (stderr))); r = xmalloc_clear (sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; uid = trim_spaces (r->u.value); if (!*uid || (!opt.allow_freeform_uid && !is_valid_user_id (uid))) { log_error (_("Key generation failed: %s\n"), gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } /* If gpg is directly used on the console ask whether a key with the given user id shall really be created. */ if (use_tty) { tty_printf (_("About to create a key for:\n \"%s\"\n\n"), uid); if (!cpr_get_answer_is_yes_def ("quick_keygen.okay", _("Continue? (Y/n) "), 1)) goto leave; } /* Check whether such a user ID already exists. */ { KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = uid; kdbhd = keydb_new (ctrl); if (!kdbhd) goto leave; err = keydb_search (kdbhd, &desc, 1, NULL); keydb_release (kdbhd); if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_info (_("A key for \"%s\" already exists\n"), uid); if (opt.answer_yes) ; else if (!use_tty || !cpr_get_answer_is_yes_def ("quick_keygen.force", _("Create anyway? (y/N) "), 0)) { write_status_error ("genkey", gpg_error (304)); log_inc_errorcount (); /* we used log_info */ goto leave; } log_info (_("creating anyway\n")); } } if (!*expirestr || strcmp (expirestr, "-") == 0) expirestr = default_expiration_interval; if ((!*algostr || !ascii_strcasecmp (algostr, "default") || !ascii_strcasecmp (algostr, "future-default") || !ascii_strcasecmp (algostr, "futuredefault") || !ascii_strcasecmp (algostr, "card")) && (!*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-"))) { /* Use default key parameters. */ int algo, subalgo, version, subversion; unsigned int size, subsize; unsigned int keyuse, subkeyuse; const char *curve, *subcurve; char *keygrip, *subkeygrip; u32 keytime, subkeytime; err = parse_key_parameter_string (ctrl, algostr, -1, 0, &algo, &size, &keyuse, &curve, &version, &keygrip, &keytime, &subalgo, &subsize, &subkeyuse, &subcurve, &subversion, &subkeygrip, &subkeytime); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); goto leave; } para = quickgen_set_para (para, 0, algo, size, curve, keyuse, version, keygrip, keytime); if (subalgo) para = quickgen_set_para (para, 1, subalgo, subsize, subcurve, subkeyuse, subversion, subkeygrip, subkeytime); if (*expirestr) { u32 expire; expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { err = gpg_error (GPG_ERR_INV_VALUE); log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); goto leave; } r = xmalloc_clear (sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; } xfree (keygrip); xfree (subkeygrip); } else { /* Extended unattended mode. Creates only the primary key. */ int algo, version; unsigned int use; u32 expire; unsigned int nbits; const char *curve; char *keygrip; u32 keytime; err = parse_algo_usage_expire (ctrl, 0, algostr, usagestr, expirestr, &algo, &use, &expire, &nbits, &curve, &version, &keygrip, &keytime); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); goto leave; } para = quickgen_set_para (para, 0, algo, nbits, curve, use, version, keygrip, keytime); r = xmalloc_clear (sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; xfree (keygrip); } /* If the pinentry loopback mode is not and we have a static passphrase (i.e. set with --passphrase{,-fd,-file} while in batch mode), we use that passphrase for the new key. */ if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) { const char *s = get_static_passphrase (); r = xmalloc_clear (sizeof *r + strlen (s)); r->key = pPASSPHRASE; strcpy (r->u.value, s); r->next = para; para = r; } if (!ascii_strcasecmp (algostr, "card") || !ascii_strncasecmp (algostr, "card/", 5)) { r = xmalloc_clear (sizeof *r); r->key = pCARDKEY; r->u.abool = 1; r->next = para; para = r; } proc_parameter_file (ctrl, para, "[internal]", &outctrl, 0); leave: release_parameter_list (para); } /* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the function will create the keys on an * OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is * NOT NULL, the encryption key for the card is generated on the host, * imported to the card and a backup file created by gpg-agent. If * FULL is not set only the basic prompts are used (except for batch * mode). */ void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key) { gpg_error_t err; unsigned int nbits; char *uid = NULL; int algo; unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; #ifndef ENABLE_CARD_SUPPORT (void)card_backup_key; #endif memset( &outctrl, 0, sizeof( outctrl ) ); if (opt.batch && card_serialno) { /* We don't yet support unattended key generation with a card * serial number. */ log_error (_("can't do this in batch mode\n")); print_further_info ("key generation with card serial number"); return; } if (opt.batch) { read_parameter_file (ctrl, fname); return; } if (card_serialno) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; memset (&info, 0, sizeof (info)); err = agent_scd_getattr ("KEY-ATTR", &info); if (err) { log_error (_("error getting current key info: %s\n"), gpg_strerror (err)); return; } r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[0].algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy (r->u.value, "sign"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[1].algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy (r->u.value, "encrypt"); r->next = para; para = r; if (info.key_attr[1].algo == PUBKEY_ALGO_RSA) { r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYLENGTH; sprintf( r->u.value, "%u", info.key_attr[1].nbits); r->next = para; para = r; } else if (info.key_attr[1].algo == PUBKEY_ALGO_ECDSA || info.key_attr[1].algo == PUBKEY_ALGO_EDDSA || info.key_attr[1].algo == PUBKEY_ALGO_ECDH) { r = xcalloc (1, sizeof *r + strlen (info.key_attr[1].curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, info.key_attr[1].curve); r->next = para; para = r; } r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[2].algo ); r->next = para; para = r; if (card_backup_key) { r = xcalloc (1, sizeof *r + 1); r->key = pCARDBACKUPKEY; strcpy (r->u.value, "1"); r->next = para; para = r; } #endif /*ENABLE_CARD_SUPPORT*/ } else if (full) /* Full featured key generation. */ { int subkey_algo; char *key_from_hexgrip = NULL; int cardkey; u32 keytime; algo = ask_algo (ctrl, 0, &subkey_algo, &use, &key_from_hexgrip, &cardkey, &keytime); if (key_from_hexgrip) { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 40 ); r->key = pKEYGRIP; strcpy (r->u.value, key_from_hexgrip); r->next = para; para = r; r = xmalloc_clear (sizeof *r); r->key = pCARDKEY; r->u.abool = cardkey; r->next = para; para = r; if (cardkey) { r = xmalloc_clear (sizeof *r); r->key = pKEYCREATIONDATE; r->u.creation = keytime; r->next = para; para = r; } xfree (key_from_hexgrip); } else { const char *curve = NULL; if (subkey_algo) { /* Create primary and subkey at once. */ both = 1; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, &subkey_algo, NULL); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; if (!strcmp (curve, "X448") || !strcmp (curve, "Ed448")) { r = xmalloc_clear (sizeof *r + 20); r->key = pVERSION; snprintf (r->u.value, 20, "%d", 5); r->next = para; para = r; } } else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; para = r; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { if (algo == PUBKEY_ALGO_EDDSA && subkey_algo == PUBKEY_ALGO_ECDH) { /* Need to switch to a different curve for the encryption key. */ if (!strcmp (curve, "Ed25519")) curve = "Curve25519"; else { curve = "X448"; r = xmalloc_clear (sizeof *r + 20); r->key = pSUBVERSION; snprintf (r->u.value, 20, "%d", 5); r->next = para; para = r; } } r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } } else /* Create only a single key. */ { /* For ECC we need to ask for the curve before storing the algo because ask_curve may change the algo. */ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, NULL, NULL); r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; if (!strcmp (curve, "X448") || !strcmp (curve, "Ed448")) { r = xmalloc_clear (sizeof *r + 20); r->key = pVERSION; snprintf (r->u.value, 20, "%d", 5); r->next = para; para = r; } } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } nbits = 0; } if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { /* The curve has already been set. */ } else { nbits = ask_keysize (both? subkey_algo : algo, nbits); r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } } } else /* Default key generation. */ { int subalgo, version, subversion; unsigned int size, subsize; unsigned int keyuse, subkeyuse; const char *curve, *subcurve; char *keygrip, *subkeygrip; u32 keytime, subkeytime; tty_printf ( _("Note: Use \"%s %s\"" " for a full featured key generation dialog.\n"), #if USE_GPG2_HACK GPG_NAME "2" #else GPG_NAME #endif , "--full-generate-key" ); err = parse_key_parameter_string (ctrl, NULL, -1, 0, &algo, &size, &keyuse, &curve, &version, &keygrip, &keytime, &subalgo, &subsize, &subkeyuse, &subcurve, &subversion, &subkeygrip, &subkeytime); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); return; } para = quickgen_set_para (para, 0, algo, size, curve, keyuse, version, keygrip, keytime); if (subalgo) para = quickgen_set_para (para, 1, subalgo, subsize, subcurve, subkeyuse, subversion, subkeygrip, subkeytime); xfree (keygrip); xfree (subkeygrip); } expire = full? ask_expire_interval (0, NULL) : parse_expire_string (default_expiration_interval); r = xcalloc (1, sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; r = xcalloc (1, sizeof *r + 20); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; uid = ask_user_id (0, full, NULL); if (!uid) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } r = xcalloc (1, sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; proc_parameter_file (ctrl, para, "[internal]", &outctrl, !!card_serialno); release_parameter_list (para); } /* Create and delete a dummy packet to start off a list of kbnodes. */ static void start_tree(KBNODE *tree) { PACKET *pkt; pkt=xmalloc_clear(sizeof(*pkt)); pkt->pkttype=PKT_NONE; *tree=new_kbnode(pkt); delete_kbnode(*tree); } /* Write the *protected* secret key to the file. */ static gpg_error_t card_write_key_to_backup_file (PKT_public_key *sk, const char *backup_dir) { gpg_error_t err = 0; int rc; char keyid_buffer[2 * 8 + 1]; char name_buffer[50]; char *fname; IOBUF fp; mode_t oldmask; PACKET *pkt = NULL; format_keyid (pk_keyid (sk), KF_LONG, keyid_buffer, sizeof (keyid_buffer)); snprintf (name_buffer, sizeof name_buffer, "sk_%s.gpg", keyid_buffer); fname = make_filename (backup_dir, name_buffer, NULL); /* Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp = iobuf_create (fname, 1); umask (oldmask); if (!fp) { err = gpg_error_from_syserror (); log_error (_("can't create backup file '%s': %s\n"), fname, strerror (errno) ); goto leave; } pkt = xcalloc (1, sizeof *pkt); pkt->pkttype = PKT_SECRET_KEY; pkt->pkt.secret_key = sk; rc = build_packet (fp, pkt); if (rc) { log_error ("build packet failed: %s\n", gpg_strerror (rc)); iobuf_cancel (fp); } else { char *fprbuf; iobuf_close (fp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); log_info (_("Note: backup of card key saved to '%s'\n"), fname); fprbuf = hexfingerprint (sk, NULL, 0); if (!fprbuf) { err = gpg_error_from_syserror (); goto leave; } write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, fname, strlen (fname), 0); xfree (fprbuf); } leave: xfree (pkt); xfree (fname); return err; } /* Store key to card and make a backup file in OpenPGP format. */ static gpg_error_t card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, const char *backup_dir) { PKT_public_key *sk; gnupg_isotime_t timestamp; gpg_error_t err; char *hexgrip; int rc; struct agent_card_info_s info; gcry_cipher_hd_t cipherhd = NULL; char *cache_nonce = NULL; void *kek = NULL; size_t keklen; char *ecdh_param_str = NULL; sk = copy_public_key (NULL, sub_psk); if (!sk) return gpg_error_from_syserror (); epoch2isotime (timestamp, (time_t)sk->timestamp); if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { ecdh_param_str = ecdh_param_str_from_pk (sk); if (!ecdh_param_str) { free_public_key (sk); return gpg_error_from_syserror (); } } err = hexkeygrip_from_pk (sk, &hexgrip); if (err) { xfree (ecdh_param_str); free_public_key (sk); goto leave; } memset(&info, 0, sizeof (info)); rc = agent_scd_getattr ("SERIALNO", &info); if (rc) { xfree (ecdh_param_str); free_public_key (sk); err = (gpg_error_t)rc; goto leave; } rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp, ecdh_param_str); xfree (info.serialno); if (rc) { err = (gpg_error_t)rc; goto leave; } err = agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) { log_error ("error setting up an encryption context: %s\n", gpg_strerror (err)); goto leave; } err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, &cache_nonce, hexgrip, sk, NULL); if (err) { log_error ("error getting secret key from agent: %s\n", gpg_strerror (err)); goto leave; } err = card_write_key_to_backup_file (sk, backup_dir); if (err) log_error ("writing card key to backup file: %s\n", gpg_strerror (err)); else /* Remove secret key data in agent side. */ agent_scd_learn (NULL, 1); leave: xfree (ecdh_param_str); xfree (cache_nonce); gcry_cipher_close (cipherhd); xfree (kek); xfree (hexgrip); free_public_key (sk); return err; } static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, struct output_control_s *outctrl, int card) { gpg_error_t err; KBNODE pub_root = NULL; const char *s; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; struct revocation_key *revkey; int did_sub = 0; u32 keytimestamp, subkeytimestamp, authkeytimestamp, signtimestamp; char *cache_nonce = NULL; int algo; u32 expire; const char *key_from_hexgrip = NULL; int cardkey; unsigned int keygen_flags; unsigned int idx; if (outctrl->dryrun) { log_info("dry-run mode - key generation skipped\n"); return; } if ( outctrl->use_files ) { if ( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; if (outctrl->pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl->pub.fname); xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; gpg_err_set_errno (EPERM); } else outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0); if (!outctrl->pub.stream) { log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname, strerror(errno) ); return; } if (opt.armor) { outctrl->pub.afx->what = 1; push_armor_filter (outctrl->pub.afx, outctrl->pub.stream); } } log_assert( outctrl->pub.stream ); if (opt.verbose) log_info (_("writing public key to '%s'\n"), outctrl->pub.fname ); } /* We create the packets as a tree of kbnodes. Because the structure we create is known in advance we simply generate a linked list. The first packet is a dummy packet which we flag as deleted. The very first packet must always be a KEY packet. */ start_tree (&pub_root); cardkey = get_parameter_bool (para, pCARDKEY); /* In the case that the keys are created from the card we need to * take the timestamps from the card. Only in this case a * pSUBKEYCREATIONDATE or pAUTHKEYCREATIONDATE might be defined and * then we need to use that so that the fingerprint of the subkey * also matches the pre-computed and stored one on the card. In * this case we also use the current time to create the * self-signatures. */ keytimestamp = get_parameter_u32 (para, pKEYCREATIONDATE); if (!keytimestamp) keytimestamp = make_timestamp (); subkeytimestamp = cardkey? get_parameter_u32 (para, pSUBKEYCREATIONDATE) : 0; if (!subkeytimestamp) subkeytimestamp = keytimestamp; authkeytimestamp = cardkey? get_parameter_u32 (para, pAUTHKEYCREATIONDATE): 0; if (!authkeytimestamp) authkeytimestamp = keytimestamp; signtimestamp = cardkey? make_timestamp () : keytimestamp; /* log_debug ("XXX: cardkey ..: %d\n", cardkey); */ /* log_debug ("XXX: keytime ..: %s\n", isotimestamp (keytimestamp)); */ /* log_debug ("XXX: subkeytime: %s\n", isotimestamp (subkeytimestamp)); */ /* log_debug ("XXX: authkeytim: %s\n", isotimestamp (authkeytimestamp)); */ /* log_debug ("XXX: signtime .: %s\n", isotimestamp (signtimestamp)); */ /* Fixme: Check that this comment is still valid: Note that, depending on the backend (i.e. the used scdaemon version), the card key generation may update TIMESTAMP for each key. Thus we need to pass TIMESTAMP to all signing function to make sure that the binding signature is done using the timestamp of the corresponding (sub)key and not that of the primary key. An alternative implementation could tell the signing function the node of the subkey but that is more work than just to pass the current timestamp. */ algo = get_parameter_algo (ctrl, para, pKEYTYPE, NULL ); expire = get_parameter_u32( para, pKEYEXPIRE ); key_from_hexgrip = get_parameter_value (para, pKEYGRIP); if (cardkey && !key_from_hexgrip) BUG (); keygen_flags = outctrl->keygen_flags; if (get_parameter_uint (para, pVERSION) == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (key_from_hexgrip) err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, cardkey, pub_root, keytimestamp, expire, 0, &keygen_flags); else if (!card) err = do_create (algo, get_parameter_uint( para, pKEYLENGTH ), get_parameter_value (para, pKEYCURVE), pub_root, keytimestamp, expire, 0, &keygen_flags, get_parameter_passphrase (para), &cache_nonce, NULL, NULL, NULL); else err = gen_card_key (1, algo, 1, pub_root, &keytimestamp, expire, &keygen_flags); /* Get the pointer to the generated public key packet. */ if (!err) { pri_psk = pub_root->next->pkt->pkt.public_key; log_assert (pri_psk); /* Make sure a few fields are correctly set up before going further. */ pri_psk->flags.primary = 1; keyid_from_pk (pri_psk, NULL); /* We don't use pk_keyid to get keyid, because it also asserts that main_keyid is set! */ keyid_copy (pri_psk->main_keyid, pri_psk->keyid); } /* Write all signatures specifying designated revokers. */ for (idx=0; !err && (revkey = get_parameter_revkey (para, pREVOKER, idx)); idx++) err = write_direct_sig (ctrl, pub_root, pri_psk, revkey, signtimestamp, cache_nonce); if (!err && (s = get_parameter_value (para, pUSERID))) { err = write_uid (pub_root, s ); if (!err) err = write_selfsigs (ctrl, pub_root, pri_psk, get_parameter_uint (para, pKEYUSAGE), signtimestamp, cache_nonce); } /* Write the auth key to the card before the encryption key. This is a partial workaround for a PGP bug (as of this writing, all versions including 8.1), that causes it to try and encrypt to the most recent subkey regardless of whether that subkey is actually an encryption type. In this case, the auth key is an RSA key so it succeeds. */ if (!err && card && get_parameter (para, pAUTHKEYTYPE)) { err = gen_card_key (3, get_parameter_algo (ctrl, para, pAUTHKEYTYPE, NULL ), 0, pub_root, &authkeytimestamp, expire, &keygen_flags); if (!err) err = write_keybinding (ctrl, pub_root, pri_psk, NULL, PUBKEY_USAGE_AUTH, signtimestamp, cache_nonce); } if (!err && get_parameter (para, pSUBKEYTYPE)) { int subkey_algo = get_parameter_algo (ctrl, para, pSUBKEYTYPE, NULL); key_from_hexgrip = get_parameter_value (para, pSUBKEYGRIP); keygen_flags = outctrl->keygen_flags; if (get_parameter_uint (para, pSUBVERSION) == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (key_from_hexgrip) err = do_create_from_keygrip (ctrl, subkey_algo, key_from_hexgrip, cardkey, pub_root, subkeytimestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, &keygen_flags); else if (get_parameter_value (para, pCARDBACKUPKEY)) { int lastmode; unsigned int mykeygenflags = KEYGEN_FLAG_NO_PROTECTION; err = agent_set_ephemeral_mode (ctrl, 1, &lastmode); if (err) log_error ("error switching to ephemeral mode: %s\n", gpg_strerror (err)); else { err = do_create (subkey_algo, get_parameter_uint (para, pSUBKEYLENGTH), get_parameter_value (para, pSUBKEYCURVE), pub_root, subkeytimestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, &mykeygenflags, get_parameter_passphrase (para), &cache_nonce, NULL, NULL, NULL); /* Get the pointer to the generated public subkey packet. */ if (!err) { kbnode_t node; for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; log_assert (sub_psk); err = card_store_key_with_backup (ctrl, sub_psk, gnupg_homedir ()); } /* Reset the ephemeral mode as needed. */ if (!lastmode && agent_set_ephemeral_mode (ctrl, 0, NULL)) log_error ("error clearing the ephemeral mode\n"); } } else if (!card) { err = do_create (subkey_algo, get_parameter_uint (para, pSUBKEYLENGTH), get_parameter_value (para, pSUBKEYCURVE), pub_root, subkeytimestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, &keygen_flags, get_parameter_passphrase (para), &cache_nonce, NULL, NULL, NULL); if (!err) { kbnode_t node; for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; log_assert (sub_psk); } } else { err = gen_card_key (2, subkey_algo, 0, pub_root, &subkeytimestamp, expire, &keygen_flags); } if (!err) err = write_keybinding (ctrl, pub_root, pri_psk, sub_psk, get_parameter_uint (para, pSUBKEYUSAGE), signtimestamp, cache_nonce); did_sub = 1; } if (!err && outctrl->use_files) /* Direct write to specified files. */ { err = write_keyblock (outctrl->pub.stream, pub_root); if (err) log_error ("can't write public key: %s\n", gpg_strerror (err)); } else if (!err) /* Write to the standard keyrings. */ { KEYDB_HANDLE pub_hd; pub_hd = keydb_new (ctrl); if (!pub_hd) err = gpg_error_from_syserror (); else { err = keydb_locate_writable (pub_hd); if (err) log_error (_("no writable public keyring found: %s\n"), gpg_strerror (err)); } if (!err && opt.verbose) { log_info (_("writing public key to '%s'\n"), keydb_get_resource_name (pub_hd)); } if (!err) { err = keydb_insert_keyblock (pub_hd, pub_root); if (err) log_error (_("error writing public keyring '%s': %s\n"), keydb_get_resource_name (pub_hd), gpg_strerror (err)); } keydb_release (pub_hd); if (!err) { int no_enc_rsa; PKT_public_key *pk; no_enc_rsa = ((get_parameter_algo (ctrl, para, pKEYTYPE, NULL) == PUBKEY_ALGO_RSA) && get_parameter_uint (para, pKEYUSAGE) && !((get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC)) ); pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; if (!opt.flags.no_auto_trust_new_key) update_ownertrust (ctrl, pk, ((get_ownertrust (ctrl, pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); gen_standard_revoke (ctrl, pk, cache_nonce); /* Get rid of the first empty packet. */ commit_kbnode (&pub_root); if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); tty_printf ("\n"); merge_keys_and_selfsig (ctrl, pub_root); list_keyblock_direct (ctrl, pub_root, 0, 1, opt.fingerprint || opt.with_fingerprint, 1); } if (!opt.batch && (get_parameter_algo (ctrl, para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA || no_enc_rsa ) && !get_parameter (para, pSUBKEYTYPE) ) { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "subkey for this purpose.\n") ); } } } if (err) { if (opt.batch) log_error ("key generation failed: %s\n", gpg_strerror (err) ); else tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) ); write_status_error (card? "card_key_generate":"key_generate", err); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else { PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } release_kbnode (pub_root); xfree (cache_nonce); } static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, const char *expirestr, int *r_algo, unsigned int *r_usage, u32 *r_expire, unsigned int *r_nbits, const char **r_curve, int *r_version, char **r_keygrip, u32 *r_keytime) { gpg_error_t err; int algo; unsigned int use, nbits; u32 expire; int wantuse; int version = 4; const char *curve = NULL; *r_curve = NULL; if (r_keygrip) *r_keygrip = NULL; if (r_keytime) *r_keytime = 0; nbits = 0; /* Parse the algo string. */ if (algostr && *algostr == '&' && strlen (algostr) == 41) { /* Take algo from existing key. */ algo = check_keygrip (ctrl, algostr+1); /* FIXME: We need the curve name as well. */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } err = parse_key_parameter_string (ctrl, algostr, for_subkey? 1 : 0, usagestr? parse_usagestr (usagestr):0, &algo, &nbits, &use, &curve, &version, r_keygrip, r_keytime, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (err) { if (r_keygrip) { xfree (*r_keygrip); *r_keygrip = NULL; } return err; } /* Parse the usage string. */ if (!usagestr || !*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-")) ; /* Keep usage from parse_key_parameter_string. */ else if ((wantuse = parse_usagestr (usagestr)) != -1) use = wantuse; else { if (r_keygrip) { xfree (*r_keygrip); *r_keygrip = NULL; } return gpg_error (GPG_ERR_INV_VALUE); } /* Make sure a primary key has the CERT usage. */ if (!for_subkey) use |= PUBKEY_USAGE_CERT; /* Check that usage is possible. NB: We have the same check in * parse_key_parameter_string but need it here again in case the * separate usage value has been given. */ if (/**/((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) && !pubkey_get_nsig (algo)) || ((use & PUBKEY_USAGE_ENC) && !pubkey_get_nenc (algo)) || (for_subkey && (use & PUBKEY_USAGE_CERT))) { if (r_keygrip) { xfree (*r_keygrip); *r_keygrip = NULL; } return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Parse the expire string. */ expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { if (r_keygrip) { xfree (*r_keygrip); *r_keygrip = NULL; } return gpg_error (GPG_ERR_INV_VALUE); } if (curve) *r_curve = curve; *r_algo = algo; *r_usage = use; *r_expire = expire; *r_nbits = nbits; *r_version = version; return 0; } /* Add a new subkey to an existing key. Returns 0 if a new key has been generated and put into the keyblocks. If any of ALGOSTR, USAGESTR, or EXPIRESTR is NULL interactive mode is used. */ gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err = 0; int interactive; kbnode_t node; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; int algo; unsigned int use; u32 expire; unsigned int nbits = 0; const char *curve = NULL; u32 cur_time; char *key_from_hexgrip = NULL; u32 keytime = 0; int cardkey = 0; char *hexgrip = NULL; char *serialno = NULL; char *cache_nonce = NULL; char *passwd_nonce = NULL; int keygen_flags = 0; interactive = (!algostr || !usagestr || !expirestr); /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; primary key missing in keyblock!\n"); err = gpg_error (GPG_ERR_BUG); goto leave; } pri_psk = node->pkt->pkt.public_key; cur_time = make_timestamp (); if (pri_psk->timestamp > cur_time) { ulong d = pri_psk->timestamp - cur_time; log_info ( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_psk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_CONFLICT); goto leave; } err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; /* FIXME: Right now the primary key won't be a dual key. But this * will change */ if (agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) { if (interactive) tty_printf (_("Secret parts of primary key are not available.\n")); else log_info ( _("Secret parts of primary key are not available.\n")); err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } if (serialno) { if (interactive) tty_printf (_("Secret parts of primary key are stored on-card.\n")); else log_info ( _("Secret parts of primary key are stored on-card.\n")); } if (interactive) { algo = ask_algo (ctrl, 1, NULL, &use, &key_from_hexgrip, &cardkey, &keytime); log_assert (algo); if (key_from_hexgrip) nbits = 0; else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, NULL, NULL); if (curve && (!strcmp (curve, "X448") || !strcmp (curve, "Ed448"))) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; } else nbits = ask_keysize (algo, 0); expire = ask_expire_interval (0, NULL); if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } } else /* Unattended mode. */ { int version; err = parse_algo_usage_expire (ctrl, 1, algostr, usagestr, expirestr, &algo, &use, &expire, &nbits, &curve, &version, &key_from_hexgrip, &keytime); if (err) goto leave; if (version == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; } /* Verify the passphrase now so that we get a cache item for the * primary key passphrase. The agent also returns a passphrase * nonce, which we can use to set the passphrase for the subkey to * that of the primary key. */ { char *desc = gpg_format_keydesc (ctrl, pri_psk, FORMAT_KEYDESC_NORMAL, 1); err = agent_passwd (ctrl, hexgrip, desc, 1 /*=verify*/, &cache_nonce, &passwd_nonce); xfree (desc); if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT) err = 0; /* Very likely that the key is on a card. */ if (err) goto leave; } /* Start creation. */ if (key_from_hexgrip) { err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, cardkey, keyblock, keytime? keytime : cur_time, expire, 1, &keygen_flags); } else { const char *passwd; /* If the pinentry loopback mode is not and we have a static passphrase (i.e. set with --passphrase{,-fd,-file} while in batch mode), we use that passphrase for the new subkey. */ if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) passwd = get_static_passphrase (); else passwd = NULL; err = do_create (algo, nbits, curve, keyblock, cur_time, expire, 1, &keygen_flags, passwd, &cache_nonce, &passwd_nonce, NULL, NULL); } if (err) goto leave; /* Get the pointer to the generated public subkey packet. */ for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; /* Write the binding signature. */ err = write_keybinding (ctrl, keyblock, pri_psk, sub_psk, use, cur_time, cache_nonce); if (err) goto leave; print_status_key_created ('S', sub_psk, NULL); leave: xfree (key_from_hexgrip); xfree (hexgrip); xfree (serialno); xfree (cache_nonce); xfree (passwd_nonce); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); write_status_error (cardkey? "card_key_generate":"key_generate", err); print_status_key_not_created ( NULL ); } return err; } #ifdef ENABLE_CARD_SUPPORT /* Generate a subkey on a card. */ gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_pk = NULL; unsigned int use; u32 expire; u32 cur_time; struct para_data_s *para = NULL; PKT_public_key *sub_pk = NULL; int algo; struct agent_card_info_s info; int keygen_flags = 0; /* FIXME!!! */ log_assert (keyno >= 1 && keyno <= 3); memset (&info, 0, sizeof (info)); err = agent_scd_getattr ("KEY-ATTR", &info); if (err) { log_error (_("error getting current key info: %s\n"), gpg_strerror (err)); return err; } algo = info.key_attr[keyno-1].algo; para = xtrycalloc (1, sizeof *para + strlen (serialno) ); if (!para) { err = gpg_error_from_syserror (); goto leave; } para->key = pSERIALNO; strcpy (para->u.value, serialno); /* Break out the primary secret key */ node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; public key lost!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pri_pk = node->pkt->pkt.public_key; cur_time = make_timestamp(); if (pri_pk->timestamp > cur_time) { ulong d = pri_pk->timestamp - cur_time; log_info (d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_pk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } expire = ask_expire_interval (0, NULL); if (keyno == 1) use = PUBKEY_USAGE_SIG; else if (keyno == 2) use = PUBKEY_USAGE_ENC; else use = PUBKEY_USAGE_AUTH; if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } /* Note, that depending on the backend, the card key generation may update CUR_TIME. */ err = gen_card_key (keyno, algo, 0, pub_keyblock, &cur_time, expire, &keygen_flags); /* Get the pointer to the generated public subkey packet. */ if (!err) { for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; log_assert (sub_pk); err = write_keybinding (ctrl, pub_keyblock, pri_pk, sub_pk, use, cur_time, NULL); } leave: if (err) log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); else print_status_key_created ('S', sub_pk, NULL); release_parameter_list (para); return err; } #endif /* !ENABLE_CARD_SUPPORT */ /* * Write a keyblock to an output stream */ static int write_keyblock( IOBUF out, KBNODE node ) { for( ; node ; node = node->next ) { if(!is_deleted_kbnode(node)) { int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } } } return 0; } /* Note that timestamp is an in/out arg. */ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval, int *keygen_flags) { #ifdef ENABLE_CARD_SUPPORT gpg_error_t err; PACKET *pkt; PKT_public_key *pk; char keyid[10]; unsigned char *public; gcry_sexp_t s_key; snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno); pk = xtrycalloc (1, sizeof *pk ); if (!pk) return gpg_error_from_syserror (); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { xfree (pk); return gpg_error_from_syserror (); } /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ err = agent_scd_genkey (keyno, 1, timestamp); /* The code below is not used because we force creation of * the a card key (3rd arg). * if (gpg_err_code (rc) == GPG_ERR_EEXIST) * { * tty_printf ("\n"); * log_error ("WARNING: key does already exists!\n"); * tty_printf ("\n"); * if ( cpr_get_answer_is_yes( "keygen.card.replace_key", * _("Replace existing key? "))) * rc = agent_scd_genkey (keyno, 1, timestamp); * } */ if (err) { log_error ("key generation failed: %s\n", gpg_strerror (err)); xfree (pkt); xfree (pk); return err; } /* Send the READKEY command so that the agent creates a shadow key for card key. We need to do that now so that we are able to create the self-signatures. */ err = agent_readkey (NULL, 1, keyid, &public); if (err) { xfree (pkt); xfree (pk); return err; } err = gcry_sexp_sscan (&s_key, NULL, public, gcry_sexp_canon_len (public, 0, NULL, NULL)); xfree (public); if (err) { xfree (pkt); xfree (pk); return err; } /* Force creation of v5 keys for X448. */ if (curve_is_448 (s_key)) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (algo == PUBKEY_ALGO_RSA) err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); free_public_key (pk); return err; } pk->timestamp = *timestamp; pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; #else (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ }