Merge branch 'STABLE-BRANCH-2-2' into master
[gnupg.git] / g10 / mainproc.c
index c7deeab..69de865 100644 (file)
@@ -245,46 +245,116 @@ add_signature (CTX c, PACKET *pkt)
   return 1;
 }
 
-static int
+static gpg_error_t
 symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
 {
+  gpg_error_t err;
   gcry_cipher_hd_t hd;
+  unsigned int noncelen, keylen;
+  enum gcry_cipher_modes ciphermode;
+
+  if (dek->use_aead)
+    {
+      err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
+      if (err)
+        return err;
+    }
+  else
+    {
+      ciphermode = GCRY_CIPHER_MODE_CFB;
+      noncelen = 0;
+    }
 
-  if(slen < 17 || slen > 33)
+  /* Check that the session key has a size of 16 to 32 bytes.  */
+  if ((dek->use_aead && (slen < (noncelen + 16 + 16)
+                         || slen > (noncelen + 32 + 16)))
+      || (!dek->use_aead && (slen < 17 || slen > 33)))
     {
       log_error ( _("weird size for an encrypted session key (%d)\n"),
                  (int)slen);
-      return GPG_ERR_BAD_KEY;
+      return gpg_error (GPG_ERR_BAD_KEY);
     }
 
-  if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
-      BUG ();
-  if (gcry_cipher_setkey ( hd, dek->key, dek->keylen ))
-    BUG ();
-  gcry_cipher_setiv ( hd, NULL, 0 );
-  gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 );
-  gcry_cipher_close ( hd );
-
-  /* Now we replace the dek components with the real session key to
-     decrypt the contents of the sequencing packet. */
-
-  dek->keylen=slen-1;
-  dek->algo=seskey[0];
+  err = openpgp_cipher_open (&hd, dek->algo, ciphermode, GCRY_CIPHER_SECURE);
+  if (!err)
+    err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
+  if (!err)
+    err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen);
+  if (err)
+    goto leave;
 
-  if(dek->keylen > DIM(dek->key))
-    BUG ();
+  if (dek->use_aead)
+    {
+      byte ad[4];
+
+      ad[0] = (0xc0 | PKT_SYMKEY_ENC);
+      ad[1] = 5;
+      ad[2] = dek->algo;
+      ad[3] = dek->use_aead;
+      err = gcry_cipher_authenticate (hd, ad, 4);
+      if (err)
+        goto leave;
+      gcry_cipher_final (hd);
+      keylen = slen - noncelen - 16;
+      err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
+      if (err)
+        goto leave;
+      err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
+      if (err)
+        goto leave;
+      /* Now we replace the dek components with the real session key to
+       * decrypt the contents of the sequencing packet. */
+      if (keylen > DIM(dek->key))
+        {
+          err = gpg_error (GPG_ERR_TOO_LARGE);
+          goto leave;
+        }
+      dek->keylen = keylen;
+      memcpy (dek->key, seskey + noncelen, dek->keylen);
+    }
+  else
+    {
+      gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 );
+      /* Here we can only test whether the algo given in decrypted
+       * session key is a valid OpenPGP algo.  With 11 defined
+       * symmetric algorithms we will miss 4.3% of wrong passphrases
+       * here.  The actual checking is done later during bulk
+       * decryption; we can't bring this check forward easily.  We
+       * need to use the GPG_ERR_CHECKSUM so that we won't run into
+       * the gnupg < 2.2 bug compatible case which would terminate the
+       * process on GPG_ERR_CIPHER_ALGO.  Note that with AEAD (above)
+       * we will have a reliable test here.  */
+      if (openpgp_cipher_test_algo (seskey[0]))
+        {
+          err = gpg_error (GPG_ERR_CHECKSUM);
+          goto leave;
+        }
 
-  memcpy(dek->key, seskey + 1, dek->keylen);
+      /* Now we replace the dek components with the real session key to
+       * decrypt the contents of the sequencing packet. */
+      keylen = slen-1;
+      if (keylen > DIM(dek->key))
+        {
+          err = gpg_error (GPG_ERR_TOO_LARGE);
+          goto leave;
+        }
+      dek->algo = seskey[0];
+      dek->keylen = keylen;
+      memcpy (dek->key, seskey + 1, dek->keylen);
+    }
 
   /*log_hexdump( "thekey", dek->key, dek->keylen );*/
 
-  return 0;
+ leave:
+  gcry_cipher_close (hd);
+  return err;
 }
 
 
 static void
 proc_symkey_enc (CTX c, PACKET *pkt)
 {
+  gpg_error_t err;
   PKT_symkey_enc *enc;
 
   enc = pkt->pkt.symkey_enc;
@@ -294,19 +364,21 @@ proc_symkey_enc (CTX c, PACKET *pkt)
     {
       int algo = enc->cipher_algo;
       const char *s = openpgp_cipher_algo_name (algo);
+      const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo)
+                       /**/           : "CFB");
 
       if (!openpgp_cipher_test_algo (algo))
         {
           if (!opt.quiet)
             {
               if (enc->seskeylen)
-                log_info (_("%s encrypted session key\n"), s );
+                log_info (_("%s.%s encrypted session key\n"), s, a );
               else
-                log_info (_("%s encrypted data\n"), s );
+                log_info (_("%s.%s encrypted data\n"), s, a );
             }
         }
       else
