[git] GnuPG - branch, master, updated. gnupg-2.1.11-130-g4159567

by Werner Koch cvs at cvs.gnupg.org
Tue Apr 12 14:40:15 CEST 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  4159567f7ed7a1139fdc3a6c92988e1648ad84ab (commit)
       via  100b413d7f69f6f091fd98185df4d579ce6ce0ce (commit)
       via  7faf131c8b8710419df3dc13a1228d1977c55f53 (commit)
       via  22b869adca877ff0826707109e7f7b391eb44441 (commit)
      from  02cf1357dd5ee34a57371f55b9d312b8b9e3a7e8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 4159567f7ed7a1139fdc3a6c92988e1648ad84ab
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Apr 12 14:37:26 2016 +0200

    agent: Implement new protection mode openpgp-s2k3-ocb-aes.
    
    * agent/protect.c (agent_protect): Add arg use_ocb.  Change all caller
    to pass -1 for default.
    * agent/protect-tool.c: New option --debug-use-ocb.
    (oDebugUseOCB): New.
    (opt_debug_use_ocb): New.
    (main): Set option.
    (read_and_protect): Implement option.
    
    * agent/protect.c (OCB_MODE_SUPPORTED): New macro.
    (PROT_DEFAULT_TO_OCB): New macro.
    (do_encryption): Add args use_ocb, hashbegin, hashlen, timestamp_exp,
    and timestamp_exp_len.  Implement OCB.
    (agent_protect): Change to support OCB.
    (do_decryption): Add new args is_ocb, aadhole_begin, and aadhole_len.
    Implement OCB.
    (merge_lists): Allow NULL for sha1hash.
    (agent_unprotect): Change to support OCB.
    (agent_private_key_type): Remove debug output.
    --
    
    Instead of using the old OpenPGP way of appending a hash of the
    plaintext and encrypt that along with the plaintext, the new scheme
    uses a proper authenticated encryption mode.  See keyformat.txt for a
    description.  Libgcrypt 1.7 is required.
    
    This mode is not yet enabled because there would be no way to return
    to an older GnuPG version.  To test the new scheme use
    gpg-protect-tool:
    
     ./gpg-protect-tool -av -P abc -p --debug-use-ocb <plain.key >prot.key
     ./gpg-protect-tool -av -P abc -u <prot.key
    
    Any key from the private key storage should work.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/agent.h b/agent/agent.h
index c2726bb..0dcb201 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -471,7 +471,7 @@ unsigned long get_standard_s2k_count (void);
 unsigned char get_standard_s2k_count_rfc4880 (void);
 int agent_protect (const unsigned char *plainkey, const char *passphrase,
                    unsigned char **result, size_t *resultlen,
-		   unsigned long s2k_count);
+		   unsigned long s2k_count, int use_ocb);
 int agent_unprotect (ctrl_t ctrl,
                      const unsigned char *protectedkey, const char *passphrase,
                      gnupg_isotime_t protected_at,
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 6e809f6..0e1d9fc 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -3125,7 +3125,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
   gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
   /* FIXME: guarantee?  */
 
-  err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0);
+  err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1);
 
  out:
 
diff --git a/agent/command.c b/agent/command.c
index dfe292d..c94fdd3 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -2140,7 +2140,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
   if (passphrase)
     {
       err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
-                           ctrl->s2k_count);
+                           ctrl->s2k_count, -1);
       if (!err)
         err = agent_write_private_key (grip, finalkey, finalkeylen, force);
     }
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index 8df6b8e..40d9a3e 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1066,7 +1066,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
 
           if (!agent_protect (*r_key, passphrase,
                               &protectedkey, &protectedkeylen,
-                              ctrl->s2k_count))
+                              ctrl->s2k_count, -1))
             agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
           xfree (protectedkey);
         }
