sm: Print Yubikey attestation extensions with --dump-cert.
[gnupg.git] / g10 / pubkey-enc.c
index d90559d..e0a6e8a 100644 (file)
  * 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 <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #include "gpg.h"
-#include "util.h"
+#include "../common/util.h"
 #include "packet.h"
 #include "keydb.h"
 #include "trustdb.h"
-#include "cipher.h"
-#include "status.h"
+#include "../common/status.h"
 #include "options.h"
 #include "main.h"
-#include "i18n.h"
+#include "../common/i18n.h"
 #include "pkglue.h"
 #include "call-agent.h"
+#include "../common/host2net.h"
+#include "../common/compliance.h"
 
 
-static gpg_error_t get_it (PKT_pubkey_enc *k,
+static gpg_error_t get_it (ctrl_t ctrl, struct pubkey_enc_list *k,
                            DEK *dek, PKT_public_key *sk, u32 *keyid);
 
 
@@ -55,7 +55,7 @@ is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
           PKT_user_id *uid = k->pkt->pkt.user_id;
           prefitem_t *prefs = uid->prefs;
 
-          if (uid->created && prefs && !uid->is_revoked && !uid->is_expired)
+          if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired)
             {
               for (; prefs->type; prefs++)
                 if (prefs->type == type && prefs->value == algo)
@@ -72,100 +72,130 @@ is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
  * which should have been allocated in secure memory by the caller.
  */
 gpg_error_t
-get_session_key (PKT_pubkey_enc * k, DEK * dek)
+get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
 {
   PKT_public_key *sk = NULL;
   int rc;
+  void *enum_context = NULL;
+  u32 keyid[2];
+  int search_for_secret_keys = 1;
 
-  rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC);
-  if (rc)
-    goto leave;
+  if (DBG_CLOCK)
+    log_clock ("get_session_key enter");
 
-  if ((k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets)
+  while (search_for_secret_keys)
     {
+      struct pubkey_enc_list *k;
+
       sk = xmalloc_clear (sizeof *sk);
-      sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo.  */
-      if (!(rc = get_seckey (sk, k->keyid)))
-        rc = get_it (k, dek, sk, k->keyid);
-    }
-  else if (opt.skip_hidden_recipients)
-    rc = gpg_error (GPG_ERR_NO_SECKEY);
-  else  /* Anonymous receiver: Try all available secret keys.  */
-    {
-      void *enum_context = NULL;
-      u32 keyid[2];
-      char *p;
+      rc = enum_secret_keys (ctrl, &enum_context, sk);
+      if (rc)
+        {
+          rc = GPG_ERR_NO_SECKEY;
+          break;
+        }
 
-      for (;;)
+      if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC))
+        continue;
+
+      /* Check compliance.  */
+      if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
+                                 sk->pubkey_algo,
+                                 sk->pkey, nbits_from_pk (sk), NULL))
         {
-          if (sk)
-            free_public_key (sk);
-          sk = xmalloc_clear (sizeof *sk);
-          rc = -1; /* FIXME:enum_secret_keys (&enum_context, sk, 1, 0);*/
-          if (rc)
-            {
-              rc = G10ERR_NO_SECKEY;
-              break;
-            }
+          log_info (_("key %s is not suitable for decryption"
+                      " in %s mode\n"),
+                    keystr_from_pk (sk),
+                    gnupg_compliance_option_string (opt.compliance));
+          continue;
+        }
+
+      /* FIXME: The list needs to be sorted so that we try the keys in
+       * an appropriate order.  For example:
+       * - On-disk keys w/o protection
+       * - On-disk keys with a cached passphrase
+       * - On-card keys of an active card
+       * - On-disk keys with protection
+       * - On-card keys from cards which are not plugged it.  Here a
+       *   cancel-all button should stop aksing for other cards.
+       * Without any anonymous keys the sorting can be skipped.
+       */
+      for (k = list; k; k = k->next)
+        {
+          if (!(k->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
+                || k->pubkey_algo == PUBKEY_ALGO_ECDH
+                || k->pubkey_algo == PUBKEY_ALGO_RSA
+                || k->pubkey_algo == PUBKEY_ALGO_RSA_E
+                || k->pubkey_algo == PUBKEY_ALGO_ELGAMAL))
+            continue;
+
+          if (openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC))
+            continue;
+
+          k->result = GPG_ERR_NO_SECKEY;
+
           if (sk->pubkey_algo != k->pubkey_algo)
             continue;