-        log_error (_("encrypted with unknown algorithm %d\n"), algo);
+        log_error (_("encrypted with unknown algorithm %d.%s\n"), algo, a);
 
       if (openpgp_md_test_algo (enc->s2k.hash_algo))
         {
@@ -334,6 +406,7 @@ proc_symkey_enc (CTX c, PACKET *pkt)
           if (c->dek)
             {
               c->dek->symmetric = 1;
+              c->dek->use_aead = enc->aead_algo;
 
               /* FIXME: This doesn't work perfectly if a symmetric key
                  comes before a public key in the message - if the
@@ -344,9 +417,24 @@ proc_symkey_enc (CTX c, PACKET *pkt)
                  come later. */
               if (enc->seskeylen)
                 {
-                  if (symkey_decrypt_seskey (c->dek,
-                                             enc->seskey, enc->seskeylen))
+                  err = symkey_decrypt_seskey (c->dek,
+                                               enc->seskey, enc->seskeylen);
+                  if (err)
                     {
+                      log_info ("decryption of the symmetrically encrypted"
+                                 " session key failed: %s\n",
+                                 gpg_strerror (err));
+                      if (gpg_err_code (err) != GPG_ERR_BAD_KEY
+                          && gpg_err_code (err) != GPG_ERR_CHECKSUM)
+                        log_fatal ("process terminated to be bug compatible"
+                                   " with GnuPG <= 2.2\n");
+                      if (c->dek->s2k_cacheid[0])
+                        {
+                          if (opt.debug)
+                            log_debug ("cleared passphrase cached with ID:"
+                                       " %s\n", c->dek->s2k_cacheid);
+                          passphrase_clear_cache (c->dek->s2k_cacheid);
+                        }
                       xfree (c->dek);
                       c->dek = NULL;
                     }
@@ -650,6 +738,7 @@ proc_encrypted (CTX c, PACKET *pkt)
   else if (!result
            && !opt.ignore_mdc_error
            && !pkt->pkt.encrypted->mdc_method
+           && !pkt->pkt.encrypted->aead_algo
            && openpgp_cipher_get_algo_blklen (c->dek->algo) != 8
            && c->dek->algo != CIPHER_ALGO_TWOFISH)
     {
@@ -664,17 +753,25 @@ proc_encrypted (CTX c, PACKET *pkt)
       write_status (STATUS_DECRYPTION_FAILED);
     }
   else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
+                       && !pkt->pkt.encrypted->aead_algo
                        && opt.ignore_mdc_error))
     {
+      /* All is fine or for an MDC message the MDC failed but the
+       * --ignore-mdc-error option is active.  For compatibility
+       * reasons we issue GOODMDC also for AEAD messages.  */
       write_status (STATUS_DECRYPTION_OKAY);
       if (opt.verbose > 1)
         log_info(_("decryption okay\n"));
-      if (pkt->pkt.encrypted->mdc_method && !result)
+
+      if (pkt->pkt.encrypted->aead_algo)
+        write_status (STATUS_GOODMDC);
+      else if (pkt->pkt.encrypted->mdc_method && !result)
         write_status (STATUS_GOODMDC);
       else if (!opt.no_mdc_warn)
         log_info (_("WARNING: message was not integrity protected\n"));
     }
-  else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE)
+  else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
+           || gpg_err_code (result) == GPG_ERR_TRUNCATED)
     {
       glo_ctrl.lasterr = result;
       log_error (_("WARNING: encrypted message has been manipulated!\n"));
@@ -684,6 +781,7 @@ proc_encrypted (CTX c, PACKET *pkt)
   else
     {
       if ((gpg_err_code (result) == GPG_ERR_BAD_KEY
+          || gpg_err_code (result) == GPG_ERR_CHECKSUM
           || gpg_err_code (result) == GPG_ERR_CIPHER_ALGO)
           && *c->dek->s2k_cacheid != '\0')
         {
@@ -1393,7 +1491,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:    proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_SYMKEY_ENC:    proc_symkey_enc (c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break;
             case PKT_COMPRESSED:    rc = proc_compressed (c, pkt); break;
             default: newpkt = 0; break;
            }
@@ -1409,6 +1508,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:
             case PKT_ENCRYPTED:
             case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD:
               write_status_text( STATUS_UNEXPECTED, "0" );
               rc = GPG_ERR_UNEXPECTED;
               goto leave;
@@ -1436,7 +1536,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_SYMKEY_ENC:  proc_symkey_enc (c, pkt); break;
             case PKT_PUBKEY_ENC:  proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
             case PKT_PLAINTEXT:   proc_plaintext (c, pkt); break;
             case PKT_COMPRESSED:  rc = proc_compressed (c, pkt); break;
             case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
@@ -1463,7 +1564,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
             case PKT_PUBKEY_ENC:  proc_pubkey_enc (ctrl, c, pkt); break;
             case PKT_SYMKEY_ENC:  proc_symkey_enc (c, pkt); break;
             case PKT_ENCRYPTED:
-            case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
+            case PKT_ENCRYPTED_MDC:
+            case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
             case PKT_PLAINTEXT:   proc_plaintext (c, pkt); break;
             case PKT_COMPRESSED:  rc = proc_compressed (c, pkt); break;
             case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;