* protect-tool.c: New option --enable-status-msg.
[gnupg.git] / sm / decrypt.c
index 7237fef..9a21cfb 100644 (file)
@@ -1,5 +1,5 @@
 /* decrypt.c - Decrypt a message
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <time.h>
 #include <assert.h>
 
+#include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
-#include "gpgsm.h"
 #include "keydb.h"
 #include "i18n.h"
 
@@ -38,7 +38,7 @@ struct decrypt_filter_parm_s {
   int algo;
   int mode;
   int blklen;
-  GCRY_CIPHER_HD hd;
+  gcry_cipher_hd_t hd;
   char iv[16];
   size_t ivlen;
   int any_data;  /* dod we push anything through the filter at all? */
@@ -50,43 +50,23 @@ struct decrypt_filter_parm_s {
 };
 
 
-static void
-print_integer_sexp (unsigned char *p)
-{
-  unsigned long len;
-
-  if (!p)
-    log_printf ("none");
-  else
-    {
-      len = gcry_sexp_canon_len (p, 0, NULL, NULL);
-      if (!len)
-        log_printf ("invalid encoding");
-      else
-        {
-          for (; len && *p != ':'; len--, p++)
-            ;
-          for (p++; len; len--, p++)
-            log_printf ("%02X", *p);
-        }
-    }
-}
 
-/* decrypt the session key and fill in the parm structure.  The
+/* Decrypt the session key and fill in the parm structure.  The
    algo and the IV is expected to be already in PARM. */
 static int 
-prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
+prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
+                    ksba_const_sexp_t enc_val,
                     struct decrypt_filter_parm_s *parm)
 {
   char *seskey = NULL;
   size_t n, seskeylen;
   int rc;
 
-  rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val,
+  rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
                               &seskey, &seskeylen);
   if (rc)
     {
-      log_error ("error decrypting session key: %s\n", gnupg_strerror (rc));
+      log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
@@ -94,41 +74,55 @@ prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
     log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
 
   n=0;
-  if (n + 7 > seskeylen )
+  if (seskeylen == 24)
     {
-      rc = seterr (Invalid_Session_Key);
-      goto leave; 
+      /* Smells like a 3-des key.  This might happen because a SC has
+         already done the unpacking. fixme! */
     }
-
-  if (seskey[n] != 2 )  /* wrong block type version */
-    { 
-      rc = seterr (Invalid_Session_Key);
-      goto leave; 
+  else
+    {
+      if (n + 7 > seskeylen )
+        {
+          rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+          goto leave; 
+        }
+      
+      /* FIXME: Actually the leading zero is required but due to the way
+         we encode the output in libgcrypt as an MPI we are not able to
+         encode that leading zero.  However, when using a Smartcard we are
+         doing it the rightway and therefore we have to skip the zero.  This
+         should be fixed in gpg-agent of course. */
+      if (!seskey[n])
+        n++;
+      
+      if (seskey[n] != 2 )  /* wrong block type version */
+        { 
+          rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+          goto leave; 
+        }
+      
+      for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
+        ;
+      n++; /* and the zero byte */
+      if (n >= seskeylen )
+        { 
+          rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+          goto leave; 
+        }
     }
 
-  for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
-    ;
-  n++; /* and the zero byte */
-  if (n >= seskeylen )
-    { 
-      rc = seterr (Invalid_Session_Key);
-      goto leave; 
-    }
-  
   if (DBG_CRYPTO)
     log_printhex ("session key:", seskey+n, seskeylen-n);
 
-  parm->hd = gcry_cipher_open (parm->algo, parm->mode, 0);
-  if (!parm->hd)
+  rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
+  if (rc)
     {
-      rc = gcry_errno ();
-      log_error ("error creating decryptor: %s\n", gcry_strerror (rc));
-      rc = map_gcry_err (rc);
+      log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
       goto leave;
     }
                         
   rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
-  if (rc == GCRYERR_WEAK_KEY)
+  if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
     {
       log_info (_("WARNING: message was encrypted with "
                   "a weak key in the symmetric cipher.\n"));
@@ -136,8 +130,7 @@ prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
     }
   if (rc)
     {
-      log_error("key setup failed: %s\n", gcry_strerror(rc) );
-      rc = map_gcry_err (rc);
+      log_error("key setup failed: %s\n", gpg_strerror(rc) );
       goto leave;
     }
 
@@ -156,7 +149,7 @@ prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
    Due to different buffer sizes or different length of input and
    output, it may happen that fewer bytes are process or fewer bytes
    are written. */
-static KsbaError  
+static gpg_error_t
 decrypt_filter (void *arg,
                 const void *inbuf, size_t inlen, size_t *inused,
                 void *outbuf, size_t maxoutlen, size_t *outlen)
@@ -167,10 +160,10 @@ decrypt_filter (void *arg,
 
   /* fixme: Should we issue an error when we have not seen one full block? */
   if (!inlen)
-    return KSBA_Bug;
+    return gpg_error (GPG_ERR_BUG);
 
   if (maxoutlen < 2*parm->blklen)
-    return KSBA_Bug;
+    return gpg_error (GPG_ERR_BUG);
   /* make some space becuase we will later need an extra block at the end */
   maxoutlen -= blklen;
 
@@ -182,7 +175,7 @@ decrypt_filter (void *arg,
         parm->helpblock[i] = ((const char*)inbuf)[j];
       inlen -= j;
       if (blklen > maxoutlen)
-        return KSBA_Bug;
+        return gpg_error (GPG_ERR_BUG);
       if (i < blklen)
         {
           parm->helpblocklen = i;
@@ -245,16 +238,15 @@ decrypt_filter (void *arg,
 \f
 /* Perform a decrypt operation.  */
 int
-gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
+gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp)
 {
   int rc;
-  KsbaError err;
   Base64Context b64reader = NULL;
   Base64Context b64writer = NULL;
-  KsbaReader reader;
-  KsbaWriter writer;
-  KsbaCMS cms = NULL;
-  KsbaStopReason stopreason;
+  ksba_reader_t reader;
+  ksba_writer_t writer;
+  ksba_cms_t cms = NULL;
+  ksba_stop_reason_t stopreason;
   KEYDB_HANDLE kh;
   int recp;
   FILE *in_fp = NULL;
@@ -266,7 +258,7 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
   if (!kh)
     {
       log_error (_("failed to allocated keyDB handle\n"));
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 
@@ -274,49 +266,44 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
   in_fp = fdopen ( dup (in_fd), "rb");
   if (!in_fp)
     {
+      rc = gpg_error (gpg_err_code_from_errno (errno));
       log_error ("fdopen() failed: %s\n", strerror (errno));
-      rc = seterr (IO_Error);
       goto leave;
     }
 
-  rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &reader);
+  rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, 0, &reader);
   if (rc)
     {
-      log_error ("can't create reader: %s\n", gnupg_strerror (rc));
+      log_error ("can't create reader: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
   rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
   if (rc)
     {
-      log_error ("can't create writer: %s\n", gnupg_strerror (rc));
+      log_error ("can't create writer: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
-  cms = ksba_cms_new ();
-  if (!cms)
-    {
-      rc = seterr (Out_Of_Core);
-      goto leave;
-    }
+  rc = ksba_cms_new (&cms);
+  if (rc)
+    goto leave;
 
-  err = ksba_cms_set_reader_writer (cms, reader, writer);
-  if (err)
+  rc = ksba_cms_set_reader_writer (cms, reader, writer);
+  if (rc)
     {
       log_debug ("ksba_cms_set_reader_writer failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
+                 gpg_strerror (rc));
       goto leave;
     }
 
   /* parser loop */
   do 
     {
-      err = ksba_cms_parse (cms, &stopreason);
-      if (err)
+      rc = ksba_cms_parse (cms, &stopreason);
+      if (rc)
         {
-          log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
-          rc = map_ksba_err (err);
+          log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
           goto leave;
         }
 
@@ -325,21 +312,39 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
         {
           int algo, mode;
           const char *algoid;
+          int any_key = 0;
           
           algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
           algo = gcry_cipher_map_name (algoid);
           mode = gcry_cipher_mode_from_oid (algoid);
           if (!algo || !mode)
             {
+              rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
               log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
-              rc = GNUPG_Unsupported_Algorithm;
+              if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
+                log_info (_("(this is the RC2 algorithm)\n"));
+              else if (!algoid)
+                log_info (_("(this does not seem to be an encrypted"
+                            " message)\n"));
+              {
+                char numbuf[50];
+                sprintf (numbuf, "%d", rc);
+                gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
+                               numbuf, algoid?algoid:"?", NULL);
+              }
+
+              /* If it seems that this is not an ecrypted message we
+                 return a more sensible error code. */
+              if (!algoid)
+                rc = gpg_error (GPG_ERR_NO_DATA);
+
               goto leave;
             }
           dfparm.algo = algo;
           dfparm.mode = mode;
           dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
           if (dfparm.blklen > sizeof (dfparm.helpblock))
-            return GNUPG_Bug;
+            return gpg_error (GPG_ERR_BUG);
 
           rc = ksba_cms_get_content_enc_iv (cms,
                                             dfparm.iv,
@@ -347,48 +352,64 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
                                             &dfparm.ivlen);
           if (rc)
             {
-              log_error ("error getting IV: %s\n", ksba_strerror (err));
-              rc = map_ksba_err (err);
+              log_error ("error getting IV: %s\n", gpg_strerror (rc));
               goto leave;
             }
           
-          for (recp=0; recp < 1; recp++)
+          for (recp=0; !any_key; recp++)
             {
               char *issuer;
-              KsbaSexp serial;
-              KsbaSexp enc_val;
+              ksba_sexp_t serial;
+              ksba_sexp_t enc_val;
               char *hexkeygrip = NULL;
+              char *desc = NULL;
 
-              err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
-              if (err)
+              rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
+              if (rc == -1 && recp)
+                break; /* no more recipients */
+              if (rc)
                 log_error ("recp %d - error getting info: %s\n",
-                           recp, ksba_strerror (err));
+                           recp, gpg_strerror (rc));
               else
                 {
-                  KsbaCert cert = NULL;
+                  ksba_cert_t cert = NULL;
 
                   log_debug ("recp %d - issuer: `%s'\n",
                              recp, issuer? issuer:"[NONE]");
                   log_debug ("recp %d - serial: ", recp);
-                  print_integer_sexp (serial);
+                  gpgsm_dump_serial (serial);
                   log_printf ("\n");
 
                   keydb_search_reset (kh);
                   rc = keydb_search_issuer_sn (kh, issuer, serial);
                   if (rc)
                     {
-                      log_debug ("failed to find the certificate: %s\n",
-                                 gnupg_strerror(rc));
+                      log_error ("failed to find the certificate: %s\n",
+                                 gpg_strerror(rc));
                       goto oops;
                     }
 
                   rc = keydb_get_cert (kh, &cert);
                   if (rc)
                     {
-                      log_debug ("failed to get cert: %s\n", gnupg_strerror (rc));
-                      goto oops;                    }
+                      log_error ("failed to get cert: %s\n", gpg_strerror (rc));
+                      goto oops;     
+                    }
+                  /* Just in case there is a problem with the own
+                     certificate we print this message - should never
+                     happen of course */
+                  rc = gpgsm_cert_use_decrypt_p (cert);
+                  if (rc)
+                    {
+                      char numbuf[50];
+                      sprintf (numbuf, "%d", rc);
+                      gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
+                                     numbuf, NULL);
+                      rc = 0;
+                    }
 
                   hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
+                  desc = gpgsm_format_keydesc (cert);
 
                 oops:
                   xfree (issuer);
@@ -396,25 +417,36 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
                   ksba_cert_release (cert);
                 }
 
-              enc_val = ksba_cms_get_enc_val (cms, recp);
-              if (!enc_val)
+              if (!hexkeygrip)
+                ;
+              else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
                 log_error ("recp %d - error getting encrypted session key\n",
                            recp);
               else
                 {
-                  rc = prepare_decryption (hexkeygrip, enc_val,
-                                           &dfparm);
+                  rc = prepare_decryption (ctrl,
+                                           hexkeygrip, desc, enc_val, &dfparm);
                   xfree (enc_val);
                   if (rc)
-                    log_error ("decrypting session key failed: %s\n",
-                               gnupg_strerror (rc));
+                    {
+                      log_info ("decrypting session key failed: %s\n",
+                                gpg_strerror (rc));
+                    }
                   else
                     { /* setup the bulk decrypter */
+                      any_key = 1;
                       ksba_writer_set_filter (writer,
                                               decrypt_filter,
                                               &dfparm);
                     }
                 }
+              xfree (hexkeygrip);
+              xfree (desc);
+            }
+          if (!any_key)
+            {
+              rc = gpg_error (GPG_ERR_NO_SECKEY);
+              goto leave;
             }
         }
       else if (stopreason == KSBA_SR_END_DATA)
@@ -426,23 +458,21 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
               if (!npadding || npadding > dfparm.blklen)
                 {
                   log_error ("invalid padding with value %d\n", npadding);
-                  rc = seterr (Invalid_Data);
+                  rc = gpg_error (GPG_ERR_INV_DATA);
                   goto leave;
                 }
               rc = ksba_writer_write (writer,
                                       dfparm.lastblock, 
                                       dfparm.blklen - npadding);
               if (rc)
-                {
-                  rc = map_ksba_err (rc);
                   goto leave;
-                }
+
               for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
                 {
                   if (dfparm.lastblock[i] != npadding)
                     {
                       log_error ("inconsistent padding\n");
-                      rc = seterr (Invalid_Data);
+                      rc = gpg_error (GPG_ERR_INV_DATA);
                       goto leave;
                     }
                 }
@@ -455,7 +485,7 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
   rc = gpgsm_finish_writer (b64writer);
   if (rc) 
     {
-      log_error ("write failed: %s\n", gnupg_strerror (rc));
+      log_error ("write failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
   gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
@@ -463,7 +493,10 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
 
  leave:
   if (rc)
-    gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
+    {
+      gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
+      log_error ("message decryption failed: %s\n", gpg_strerror (rc));
+    }
   ksba_cms_release (cms);
   gpgsm_destroy_reader (b64reader);
   gpgsm_destroy_writer (b64writer);