agent: Improve error handling.
[gnupg.git] / agent / pksign.c
index d737bad..7b498d4 100644 (file)
@@ -281,7 +281,7 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits,
    passphrase.  If OVERRIDEDATA is not NULL, OVERRIDEDATALEN bytes
    from this buffer are used instead of the data in CTRL.  The
    override feature is required to allow the use of Ed25519 with ssh
-   because Ed25519 dies the hashing itself.  */
+   because Ed25519 does the hashing itself.  */
 int
 agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                  const char *desc_text,
@@ -290,10 +290,13 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                  const void *overridedata, size_t overridedatalen)
 {
   gcry_sexp_t s_skey = NULL, s_sig = NULL;
+  gcry_sexp_t s_hash = NULL;
+  gcry_sexp_t s_pkey = NULL;
   unsigned char *shadow_info = NULL;
   unsigned int rc = 0;         /* FIXME: gpg-error? */
   const unsigned char *data;
   int datalen;
+  int check_signature = 0;
 
   if (overridedata)
     {
@@ -329,6 +332,13 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
       int is_ECDSA = 0;
       int is_EdDSA = 0;
 
+      rc = agent_public_key_from_file (ctrl, ctrl->keygrip, &s_pkey);
+      if (rc)
+        {
+          log_error ("failed to read the public key\n");
+          goto leave;
+        }
+
       if (agent_is_eddsa_key (s_skey))
         is_EdDSA = 1;
       else
@@ -352,6 +362,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
       if (is_RSA)
         {
+          check_signature = 1;
           if (*buf & 0x80)
             {
               len++;
@@ -363,12 +374,13 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
               *buf = 0;
             }
 
-          rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))", len, buf);
+          rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))",
+                                (int)len, buf);
         }
       else if (is_EdDSA)
         {
           rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))",
-                                len/2, buf, len/2, buf + len/2);
+                                (int)len/2, buf, (int)len/2, buf + len/2);
         }
       else if (is_ECDSA)
         {
@@ -430,8 +442,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
   else
     {
       /* No smartcard, but a private key */
-      gcry_sexp_t s_hash = NULL;
-      int dsaalgo;
+      int dsaalgo = 0;
 
       /* Put the hash into a sexp */
       if (agent_is_eddsa_key (s_skey))
@@ -453,6 +464,10 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
       if (rc)
         goto leave;
 
+      if (dsaalgo == 0 && GCRYPT_VERSION_NUMBER < 0x010700)
+        /* It's RSA and Libgcrypt < 1.7 */
+        check_signature = 1;
+
       if (DBG_CRYPTO)
         {
           gcry_log_debugsxp ("skey", s_skey);
@@ -461,7 +476,6 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
       /* sign */
       rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
-      gcry_sexp_release (s_hash);
       if (rc)
         {
           log_error ("signing failed: %s\n", gpg_strerror (rc));
@@ -472,11 +486,44 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         gcry_log_debugsxp ("rslt", s_sig);
     }
 
+  /* Check that the signature verification worked and nothing is
+   * fooling us e.g. by a bug in the signature create code or by
+   * deliberately introduced faults.  Because Libgcrypt 1.7 does this
+   * for RSA internally there is no need to do it here again.  */
+  if (check_signature)
+    {
+      if (s_hash == NULL)
+        {
+          if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
+            rc = do_encode_raw_pkcs1 (data, datalen,
+                                      gcry_pk_get_nbits (s_skey),
+                                      &s_hash);
+          else
+            rc = do_encode_md (data, datalen,
+                               ctrl->digest.algo,
+                               &s_hash,
+                               ctrl->digest.raw_value);
+        }
+
+      if (! rc)
+        rc = gcry_pk_verify (s_sig, s_hash, s_pkey? s_pkey: s_skey);
+
+      if (rc)
+        {
+          log_error (_("checking created signature failed: %s\n"),
+                     gpg_strerror (rc));
+          gcry_sexp_release (s_sig);
+          s_sig = NULL;
+        }
+    }
+
  leave:
 
   *signature_sexp = s_sig;
 
+  gcry_sexp_release (s_pkey);
   gcry_sexp_release (s_skey);
+  gcry_sexp_release (s_hash);
   xfree (shadow_info);
 
   return rc;