+
           keyid_from_pk (sk, keyid);
-          log_info (_("anonymous recipient; trying secret key %s ...\n"),
-                    keystr (keyid));
 
-          if (!opt.try_all_secrets && !is_status_enabled ())
+          if (!k->keyid[0] && !k->keyid[1])
             {
-              p = get_last_passphrase ();
-              set_next_passphrase (p);
-              xfree (p);
-            }
+              if (opt.skip_hidden_recipients)
+                continue;
 
-          /* rc = check_secret_key( sk, opt.try_all_secrets?1:-1 ); /\* ask */
-          /*                                                        only */
-          /*                                                        once *\/ */
-          /* if( !rc ) */
-          {
-            rc = get_it (k, dek, sk, keyid);
-            /* Successfully checked the secret key (either it was a
-               card, had no passphrase, or had the right passphrase)
-               but couldn't decrypt the session key, so thus that key
-               is not the anonymous recipient.  Move the next
-               passphrase into last for the next round.  We only do
-               this if the secret key was successfully checked as in
-               the normal case, check_secret_key handles this for us
-               via passphrase_to_dek.  */
-            if (rc)
-              next_to_last_passphrase ();
-          }
+              if (!opt.quiet)
+                log_info (_("anonymous recipient; trying secret key %s ...\n"),
+                          keystr (keyid));
+            }
+          else if (opt.try_all_secrets
+                   || (k->keyid[0] == keyid[0] && k->keyid[1] == keyid[1]))
+            ;
+          else
+            continue;
 
+          rc = get_it (ctrl, k, dek, sk, keyid);
           if (!rc)
             {
-              log_info (_("okay, we are the anonymous recipient.\n"));
+              k->result = 0;
+              if (!opt.quiet && !k->keyid[0] && !k->keyid[1])
+                log_info (_("okay, we are the anonymous recipient.\n"));
+              search_for_secret_keys = 0;
               break;
             }
+          else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
+            {
+              search_for_secret_keys = 0;
+              break; /* Don't try any more secret keys.  */
+            }
         }
-      enum_secret_keys (&enum_context, NULL, 0, 0);     /* free context */
     }
+  enum_secret_keys (ctrl, &enum_context, NULL);  /* free context */
 
-leave:
-  if (sk)
-    free_public_key (sk);
+  if (DBG_CLOCK)
+    log_clock ("get_session_key leave");
   return rc;
 }
 
 
 static gpg_error_t
-get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
+get_it (ctrl_t ctrl,
+        struct pubkey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
 {
   gpg_error_t err;
   byte *frame = NULL;
   unsigned int n;
   size_t nframe;
   u16 csum, csum2;
-  int card = 0;
+  int padding;
   gcry_sexp_t s_data;
   char *desc;
   char *keygrip;
+  byte fp[MAX_FINGERPRINT_LEN];
+  size_t fpn;
+
+  if (DBG_CLOCK)
+    log_clock ("decryption start");
 
   /* Get the keygrip.  */
   err = hexkeygrip_from_pk (sk, &keygrip);
@@ -173,15 +203,17 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
     goto leave;
 
   /* Convert the data to an S-expression.  */
-  if (sk->pubkey_algo == GCRY_PK_ELG || sk->pubkey_algo == GCRY_PK_ELG_E)
+  if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
+      || sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
     {
       if (!enc->data[0] || !enc->data[1])
         err = gpg_error (GPG_ERR_BAD_MPI);
       else
-        err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))", 
+        err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
                                enc->data[0], enc->data[1]);
     }
-  else if (sk->pubkey_algo == GCRY_PK_RSA || sk->pubkey_algo == GCRY_PK_RSA_E)
+  else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
+           || sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
     {
       if (!enc->data[0])
         err = gpg_error (GPG_ERR_BAD_MPI);
@@ -189,15 +221,31 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
         err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
                                enc->data[0]);
     }
+  else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+    {
+      if (!enc->data[0] || !enc->data[1])
+        err = gpg_error (GPG_ERR_BAD_MPI);
+      else
+        err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))",
+                               enc->data[1], enc->data[0]);
+    }
   else
     err = gpg_error (GPG_ERR_BUG);
 
   if (err)
     goto leave;
 
+  if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+    {
+      fingerprint_from_pk (sk, fp, &fpn);
+      log_assert (fpn == 20);
+    }
+
   /* Decrypt. */
