Imported Upstream version 2.4.3
[platform/upstream/gpg2.git] / sm / sign.c
index 46c71f0..235dac8 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,6 +1,8 @@
 /* sign.c - Sign a message
  * Copyright (C) 2001, 2002, 2003, 2008,
  *               2010 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2012, 2016-2017, 2019,
+ *               2020, 2022-2023 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -16,6 +18,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 #include <config.h>
@@ -32,6 +35,7 @@
 
 #include "keydb.h"
 #include "../common/i18n.h"
+#include "../common/tlv.h"
 
 
 /* Hash the data and return if something was hashed.  Return -1 on error.  */
@@ -300,7 +304,6 @@ add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert)
 }
 
 
-#if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */
 static gpg_error_t
 add_signed_attribute (ksba_cms_t cms, const char *attrstr)
 {
@@ -378,7 +381,12 @@ add_signed_attribute (ksba_cms_t cms, const char *attrstr)
     }
 
   /* Store the data in the CMS object for all signers.  */
+#if 0
   err = ksba_cms_add_attribute (cms, -1, fields[0], 0, der, derlen);
+#else
+  (void)cms;
+  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
   if (err)
     {
       log_error ("invalid attribute specification '%s': %s\n",
@@ -391,7 +399,218 @@ add_signed_attribute (ksba_cms_t cms, const char *attrstr)
   xfree (fields);
   return err;
 }
-#endif /*ksba >= 1.4.0 */
+
+
+\f
+/* This function takes a binary detached signature in (BLOB,BLOBLEN)
+ * and writes it to OUT_FP.  The core of the function is to replace
+ * NDEF length sequences in the input to those with fixed inputs.
+ * This helps certain other implementations to properly verify
+ * detached signature.  Moreover, it allows our own trailing zero
+ * stripping code - which we need for PDF signatures - to work
+ * correctly.
+ *
+ * Example start of a detached signature as created by us:
+ *   0 NDEF: SEQUENCE {      -- 1st sequence
+ *   2    9:   OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2)
+ *  13 NDEF:   [0] {         -- 2nd sequence
+ *  15 NDEF:     SEQUENCE {  -- 3rd sequence
+ *  17    1:       INTEGER 1 -- version
+ *  20   15:       SET {     -- set of algorithms
+ *  22   13:         SEQUENCE {
+ *  24    9:           OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1)
+ *  35    0:           NULL
+ *         :           }
+ *         :         }
+ *  37 NDEF:       SEQUENCE { -- 4th pretty short sequence
+ *  39    9:         OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
+ *         :         }
+ *  52  869:       [0] {
+ * Our goal is to replace the NDEF by fixed length tags.
+ */
+static gpg_error_t
+write_detached_signature (ctrl_t ctrl, const void *blob, size_t bloblen,
+                          estream_t out_fp)
+{
+  gpg_error_t err;
+  const unsigned char *p;
+  size_t n, objlen, hdrlen;
+  int class, tag, cons, ndef;
+  const unsigned char *p_ctoid, *p_version, *p_algoset, *p_dataoid;
+  size_t               n_ctoid,  n_version,  n_algoset,  n_dataoid;
+  const unsigned char *p_certset, *p_signerinfos;
+  size_t               n_certset,  n_signerinfos;
+  int i;
+  ksba_der_t dbld;
+  unsigned char *finalder = NULL;
+  size_t finalderlen;
+
+  (void)ctrl;
+
+  p = blob;
+  n = bloblen;
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 1st sequence.  */
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No signedData OID.  */
+  if (objlen > n)
+    return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+  p_ctoid = p;
+  n_ctoid = objlen;
+  p += objlen;
+  n -= objlen;
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_CONTEXT && tag == 0 && cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 2nd sequence.  */
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 3rd sequence.  */
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_INTEGER))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No version.  */
+  if (objlen > n)
+    return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+  p_version = p;
+  n_version = objlen;
+  p += objlen;
+  n -= objlen;
+
+  p_algoset = p;
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_SET && cons && !ndef))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No set of algorithms.  */
+  if (objlen > n)
+    return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+  n_algoset = hdrlen + objlen;
+  p += objlen;
+  n -= objlen;
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 4th sequence.  */
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !cons))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No data OID.  */
+  if (objlen > n)
+    return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+  p_dataoid = p;
+  n_dataoid = objlen;
+  p += objlen;
+  n -= objlen;
+
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_NONE && !cons && !objlen))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No End tag.  */
+
+  /* certificates [0] IMPLICIT CertificateSet OPTIONAL,
+   * Note: We ignore the following
+   *       crls [1] IMPLICIT CertificateRevocationLists OPTIONAL
+   * because gpgsm does not create them.   */
+  if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen)))
+    return err;
+  if (class == CLASS_CONTEXT && tag == 0 && cons)
+    {
+      if (objlen > n)
+        return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+      p_certset = p;
+      n_certset = objlen;
+      p += objlen;
+      n -= objlen;
+      if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,
+                                 &objlen,&hdrlen)))
+        return err;
+    }
+  else
+    {
+      p_certset = NULL;
+      n_certset = 0;
+    }
+
+  /*  SignerInfos ::= SET OF SignerInfo  */
+  if (!(class == CLASS_UNIVERSAL && tag == TAG_SET && cons && !ndef))
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No set of signerInfos.  */
+  if (objlen > n)
+    return gpg_error (GPG_ERR_BAD_BER);     /* Object larger than data. */
+  p_signerinfos = p;
+  n_signerinfos = objlen;
+  p += objlen;
+  n -= objlen;
+
+  /* For the fun of it check the 3 end tags.  */
+  for (i=0; i < 3; i++)
+    {
+      if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,
+                                 &objlen,&hdrlen)))
+        return err;
+      if (!(class == CLASS_UNIVERSAL && tag == TAG_NONE && !cons && !objlen))
+        return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No End tag.  */
+    }
+  if (n)
+    return gpg_error (GPG_ERR_INV_CMS_OBJ); /* Garbage */
+
+  /*---- From here on we jump to leave on error.  ----*/
+
+  /* Now create a new object from the collected data.  */
+  dbld = ksba_der_builder_new (16);  /* (pre-allocate 16 items)  */
+  if (!dbld)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  ksba_der_add_tag (dbld, 0, KSBA_TYPE_SEQUENCE);
+  ksba_der_add_val ( dbld, 0, KSBA_TYPE_OBJECT_ID, p_ctoid, n_ctoid);
+  ksba_der_add_tag ( dbld, KSBA_CLASS_CONTEXT, 0);
+  ksba_der_add_tag (  dbld, 0, KSBA_TYPE_SEQUENCE);
+  ksba_der_add_val (   dbld, 0, KSBA_TYPE_INTEGER, p_version, n_version);
+  ksba_der_add_der (   dbld, p_algoset, n_algoset);
+  ksba_der_add_tag (   dbld, 0, KSBA_TYPE_SEQUENCE);
+  ksba_der_add_val (    dbld, 0, KSBA_TYPE_OBJECT_ID, p_dataoid, n_dataoid);
+  ksba_der_add_end (   dbld);
+  if (p_certset)
+    {
+      ksba_der_add_tag (   dbld, KSBA_CLASS_CONTEXT, 0);
+      ksba_der_add_der (    dbld, p_certset, n_certset);
+      ksba_der_add_end (   dbld);
+    }
+  ksba_der_add_tag (   dbld, 0, KSBA_TYPE_SET);
+  ksba_der_add_der (    dbld, p_signerinfos, n_signerinfos);
+  ksba_der_add_end (   dbld);
+  ksba_der_add_end (  dbld);
+  ksba_der_add_end ( dbld);
+  ksba_der_add_end (dbld);
+
+  err = ksba_der_builder_get (dbld, &finalder, &finalderlen);
+  if (err)
+    goto leave;
+
+  if (es_fwrite (finalder, finalderlen, 1, out_fp) != 1)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+
+ leave:
+  ksba_der_release (dbld);
+  ksba_free (finalder);
+  return err;
+}
 
 
 \f