diff --git a/agent/genkey.c b/agent/genkey.c
index 2eec974..12c3e34 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -58,7 +58,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
     {
       unsigned char *p;
 
-      rc = agent_protect (buf, passphrase, &p, &len, s2k_count);
+      rc = agent_protect (buf, passphrase, &p, &len, s2k_count, -1);
       if (rc)
         {
           xfree (buf);
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 150ea7c..e760412 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -43,23 +43,6 @@ optional but required for some operations to calculate the fingerprint
 of the key.  This timestamp should be a string with the number of
 seconds since Epoch or an ISO time string (yyyymmddThhmmss).
 
-Actually this form should not be used for regular purposes and only
-accepted by gpg-agent with the configuration option:
---allow-non-canonical-key-format.  The regular way to represent the
-keys is in canonical representation[3]:
-
-(private-key
-   (rsa
-    (n #00e0ce9..[some bytes not shown]..51#)
-    (e #010001#)
-    (d #046129F..[some bytes not shown]..81#)
-    (p #00e861b..[some bytes not shown]..f1#)
-    (q #00f7a7c..[some bytes not shown]..61#)
-    (u #304559a..[some bytes not shown]..9b#)
-   )
-   (uri http://foo.bar x-foo:whatever_you_want)
-)
-
 
 Protected Private Key Format
 ==============================
@@ -90,7 +73,7 @@ The currently defined protection modes are:
 
   This describes an algorithm using using AES in CBC mode for
   encryption, SHA-1 for integrity protection and the String to Key
-  algorithm 3 from OpenPGP (rfc2440).
+  algorithm 3 from OpenPGP (rfc4880).
 
   Example:
 
@@ -116,7 +99,7 @@ The currently defined protection modes are:
   easily be stripped by looking for the end of the list.
 
   The hash is calculated on the concatenation of the public key and
-  secret key parameter lists: i.e it is required to hash the
+  secret key parameter lists: i.e. it is required to hash the
   concatenation of these 6 canonical encoded lists for RSA, including
   the parenthesis, the algorithm keyword and (if used) the protected-at
   list.
@@ -135,7 +118,46 @@ The currently defined protection modes are:
   the stored one - If they don't match the integrity of the key is not
   given.
 
-2. openpgp-native
+2. openpgp-s2k3-ocb-aes
+
+  This describes an algorithm using using AES-128 in OCB mode, a nonce
+  of 96 bit, a taglen of 128 bit, and the String to Key algorithm 3
+  from OpenPGP (rfc4880).
+
+  Example:
+
+  (protected openpgp-s2k3-ocb-aes
+    ((sha1 16byte_salt no_of_iterations) 12byte_nonce)
+    encrypted_octet_string
+  )
+
+  The encrypted_octet string should yield this S-Exp (in canonical
+  representation) after decryption:
+
+  (
+   (
+    (d #046129F..[some bytes not shown]..81#)
+    (p #00e861b..[some bytes not shown]..f1#)
+    (q #00f7a7c..[some bytes not shown]..61#)
+    (u #304559a..[some bytes not shown]..9b#)
+   )
+  )
+
+  For padding reasons, random bytes may be appended to this list -
+  they can easily be stripped by looking for the end of the list.
+
+  The associated data required for this protection mode is the list
+  formiing the public key parameters.  For the above example this is
+  is this canonical encoded S-expression:
+
+  (rsa
+   (n #00e0ce9..[some bytes not shown]..51#)
+   (e #010001#)
+   (protected-at "18950523T000000")
+  )
+
+
+3. openpgp-native
 
   This is a wrapper around the OpenPGP Private Key Transport format
   which resembles the standard OpenPGP format and allows the use of an
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index 03dbda9..1871ac7 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -69,6 +69,7 @@ enum cmd_and_opt_values
   oHomedir,
   oPrompt,
   oStatusMsg,
+  oDebugUseOCB,
 
   oAgentProgram
 };
@@ -96,6 +97,7 @@ static const char *opt_passphrase;
 static char *opt_prompt;
 static int opt_status_msg;
 static const char *opt_agent_program;
+static int opt_debug_use_ocb;
 
 static char *get_passphrase (int promptno);
 static void release_passphrase (char *pw);
@@ -132,6 +134,8 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
 
+  ARGPARSE_s_n (oDebugUseOCB,  "debug-use-ocb", "@"), /* For hacking only.  */
+
   ARGPARSE_end ()
 };
 
@@ -333,7 +337,8 @@ read_and_protect (const char *fname)
     return;
 
   pw = get_passphrase (1);
-  rc = agent_protect (key, pw, &result, &resultlen, 0);
+  rc = agent_protect (key, pw, &result, &resultlen, 0,
+                      opt_debug_use_ocb? 1 : -1);
   release_passphrase (pw);
   xfree (key);
   if (rc)
@@ -598,6 +603,7 @@ main (int argc, char **argv )
         case oHaveCert: opt_have_cert = 1; break;
         case oPrompt: opt_prompt = pargs.r.ret_str; break;
         case oStatusMsg: opt_status_msg = 1; break;
+        case oDebugUseOCB: opt_debug_use_ocb = 1; break;
 
         default: pargs.err = ARGPARSE_PRINT_ERROR; break;
 	}
diff --git a/agent/protect.c b/agent/protect.c
index f037703..a78d5a5 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -41,6 +41,18 @@
 #include "cvt-openpgp.h"
 #include "sexp-parse.h"
 
+
+#if GCRYPT_VERSION_NUMBER < 0x010700
+# define OCB_MODE_SUPPORTED 0
+#else
+# define OCB_MODE_SUPPORTED 1
+#endif
+
+/* To use the openpgp-s2k3-ocb-aes scheme by default set the value of
+ * this macro to 1.  Note that the caller of agent_protect may
+ * override this default.  */
+#define PROT_DEFAULT_TO_OCB 0
+
 /* The protection mode for encryption.  The supported modes for
    decryption are listed in agent_unprotect().  */
 #define PROT_CIPHER        GCRY_CIPHER_AES128
@@ -87,6 +99,7 @@ hash_passphrase (const char *passphrase, int hashalgo,
                  unsigned char *key, size_t keylen);
 
 
+
 

 /* Get the process time and store it in DATA.  */
 static void
@@ -302,70 +315,108 @@ calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
    encrypted block in RESULT or return with an error code.  SHA1HASH
    is the 20 byte SHA-1 hash required for the integrity code.
 
-   The parameter block is expected to be an incomplete S-Expression of
-   the form (example in advanced format):
+   The parameter block is expected to be an incomplete canonical
+   encoded S-Expression of the form (example in advanced format):
 
      (d #046129F..[some bytes not shown]..81#)
      (p #00e861b..[some bytes not shown]..f1#)
      (q #00f7a7c..[some bytes not shown]..61#)
      (u #304559a..[some bytes not shown]..9b#)
 
-   the returned block is the S-Expression:
+     the returned block is the S-Expression:
 
-    (protected mode (parms) encrypted_octet_string)
+     (protected mode (parms) encrypted_octet_string)
 
 */
 static int
-do_encryption (const unsigned char *protbegin, size_t protlen,
-               const char *passphrase,  const unsigned char *sha1hash,
+do_encryption (const unsigned char *hashbegin, size_t hashlen,
+               const unsigned char *protbegin, size_t protlen,
+               const char *passphrase,
+               const char *timestamp_exp, size_t timestamp_exp_len,
                unsigned char **result, size_t *resultlen,
-	       unsigned long s2k_count)
+	       unsigned long s2k_count, int use_ocb)
 {
   gcry_cipher_hd_t hd;
-  const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
+  const char *modestr;
+  unsigned char hashvalue[20];
   int blklen, enclen, outlen;
   unsigned char *iv = NULL;
+  unsigned int ivsize;  /* Size of the buffer allocated for IV.  */
+  const unsigned char *s2ksalt; /* Points into IV.  */
   int rc;
   char *outbuf = NULL;
   char *p;
   int saltpos, ivpos, encpos;
 
+  s2ksalt = iv;  /* Silence compiler warning.  */
+
   *resultlen = 0;
   *result = NULL;
 
-  rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
+  if (use_ocb && !OCB_MODE_SUPPORTED)
+    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+
+  modestr = (use_ocb? "openpgp-s2k3-ocb-aes"
+             /*   */: "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc");
+
+  rc = gcry_cipher_open (&hd, PROT_CIPHER,
+#if OCB_MODE_SUPPORTED
+                         use_ocb? GCRY_CIPHER_MODE_OCB :
+#endif
+                         GCRY_CIPHER_MODE_CBC,
                          GCRY_CIPHER_SECURE);
   if (rc)
     return rc;
 
-
   /* We need to work on a copy of the data because this makes it
-     easier to add the trailer and the padding and more important we
-     have to prefix the text with 2 parenthesis, so we have to
-     allocate enough space for:
-
-     ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
-
-     We always append a full block of random bytes as padding but
-     encrypt only what is needed for a full blocksize.  */
+   * easier to add the trailer and the padding and more important we
+   * have to prefix the text with 2 parenthesis.  In CBC mode we
+   * have to allocate enough space for:
+   *
+   *   ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
+   *
+   * we always append a full block of random bytes as padding but
+   * encrypt only what is needed for a full blocksize.  In OCB mode we
+   * have to allocate enough space for just:
+   *
+   *   ((<parameter_list>))
+   */
   blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
-  outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
-  enclen = outlen/blklen * blklen;
-  outbuf = gcry_malloc_secure (outlen);
+  if (use_ocb)
+    {
+      /*       ((            )) */
+      outlen = 2 + protlen + 2 ;
+      enclen = outlen + 16 /* taglen */;
+      outbuf = gcry_malloc_secure (enclen);
+    }
+  else
+    {
+      /*       ((            )( 4:hash 4:sha1 20:<hash> ))  <padding>  */
+      outlen = 2 + protlen + 2 + 6   + 6    + 23      + 2 + blklen;
+      enclen = outlen/blklen * blklen;
+      outbuf = gcry_malloc_secure (outlen);
+    }
   if (!outbuf)
     rc = out_of_core ();
+
+  /* Allocate a buffer for the nonce and the salt.  */
   if (!rc)
     {
-      /* Allocate random bytes to be used as IV, padding and s2k salt. */
-      iv = xtrymalloc (blklen*2+8);
+      /* Allocate random bytes to be used as IV, padding and s2k salt
+       * or in OCB mode for a nonce and the s2k salt.  The IV/nonce is
+       * set later because for OCB we need to set the key first.  */
+      ivsize = (use_ocb? 12 : (blklen*2)) + 8;
+      iv = xtrymalloc (ivsize);
       if (!iv)
-        rc = gpg_error (GPG_ERR_ENOMEM);
+        rc = gpg_error_from_syserror ();
       else
         {
-          gcry_create_nonce (iv, blklen*2+8);
-          rc = gcry_cipher_setiv (hd, iv, blklen);
+          gcry_create_nonce (iv, ivsize);
+          s2ksalt = iv + ivsize - 8;
         }
     }
+
+  /* Hash the passphrase and set the key.  */
   if (!rc)
     {
       unsigned char *key;
@@ -377,14 +428,52 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
       else
         {
           rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
-                                3, iv+2*blklen,
-				s2k_count ? s2k_count : get_standard_s2k_count(),
+                                3, s2ksalt,
+				s2k_count? s2k_count:get_standard_s2k_count(),
 				key, keylen);
           if (!rc)
             rc = gcry_cipher_setkey (hd, key, keylen);
           xfree (key);
         }
     }
+
+  /* Set the IV/nonce.  */
+  if (!rc)
+    {
+      rc = gcry_cipher_setiv (hd, iv, use_ocb? 12 : blklen);
+    }
+
+  if (use_ocb)
+    {
+      /* In OCB Mode we use only the public key parameters as AAD.  */
+      rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin);
+      if (!rc)
+        rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len);
+      if (!rc)
+        rc = gcry_cipher_authenticate
+          (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin));
+
+    }
+  else
+    {
+      /* Hash the entire expression for CBC mode.  Because
+       * TIMESTAMP_EXP won't get protected, we can't simply hash a
+       * continuous buffer but need to call md_write several times.  */
+      gcry_md_hd_t md;
+
+      rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 );
+      if (!rc)
+        {
+          gcry_md_write (md, hashbegin, hashlen);
+          gcry_md_write (md, timestamp_exp, timestamp_exp_len);
+          gcry_md_write (md, ")", 1);
+          memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20);
+          gcry_md_close (md);
+        }
+    }
+
+
+  /* Encrypt.  */
   if (!rc)
     {
       p = outbuf;
@@ -392,17 +481,42 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
       *p++ = '(';
       memcpy (p, protbegin, protlen);
       p += protlen;
-      memcpy (p, ")(4:hash4:sha120:", 17);
-      p += 17;
-      memcpy (p, sha1hash, 20);
-      p += 20;
-      *p++ = ')';
-      *p++ = ')';
-      memcpy (p, iv+blklen, blklen);
-      p += blklen;
+      if (use_ocb)
+        {
+          *p++ = ')';
+          *p++ = ')';
+        }
+      else
+        {
+          memcpy (p, ")(4:hash4:sha120:", 17);
+          p += 17;
+          memcpy (p, hashvalue, 20);
+          p += 20;
+          *p++ = ')';
+          *p++ = ')';
+          memcpy (p, iv+blklen, blklen); /* Add padding.  */
+          p += blklen;
+        }
       assert ( p - outbuf == outlen);
-      rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
+#if OCB_MODE_SUPPORTED
+      if (use_ocb)
+        {
+          gcry_cipher_final (hd);
+          rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0);
+          if (!rc)
+            {
+              log_assert (outlen + 16 == enclen);
+              rc = gcry_cipher_gettag (hd, outbuf + outlen, 16);
+            }
+        }
+      else
+#endif /*OCB_MODE_SUPPORTED*/
+        {
+          rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
+        }
     }
+
+  /* Release cipher handle and check for errors.  */
   gcry_cipher_close (hd);
   if (rc)
     {
@@ -429,7 +543,7 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
        (int)strlen (modestr), modestr,
        &saltpos,
        (unsigned int)strlen (countbuf), countbuf,
-       blklen, &ivpos, blklen, "",
+       use_ocb? 12 : blklen, &ivpos, use_ocb? 12 : blklen, "",
        enclen, &encpos, enclen, "");
     if (!p)
       {
@@ -438,11 +552,12 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
         xfree (outbuf);
         return tmperr;
       }
+
   }
   *resultlen = strlen (p);
   *result = (unsigned char*)p;
-  memcpy (p+saltpos, iv+2*blklen, 8);
-  memcpy (p+ivpos, iv, blklen);
+  memcpy (p+saltpos, s2ksalt, 8);
+  memcpy (p+ivpos, iv, use_ocb? 12 : blklen);
   memcpy (p+encpos, outbuf, enclen);
   xfree (iv);
   xfree (outbuf);
@@ -452,11 +567,13 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
 
 
 /* Protect the key encoded in canonical format in PLAINKEY.  We assume
-   a valid S-Exp here. */
+   a valid S-Exp here.  With USE_UCB set to -1 the default scheme is
+   used (ie. either CBC or OCB), set to 0 the old CBC mode is used,
+   and set to 1 OCB is used. */
 int
 agent_protect (const unsigned char *plainkey, const char *passphrase,
                unsigned char **result, size_t *resultlen,
-	       unsigned long s2k_count)
+	       unsigned long s2k_count, int use_ocb)
 {
   int rc;
   const char *parmlist;
@@ -466,15 +583,16 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   const unsigned char *prot_begin, *prot_end, *real_end;
   size_t n;
   int c, infidx, i;
-  unsigned char hashvalue[20];
   char timestamp_exp[35];
   unsigned char *protected;
   size_t protectedlen;
   int depth = 0;
   unsigned char *p;
-  gcry_md_hd_t md;
   int have_curve = 0;
 
+  if (use_ocb == -1)
+    use_ocb = PROT_DEFAULT_TO_OCB;
+
   /* Create an S-expression with the protected-at timestamp.  */
   memcpy (timestamp_exp, "(12:protected-at15:", 19);
   gnupg_get_isotime (timestamp_exp+19);
@@ -575,21 +693,10 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   assert (!depth);
   real_end = s-1;
 
-  /* Hash the stuff.  Because the timestamp_exp won't get protected,
-     we can't simply hash a continuous buffer but need to use several
-     md_writes.  */
-  rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 );
-  if (rc)
-    return rc;
-  gcry_md_write (md, hash_begin, hash_end - hash_begin);
-  gcry_md_write (md, timestamp_exp, 35);
-  gcry_md_write (md, ")", 1);
-  memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20);
-  gcry_md_close (md);
-
-  rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
-                      passphrase,  hashvalue,
-                      &protected, &protectedlen, s2k_count);
+  rc = do_encryption (hash_begin, hash_end - hash_begin + 1,
+                      prot_begin, prot_end - prot_begin + 1,
+                      passphrase, timestamp_exp, sizeof (timestamp_exp),
+                      &protected, &protectedlen, s2k_count, use_ocb);
   if (rc)
     return rc;
 
@@ -631,11 +738,13 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
 

 /* Do the actual decryption and check the return list for consistency.  */
 static int
-do_decryption (const unsigned char *protected, size_t protectedlen,
+do_decryption (const unsigned char *aad_begin, size_t aad_len,
+               const unsigned char *aadhole_begin, size_t aadhole_len,
+               const unsigned char *protected, size_t protectedlen,
                const char *passphrase,
                const unsigned char *s2ksalt, unsigned long s2kcount,
                const unsigned char *iv, size_t ivlen,
-               int prot_cipher, int prot_cipher_keylen,
+               int prot_cipher, int prot_cipher_keylen, int is_ocb,
                unsigned char **result)
 {
   int rc = 0;
@@ -644,11 +753,29 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
   unsigned char *outbuf;
   size_t reallen;
 
+  if (is_ocb && !OCB_MODE_SUPPORTED)
+    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+
   blklen = gcry_cipher_get_algo_blklen (prot_cipher);
-  if (protectedlen < 4 || (protectedlen%blklen))
-    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+  if (is_ocb)
+    {
+      /* OCB does not require a multiple of the block length but we
+       * check that it is long enough for the 128 bit tag and that we
+       * have the 96 bit nonce.  */
+      if (protectedlen < (4 + 16) || ivlen != 12)
+        return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+    }
+  else
+    {
+      if (protectedlen < 4 || (protectedlen%blklen))
+        return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+    }
 
-  rc = gcry_cipher_open (&hd, prot_cipher, GCRY_CIPHER_MODE_CBC,
+  rc = gcry_cipher_open (&hd, prot_cipher,
+#if OCB_MODE_SUPPORTED
+                         is_ocb? GCRY_CIPHER_MODE_OCB :
+#endif
+                         GCRY_CIPHER_MODE_CBC,
                          GCRY_CIPHER_SECURE);
   if (rc)
     return rc;
@@ -656,8 +783,8 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
   outbuf = gcry_malloc_secure (protectedlen);
   if (!outbuf)
     rc = out_of_core ();
-  if (!rc)
-    rc = gcry_cipher_setiv (hd, iv, ivlen);
+
+  /* Hash the passphrase and set the key.  */
   if (!rc)
     {
       unsigned char *key;
@@ -674,21 +801,60 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
           xfree (key);
         }
     }
+
+  /* Set the IV/nonce.  */
   if (!rc)
-    rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
-                              protected, protectedlen);
+    {
+      rc = gcry_cipher_setiv (hd, iv, ivlen);
+    }
+
+  /* Decrypt.  */
+  if (!rc)
+    {
+#if OCB_MODE_SUPPORTED
+      if (is_ocb)
+        {
+          rc = gcry_cipher_authenticate (hd, aad_begin,
+                                         aadhole_begin - aad_begin);
+          if (!rc)
+            rc = gcry_cipher_authenticate
+              (hd, aadhole_begin + aadhole_len,
+               aad_len - (aadhole_begin+aadhole_len - aad_begin));
+
+          if (!rc)
+            {
+              gcry_cipher_final (hd);
+              rc = gcry_cipher_decrypt (hd, outbuf, protectedlen - 16,
+                                        protected, protectedlen - 16);
+            }
+          if (!rc)
+            rc = gcry_cipher_checktag (hd, protected + protectedlen - 16, 16);
+        }
+      else
+#endif /*OCB_MODE_SUPPORTED*/
+        {
+          rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
+                                    protected, protectedlen);
+        }
+    }
+
+  /* Release cipher handle and check for errors.  */
   gcry_cipher_close (hd);
   if (rc)
     {
       xfree (outbuf);
       return rc;
     }
-  /* Do a quick check first. */
+
+  /* Do a quick check on the data structure. */
   if (*outbuf != '(' && outbuf[1] != '(')
     {
+      /* Note that in OCB mode this is actually invalid _encrypted_
+       * data and not a bad passphrase.  */
       xfree (outbuf);
       return gpg_error (GPG_ERR_BAD_PASSPHRASE);
     }
+
   /* Check that we have a consistent S-Exp. */
   reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
   if (!reallen || (reallen + blklen < protectedlen) )
@@ -702,11 +868,12 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
 
 
 /* Merge the parameter list contained in CLEARTEXT with the original
-   protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
-   Return the new list in RESULT and the MIC value in the 20 byte
-   buffer SHA1HASH.  CUTOFF and CUTLEN will receive the offset and the
-   length of the resulting list which should go into the MIC
-   calculation but then be removed.  */
+ * protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
+ * Return the new list in RESULT and the MIC value in the 20 byte
+ * buffer SHA1HASH; if SHA1HASH is NULL no MIC will be computed.
+ * CUTOFF and CUTLEN will receive the offset and the length of the
+ * resulting list which should go into the MIC calculation but then be
+ * removed.  */
 static int
 merge_lists (const unsigned char *protectedkey,
              size_t replacepos,
@@ -730,7 +897,7 @@ merge_lists (const unsigned char *protectedkey,
     return gpg_error (GPG_ERR_BUG);
 
   /* Estimate the required size of the resulting list.  We have a large
-     safety margin of >20 bytes (MIC hash from CLEARTEXT and the
+     safety margin of >20 bytes (FIXME: MIC hash from CLEARTEXT and the
      removed "protected-" */
   newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
   if (!newlistlen)
@@ -749,7 +916,7 @@ merge_lists (const unsigned char *protectedkey,
   memcpy (p, protectedkey+15+10, replacepos-15-10);
   p += replacepos-15-10;
 
-  /* copy the cleartext */
+  /* Copy the cleartext.  */
   s = cleartext;
   if (*s != '(' && s[1] != '(')
     return gpg_error (GPG_ERR_BUG);  /*we already checked this */
@@ -774,26 +941,29 @@ merge_lists (const unsigned char *protectedkey,
     goto invalid_sexp;
   endpos = s;
   s++;
-  /* Intermezzo: Get the MIC */
-  if (*s != '(')
-    goto invalid_sexp;
-  s++;
-  n = snext (&s);
-  if (!smatch (&s, n, "hash"))
-    goto invalid_sexp;
-  n = snext (&s);
-  if (!smatch (&s, n, "sha1"))
-    goto invalid_sexp;
-  n = snext (&s);
-  if (n != 20)
-    goto invalid_sexp;
-  memcpy (sha1hash, s, 20);
-  s += n;
-  if (*s != ')')
-    goto invalid_sexp;
-  /* End intermezzo */
 
-  /* append the parameter list */
+  /* Intermezzo: Get the MIC if requested.  */
+  if (sha1hash)
+    {
+      if (*s != '(')
+        goto invalid_sexp;
+      s++;
+      n = snext (&s);
+      if (!smatch (&s, n, "hash"))
+        goto invalid_sexp;
+      n = snext (&s);
+      if (!smatch (&s, n, "sha1"))
+        goto invalid_sexp;
+      n = snext (&s);
+      if (n != 20)
+        goto invalid_sexp;
+      memcpy (sha1hash, s, 20);
+      s += n;
+      if (*s != ')')
+        goto invalid_sexp;
+    }
+
+  /* Append the parameter list.  */
   memcpy (p, startpos, endpos - startpos);
   p += endpos - startpos;
 
@@ -867,9 +1037,11 @@ agent_unprotect (ctrl_t ctrl,
     const char *name; /* Name of the protection method. */
     int algo;         /* (A zero indicates the "openpgp-native" hack.)  */
     int keylen;       /* Used key length in bytes.  */
+    unsigned int is_ocb:1;
   } algotable[] = {
     { "openpgp-s2k3-sha1-aes-cbc",    GCRY_CIPHER_AES128, (128/8)},
     { "openpgp-s2k3-sha1-aes256-cbc", GCRY_CIPHER_AES256, (256/8)},
+    { "openpgp-s2k3-ocb-aes",         GCRY_CIPHER_AES128, (128/8), 1},
     { "openpgp-native", 0, 0 }
   };
   int rc;
@@ -882,6 +1054,8 @@ agent_unprotect (ctrl_t ctrl,
   unsigned long s2kcount;
   const unsigned char *iv;
   int prot_cipher, prot_cipher_keylen;
+  int is_ocb;
+  const unsigned char *aad_begin, *aad_end, *aadhole_begin, *aadhole_end;
   const unsigned char *prot_begin;
   unsigned char *cleartext;
   unsigned char *final;
@@ -902,6 +1076,15 @@ agent_unprotect (ctrl_t ctrl,
     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
   if (*s != '(')
     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+  {
+    aad_begin = aad_end = s;
+    aad_end++;
+    i = 1;
+    rc = sskip (&aad_end, &i);
+    if (rc)
+      return rc;
+  }
+
   s++;
   n = snext (&s);
   if (!n)
@@ -913,7 +1096,6 @@ agent_unprotect (ctrl_t ctrl,
   if (!protect_info[infidx].algo)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 
-
   /* See wether we have a protected-at timestamp.  */
   protect_list = s;  /* Save for later.  */
   if (protected_at)
@@ -969,6 +1151,14 @@ agent_unprotect (ctrl_t ctrl,
         return rc;
     }
   /* found */
+  {
+    aadhole_begin = aadhole_end = prot_begin;
+    aadhole_end++;
+    i = 1;
+    rc = sskip (&aadhole_end, &i);
+    if (rc)
+      return rc;
+  }
   n = snext (&s);
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
@@ -976,14 +1166,17 @@ agent_unprotect (ctrl_t ctrl,
   /* Lookup the protection algo.  */
   prot_cipher = 0;        /* (avoid gcc warning) */
   prot_cipher_keylen = 0; /* (avoid gcc warning) */
-  for (i= 0; i < DIM (algotable); i++)
+  is_ocb = 0;
+  for (i=0; i < DIM (algotable); i++)
     if (smatch (&s, n, algotable[i].name))
       {
         prot_cipher = algotable[i].algo;
         prot_cipher_keylen = algotable[i].keylen;
+        is_ocb = algotable[i].is_ocb;
         break;
       }
-  if (i == DIM (algotable))
+  if (i == DIM (algotable)
+      || (is_ocb && !OCB_MODE_SUPPORTED))
     return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
 
   if (!prot_cipher)  /* This is "openpgp-native".  */
@@ -1048,8 +1241,16 @@ agent_unprotect (ctrl_t ctrl,
   s++; /* skip list end */
 
   n = snext (&s);
-  if (n != 16) /* Wrong blocksize for IV (we support only 128 bit). */
-    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+  if (is_ocb)
+    {
+      if (n != 12) /* Wrong size of the nonce. */
+        return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+    }
+  else
+    {
+      if (n != 16) /* Wrong blocksize for IV (we support only 128 bit). */
+        return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+    }
   iv = s;
   s += n;
   if (*s != ')' )
@@ -1060,15 +1261,19 @@ agent_unprotect (ctrl_t ctrl,
     return gpg_error (GPG_ERR_INV_SEXP);
 
   cleartext = NULL; /* Avoid cc warning. */
-  rc = do_decryption (s, n,
+  rc = do_decryption (aad_begin, aad_end - aad_begin,
+                      aadhole_begin, aadhole_end - aadhole_begin,
+                      s, n,
                       passphrase, s2ksalt, s2kcount,
-                      iv, 16, prot_cipher, prot_cipher_keylen,
+                      iv, is_ocb? 12:16,
+                      prot_cipher, prot_cipher_keylen, is_ocb,
                       &cleartext);
   if (rc)
     return rc;
 
   rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
-                    sha1hash, &final, &finallen, &cutoff, &cutlen);
+                    is_ocb? NULL : sha1hash,
+                    &final, &finallen, &cutoff, &cutlen);
   /* Albeit cleartext has been allocated in secure memory and thus
      xfree will wipe it out, we do an extra wipe just in case
      somethings goes badly wrong. */
@@ -1077,15 +1282,19 @@ agent_unprotect (ctrl_t ctrl,
   if (rc)
     return rc;
 
-  rc = calculate_mic (final, sha1hash2);
-  if (!rc && memcmp (sha1hash, sha1hash2, 20))
-    rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
-  if (rc)
+  if (!is_ocb)
     {
-      wipememory (final, finallen);
-      xfree (final);
-      return rc;
+      rc = calculate_mic (final, sha1hash2);
+      if (!rc && memcmp (sha1hash, sha1hash2, 20))
+        rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
+      if (rc)
+        {
+          wipememory (final, finallen);
+          xfree (final);
+          return rc;
+        }
     }
+
   /* Now remove the part which is included in the MIC but should not
      go into the final thing.  */
   if (cutlen)
@@ -1184,7 +1393,6 @@ agent_private_key_type (const unsigned char *privatekey)
           n = snext (&s);
           if (!n)
             return PRIVATE_KEY_UNKNOWN; /* Invalid sexp.  */
-          log_debug ("openpgp-native protection '%.*s'\n", (int)n, s);
           if (smatch (&s, n, "none"))
             return PRIVATE_KEY_OPENPGP_NONE;  /* Yes.  */
         }
diff --git a/agent/t-protect.c b/agent/t-protect.c
index 9096cb2..431eccf 100644
--- a/agent/t-protect.c
+++ b/agent/t-protect.c
@@ -195,7 +195,7 @@ test_agent_protect (void)
     {
       ret = agent_protect ((const unsigned char*)specs[i].key,
                            specs[i].passphrase,
-			   &specs[i].result, &specs[i].resultlen, 0);
+			   &specs[i].result, &specs[i].resultlen, 0, -1);
       if (gpg_err_code (ret) != specs[i].ret_expected)
 	{
 	  printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n",

commit 100b413d7f69f6f091fd98185df4d579ce6ce0ce
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Apr 12 14:20:53 2016 +0200

    doc: Note that the persistant passphrase format is unimplemented.
    
    --

diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 42c4b1f..150ea7c 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -240,6 +240,8 @@ This format is used to transfer keys between gpg and gpg-agent.
 Persistent Passphrase Format
 ============================
 
+Note: That this has not yet been implemented.
+
 To allow persistent storage of cached passphrases we use a scheme
 similar to the private-key storage format.  This is a master
 passphrase format where each file may protect several secrets under
diff --git a/common/sexp-parse.h b/common/sexp-parse.h
index f68c552..442d106 100644
--- a/common/sexp-parse.h
+++ b/common/sexp-parse.h
@@ -49,7 +49,7 @@ snext (unsigned char const **buf)
 }
 
 /* Skip over the S-Expression BUF points to and update BUF to point to
-   the chacter right behind.  DEPTH gives the initial number of open
+   the character right behind.  DEPTH gives the initial number of open
    lists and may be passed as a positive number to skip over the
    remainder of an S-Expression if the current position is somewhere
    in an S-Expression.  The function may return an error code if it

commit 7faf131c8b8710419df3dc13a1228d1977c55f53
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Apr 11 10:24:15 2016 +0200

    indent: Help Emacs not to get confused by conditional compilation.
    
    * agent/protect.c (calibrate_get_time) [W32]: Use separate function
    calls for W32 and W32CE.
    --
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/protect.c b/agent/protect.c
index cdb39fd..f037703 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -95,11 +95,13 @@ calibrate_get_time (struct calibrate_time_s *data)
 #ifdef HAVE_W32_SYSTEM
 # ifdef HAVE_W32CE_SYSTEM
   GetThreadTimes (GetCurrentThread (),
+                  &data->creation_time, &data->exit_time,
+                  &data->kernel_time, &data->user_time);
 # else
   GetProcessTimes (GetCurrentProcess (),
-# endif
                    &data->creation_time, &data->exit_time,
                    &data->kernel_time, &data->user_time);
+# endif
 #else
   struct tms tmp;
 
diff --git a/doc/HACKING b/doc/HACKING
index 2f3dd43..11ae53b 100644
--- a/doc/HACKING
+++ b/doc/HACKING
@@ -51,6 +51,7 @@ are
  - build   :: Changes to the build system
  - speedo  :: Speedo build system specific changes
  - doc     :: Documentation changes
+ - indent  :: Indentation and similar changes
 
 Typo fixes and documentation updates don't need a ChangeLog entry;
 thus you would use a commit message like

commit 22b869adca877ff0826707109e7f7b391eb44441
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Apr 8 09:49:15 2016 +0200

    doc: Point to RFC-4880 for keyedit subcommand "tsign".
    
    --
    
    GnuPG-bug-id: 2283

diff --git a/doc/gpg.texi b/doc/gpg.texi
index 0e91d8c..5994d9f 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -694,7 +694,8 @@ line.
   Make a trust signature. This is a signature that combines the notions
   of certification (like a regular signature), and trust (like the
   "trust" command). It is generally only useful in distinct communities
-  or groups.
+  or groups.  For more information please read the sections
+  ``Trust Signature'' and ``Regular Expression'' in RFC-4880.
 @end table
 
 @c man:.RS

-----------------------------------------------------------------------

Summary of changes:
 agent/agent.h        |   2 +-
 agent/command-ssh.c  |   2 +-
 agent/command.c      |   2 +-
 agent/cvt-openpgp.c  |   2 +-
 agent/genkey.c       |   2 +-
 agent/keyformat.txt  |  64 +++++---
 agent/protect-tool.c |   8 +-
 agent/protect.c      | 434 ++++++++++++++++++++++++++++++++++++++-------------
 agent/t-protect.c    |   2 +-
 common/sexp-parse.h  |   2 +-
 doc/HACKING          |   1 +
 doc/gpg.texi         |   3 +-
 12 files changed, 383 insertions(+), 141 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list