-  desc = gpg_format_keydesc (sk, 1);
-  err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe);
+  desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
+  err = agent_pkdecrypt (NULL, keygrip,
+                         desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
+                         s_data, &frame, &nframe, &padding);
   xfree (desc);
   gcry_sexp_release (s_data);
   if (err)
@@ -205,7 +253,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
 
   /* Now get the DEK (data encryption key) from the frame
    *
-   * Old versions encode the DEK in in this format (msb is left):
+   * Old versions encode the DEK in this format (msb is left):
    *
    *     0  1  DEK(16 bytes)  CSUM(2 bytes)  0  RND(n bytes) 2
    *
@@ -220,42 +268,82 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
    * DEK is the encryption key (session key) with length k
    * CSUM
    */
-  if (DBG_CIPHER)
-    log_printhex ("DEK frame:", frame, nframe);
+  if (DBG_CRYPTO)
+    log_printhex (frame, nframe, "DEK frame:");
   n = 0;
-  if (!card)
+
+  if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
     {
-      if (n + 7 > nframe)
+      gcry_mpi_t shared_mpi;
+      gcry_mpi_t decoded;
+
+      /* At the beginning the frame are the bytes of shared point MPI.  */
+      err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL);
+      if (err)
         {
-          err = gpg_error (G10ERR_WRONG_SECKEY);
+          err = gpg_error (GPG_ERR_WRONG_SECKEY);
           goto leave;
         }
-      if (frame[n] == 1 && frame[nframe - 1] == 2)
+
+      err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/,
+                             shared_mpi, sk->pkey);
+      mpi_release (shared_mpi);
+      if(err)
+        goto leave;
+
+      xfree (frame);
+      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded);
+      mpi_release (decoded);
+      if (err)
+        goto leave;
+
+      /* Now the frame are the bytes decrypted but padded session key.  */
+
+      /* Allow double padding for the benefit of DEK size concealment.
+         Higher than this is wasteful. */
+      if (!nframe || frame[nframe-1] > 8*2 || nframe <= 8
+          || frame[nframe-1] > nframe)
         {
-          log_info (_("old encoding of the DEK is not supported\n"));
-          err = gpg_error (G10ERR_CIPHER_ALGO);
+          err = gpg_error (GPG_ERR_WRONG_SECKEY);
           goto leave;
         }
-      if (frame[n] != 2) /* Something went wrong.  */
+      nframe -= frame[nframe-1]; /* Remove padding.  */
+      log_assert (!n); /* (used just below) */
+    }
+  else
+    {
+      if (padding)
         {
-          err = gpg_error (G10ERR_WRONG_SECKEY);
-          goto leave;
+          if (n + 7 > nframe)
+            {
+              err = gpg_error (GPG_ERR_WRONG_SECKEY);
+              goto leave;
+            }
+          if (frame[n] == 1 && frame[nframe - 1] == 2)
+            {
+              log_info (_("old encoding of the DEK is not supported\n"));
+              err = gpg_error (GPG_ERR_CIPHER_ALGO);
+              goto leave;
+            }
+          if (frame[n] != 2) /* Something went wrong.  */
+            {
+              err = gpg_error (GPG_ERR_WRONG_SECKEY);
+              goto leave;
+            }
+          for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes.  */
+            ;
+          n++; /* Skip the zero byte.  */
         }
-      for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes.  */
-        ;
-      n++; /* Skip the zero byte.  */
     }
 
   if (n + 4 > nframe)
     {
-      err = gpg_error (G10ERR_WRONG_SECKEY);
+      err = gpg_error (GPG_ERR_WRONG_SECKEY);
       goto leave;
     }
 
   dek->keylen = nframe - (n + 1) - 2;
   dek->algo = frame[n++];
-  if (dek->algo == CIPHER_ALGO_IDEA)
-    write_status (STATUS_RSA_OR_IDEA);
   err = openpgp_cipher_test_algo (dek->algo);
   if (err)
     {
@@ -264,8 +352,6 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
           log_info (_("cipher algorithm %d%s is unknown or disabled\n"),
                     dek->algo,
                     dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : "");
-          if (dek->algo == CIPHER_ALGO_IDEA)
-            idea_cipher_warn (0);
         }
       dek->algo = 0;
       goto leave;
@@ -277,8 +363,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
     }
 
   /* Copy the key to DEK and compare the checksum.  */