@@ -409,6 +628,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
   gpg_error_t err;
   gnupg_ksba_io_t b64writer = NULL;
   ksba_writer_t writer;
+  estream_t sig_fp = NULL;  /* Used for detached signatures.  */
   ksba_cms_t cms = NULL;
   ksba_stop_reason_t stopreason;
   KEYDB_HANDLE kh = NULL;
@@ -419,6 +639,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
   ksba_isotime_t signed_at;
   certlist_t cl;
   int release_signerlist = 0;
+  int binary_detached = detached && !ctrl->create_pem && !ctrl->create_base64;
 
   audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
 
@@ -441,17 +662,35 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       goto leave;
     }
 
+  /* Note that in detached mode the b64 write is actually a binary
+   * writer because we need to fixup the created signature later.
+   * Note that we do this only for binary output because we have no
+   * PEM writer interface outside of the ksba create writer code.  */
   ctrl->pem_name = "SIGNED MESSAGE";
-  rc = gnupg_ksba_create_writer
-    (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
-                  | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
-     ctrl->pem_name, out_fp, &writer);
+  if (binary_detached)
+    {
+      sig_fp = es_fopenmem (0, "w+");
+      rc = sig_fp? 0 : gpg_error_from_syserror ();
+      if (!rc)
+        rc = gnupg_ksba_create_writer (&b64writer, 0, NULL, sig_fp, &writer);
+    }
+  else
+    {
+      rc = gnupg_ksba_create_writer
+        (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
+                      | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
+         ctrl->pem_name, out_fp, &writer);
+    }
   if (rc)
     {
       log_error ("can't create writer: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
+  gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+  if (ctrl->input_size_hint)
+    gnupg_ksba_set_total (b64writer, ctrl->input_size_hint);
+
   err = ksba_cms_new (&cms);
   if (err)
     {
@@ -474,9 +713,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
   if (!err)
     err = ksba_cms_set_content_type
       (cms, 1,
-#if KSBA_VERSION_NUMBER >= 0x010400 && 0
        opt.authenticode? KSBA_CT_SPC_IND_DATA_CTX :
-#endif
        KSBA_CT_DATA
        );
   if (err)
@@ -504,7 +741,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
          check that the signer's certificate is usable and valid.  */
       rc = gpgsm_cert_use_sign_p (cert, 0);
       if (!rc)
-        rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
+        rc = gpgsm_validate_chain (ctrl, cert,
+                                   GNUPG_ISOTIME_NONE, NULL, 0, NULL, 0, NULL);
       if (rc)
         {
           char *tmpfpr;
@@ -757,8 +995,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
         }
     }
 
-  /* We can add signed attributes only when build against libksba 1.4.  */
-#if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */
   {
     strlist_t sl;
 
@@ -766,20 +1002,19 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       if ((err = add_signed_attribute (cms, sl->d)))
         goto leave;
   }
-#else
-  if (opt.attributes)
-    log_info ("Note: option --attribute is ignored by this version\n");
-#endif /*ksba >= 1.4.0  */
 
 
   /* We need to write at least a minimal list of our capabilities to
-     try to convince some MUAs to use 3DES and not the crippled
-     RC2. Our list is:
-
-        aes128-CBC
-        des-EDE3-CBC
-  */
-  err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0);
+   * try to convince some MUAs to use 3DES and not the crippled
+   * RC2. Our list is:
+   *
+   *   aes256-CBC
+   *   aes128-CBC
+   *   des-EDE3-CBC
+   */
+  err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.42", NULL,0);
+  if (!err)
+    err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL,0);
   if (!err)
     err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0);
   if (err)
@@ -796,7 +1031,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       err = ksba_cms_build (cms, &stopreason);
       if (err)
         {
-          log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+          log_error ("creating CMS object failed: %s\n", gpg_strerror (err));
           rc = err;
           goto leave;
         }
@@ -945,6 +1180,22 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       goto leave;
     }
 
+  if (binary_detached)
+    {
+      void *blob = NULL;
+      size_t bloblen;
+
+      rc = es_fclose_snatch (sig_fp, &blob, &bloblen);
+      sig_fp = NULL;
+      if (rc)
+        goto leave;
+      rc = write_detached_signature (ctrl, blob, bloblen, out_fp);
+      xfree (blob);
+      if (rc)
+        goto leave;
+    }
+
+
   audit_log (ctrl->audit, AUDIT_SIGNING_DONE);
   log_info ("signature created\n");
 
@@ -958,5 +1209,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
   gnupg_ksba_destroy_writer (b64writer);
   keydb_release (kh);
   gcry_md_close (data_md);
+  es_fclose (sig_fp);
   return rc;
 }