-  csum = frame[nframe - 2] << 8;
-  csum |= frame[nframe - 1];
+  csum = buf16_to_u16 (frame+nframe-2);
   memcpy (dek->key, frame + n, dek->keylen);
   for (csum2 = 0, n = 0; n < dek->keylen; n++)
     csum2 += dek->key[n];
@@ -287,13 +372,17 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
       err = gpg_error (GPG_ERR_WRONG_SECKEY);
       goto leave;
     }
-  if (DBG_CIPHER)
-    log_printhex ("DEK is:", dek->key, dek->keylen);
+  if (DBG_CLOCK)
+    log_clock ("decryption ready");
+  if (DBG_CRYPTO)
+    log_printhex (dek->key, dek->keylen, "DEK is:");
 
-  /* Check that the algo is in the preferences and whether it has expired.  */
+  /* Check that the algo is in the preferences and whether it has
+   * expired.  Also print a status line with the key's fingerprint.  */
   {
     PKT_public_key *pk = NULL;
-    KBNODE pkb = get_pubkeyblock (keyid);
+    PKT_public_key *mainpk = NULL;
+    KBNODE pkb = get_pubkeyblock (ctrl, keyid);
 
     if (!pkb)
       {
@@ -306,9 +395,11 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
              && !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo))
       log_info (_("WARNING: cipher algorithm %s not found in recipient"
                   " preferences\n"), openpgp_cipher_algo_name (dek->algo));
+
     if (!err)
       {
-        KBNODE k;
+        kbnode_t k;
+        int first = 1;
 
         for (k = pkb; k; k = k->next)
           {
@@ -316,8 +407,14 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
                 || k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
               {
                 u32 aki[2];
-                keyid_from_pk (k->pkt->pkt.public_key, aki);
 
+                if (first)
+                  {
+                    first = 0;
+                    mainpk = k->pkt->pkt.public_key;
+                  }
+
+                keyid_from_pk (k->pkt->pkt.public_key, aki);
                 if (aki[0] == keyid[0] && aki[1] == keyid[1])
                   {
                     pk = k->pkt->pkt.public_key;
@@ -329,16 +426,34 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
           BUG ();
         if (pk->expiredate && pk->expiredate <= make_timestamp ())
           {
-            log_info (_("NOTE: secret key %s expired at %s\n"),
+            log_info (_("Note: secret key %s expired at %s\n"),
                       keystr (keyid), asctimestamp (pk->expiredate));
           }
       }
 
-    if (pk && pk->is_revoked)
+    if (pk && pk->flags.revoked)
       {
-        log_info (_("NOTE: key has been revoked"));
+        log_info (_("Note: key has been revoked"));
         log_printf ("\n");
-        show_revocation_reason (pk, 1);
+        show_revocation_reason (ctrl, pk, 1);
+      }
+
+    if (is_status_enabled () && pk && mainpk)
+      {
+        char pkhex[MAX_FINGERPRINT_LEN*2+1];
+        char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
+
+        hexfingerprint (pk, pkhex, sizeof pkhex);
+        hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
+
+        /* Note that we do not want to create a trustdb just for
+         * getting the ownertrust: If there is no trustdb there can't
+         * be ulitmately trusted key anyway and thus the ownertrust
+         * value is irrelevant.  */
+        write_status_printf (STATUS_DECRYPTION_KEY, "%s %s %c",
+                             pkhex, mainpkhex,
+                             get_ownertrust_info (ctrl, mainpk, 1));
+
       }
 
     release_kbnode (pkb);
@@ -364,22 +479,22 @@ get_override_session_key (DEK *dek, const char *string)
   int i;
 
   if (!string)
-    return G10ERR_BAD_KEY;
+    return GPG_ERR_BAD_KEY;
   dek->algo = atoi (string);
   if (dek->algo < 1)
-    return G10ERR_BAD_KEY;
+    return GPG_ERR_BAD_KEY;
   if (!(s = strchr (string, ':')))
-    return G10ERR_BAD_KEY;
+    return GPG_ERR_BAD_KEY;
   s++;
   for (i = 0; i < DIM (dek->key) && *s; i++, s += 2)
     {
       int c = hextobyte (s);
       if (c == -1)
-        return G10ERR_BAD_KEY;
+        return GPG_ERR_BAD_KEY;
       dek->key[i] = c;
     }
   if (*s)
-    return G10ERR_BAD_KEY;
+    return GPG_ERR_BAD_KEY;
   dek->keylen = i;
   return 0;
 }