Actually show translators comments in PO files
[gnupg.git] / g10 / passphrase.c
index 0d7580d..2133de5 100644 (file)
@@ -1,6 +1,6 @@
 /* passphrase.c -  Get a passphrase
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005, 2006, 2007 Free Software Foundation, Inc.
+ *               2005, 2006, 2007, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -50,11 +50,62 @@ static char *next_pw = NULL;
 static char *last_pw = NULL;
 
 
-/* Hash a passphrase using the supplied s2k. If create is true, create
-   a new salt or what else must be filled into the s2k for a new key.
-   always needs: dek->algo, s2k->mode, s2k->hash_algo.  */
+
+/* Pack an s2k iteration count into the form specified in 2440.  If
+   we're in between valid values, round up.  With value 0 return the
+   old default.  */
+unsigned char
+encode_s2k_iterations (int iterations)
+{
+  gpg_error_t err;
+  unsigned char c=0;
+  unsigned char result;
+  unsigned int count;
+
+  if (!iterations)
+    {
+      unsigned long mycnt;
+
+      /* Ask the gpg-agent for a useful iteration count.  */
+      err = agent_get_s2k_count (&mycnt);
+      if (err || mycnt < 65536)
+        {
+          /* Don't print an error if an older agent is used.  */
+          if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER)
+            log_error (_("problem with the agent: %s\n"), gpg_strerror (err));
+          /* Default to 65536 which we used up to 2.0.13.  */
+          return 96; 
+        }
+      else if (mycnt >= 65011712)
+        return 255; /* Largest possible value.  */
+      else
+        return encode_s2k_iterations ((int)mycnt);
+    }
+
+  if (iterations <= 1024)
+    return 0;  /* Command line arg compatibility.  */
+
+  if (iterations >= 65011712)
+    return 255;
+  
+  /* Need count to be in the range 16-31 */
+  for (count=iterations>>6; count>=32; count>>=1)
+    c++;
+
+  result = (c<<4)|(count-16);
+
+  if (S2K_DECODE_COUNT(result) < iterations)
+    result++;
+  
+  return result;
+}
+
+
+
+/* Hash a passphrase using the supplied s2k. 
+   Always needs: dek->algo, s2k->mode, s2k->hash_algo.  */
 static void
-hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
+hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k)
 {
   gcry_md_hd_t md;
   int pass, i;
@@ -62,7 +113,7 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
   int pwlen = strlen(pw);
 
   assert ( s2k->hash_algo );
-  dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
+  dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
   if ( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) )
     BUG();
 
@@ -82,13 +133,6 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
           int len2 = pwlen + 8;
           ulong count = len2;
           
-          if ( create && !pass )
-            {
-              gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
-              if ( s2k->mode == 3 )
-                s2k->count = opt.s2k_count;
-           }
-
           if ( s2k->mode == 3 )
             {
               count = S2K_DECODE_COUNT(s2k->count);
@@ -96,6 +140,10 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
                 count = len2;
            }
 
+          /* Fixme: To avoid DoS attacks by sending an sym-encrypted
+             packet with a very high S2K count, we should either cap
+             the iteration count or CPU seconds based timeout.  */
+
           /* A little bit complicated because we need a ulong for count. */
           while ( count > len2 )  /* maybe iterated+salted */
             { 
@@ -236,8 +284,8 @@ read_passphrase_from_fd( int fd )
 /*
  * Ask the GPG Agent for the passphrase.
  * Mode 0:  Allow cached passphrase
- *      1:  No cached passphrase FIXME: Not really implemented
- *      2:  Ditto, but change the text to "repeat entry"
+ *      1:  No cached passphrase; that is we are asking for a new passphrase
+ *          FIXME: Only partially implemented
  *
  * Note that TRYAGAIN_TEXT must not be translated.  If CANCELED is not
  * NULL, the function does set it to 1 if the user canceled the
@@ -246,7 +294,7 @@ read_passphrase_from_fd( int fd )
  * computed, this will be used as the cacheid.
  */
 static char *
-passphrase_get ( u32 *keyid, int mode, const char *cacheid,
+passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat,
                  const char *tryagain_text,
                  const char *custom_description,
                  const char *custom_prompt, int *canceled)
@@ -261,6 +309,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
   char *my_prompt;
   char hexfprbuf[20*2+1];
   const char *my_cacheid;
+  int check = (mode == 1);
 
   if (canceled)
     *canceled = 0;
@@ -331,8 +380,6 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
       }
       
     }
-  else if (mode == 2 ) 
-    atext = xstrdup ( _("Repeat passphrase\n") );
   else
     atext = xstrdup ( _("Enter passphrase\n") );
                 
@@ -349,7 +396,8 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
 
   my_prompt = custom_prompt ? native_to_utf8 (custom_prompt): NULL;
 
-  rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext, &pw);
+  rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext,
+                             repeat, check, &pw);
   
   xfree (my_prompt);
   xfree (atext); atext = NULL;
@@ -434,53 +482,6 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo )
 }
 
 
-/****************
- * Ask for a passphrase and return that string.
- */
-char *
-ask_passphrase (const char *description,
-                const char *tryagain_text,
-                const char *promptid,
-                const char *prompt,
-                const char *cacheid, int *canceled)
-{
-  char *pw = NULL;
-
-  (void)promptid;
-  
-  if (canceled)
-    *canceled = 0;
-
-  if (!opt.batch && description)
-    {
-      if (strchr (description, '%'))
-        {
-          char *tmp = unescape_percent_string
-            ((const unsigned char*)description);
-          tty_printf ("\n%s\n", tmp);
-          xfree (tmp);
-        }
-      else
-        tty_printf ("\n%s\n",description);
-    }
-               
-  if (have_static_passphrase ()) 
-    {
-      pw = xmalloc_secure (strlen(fd_passwd)+1);
-      strcpy (pw, fd_passwd);
-    }
-  else
-    pw = passphrase_get (NULL, 0, cacheid,
-                         tryagain_text, description, prompt,
-                         canceled );
-
-  if (!pw || !*pw)
-    write_status( STATUS_MISSING_PASSPHRASE );
-
-  return pw;
-}
-
-
 /* Return a new DEK object Using the string-to-key sepcifier S2K.  Use
    KEYID and PUBKEY_ALGO to prompt the user.  Returns NULL is the user
    selected to cancel the passphrase entry and if CANCELED is not
@@ -488,17 +489,22 @@ ask_passphrase (const char *description,
 
    MODE 0:  Allow cached passphrase
         1:  Ignore cached passphrase 
-        2:  Ditto, but change the text to "repeat entry"
+        2:  Ditto, but create a new key
+        3:  Allow cached passphrase; use the S2K salt as the cache ID
+        4:  Ditto, but create a new key
 */
 DEK *
-passphrase_to_dek (u32 *keyid, int pubkey_algo,
-                  int cipher_algo, STRING2KEY *s2k, int mode,
-                   const char *tryagain_text, int *canceled)
+passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
+                       int cipher_algo, STRING2KEY *s2k, int mode,
+                       const char *tryagain_text, 
+                       const char *custdesc, const char *custprompt,
+                       int *canceled)
 {
   char *pw = NULL;
   DEK *dek;
   STRING2KEY help_s2k;
   int dummy_canceled;
+  char s2k_cacheidbuf[1+16+1], *s2k_cacheid = NULL;
 
   if (!canceled)
     canceled = &dummy_canceled;
@@ -506,6 +512,7 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
   
   if ( !s2k )
     {
+      assert (mode != 3 && mode != 4);
       /* This is used for the old rfc1991 mode 
        * Note: This must match the code in encode.c with opt.rfc1991 set */
       s2k = &help_s2k;
@@ -513,6 +520,23 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
       s2k->hash_algo = S2K_DIGEST_ALGO;
     }
 
+  /* Create a new salt or what else to be filled into the s2k for a
+     new key.  */
+  if ((mode == 2 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
+    {
+      gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
+      if ( s2k->mode == 3 )
+        {
+          /* We delay the encoding until it is really needed.  This is
+             if we are going to dynamically calibrate it, we need to
+             call out to gpg-agent and that should not be done during
+             option processing in main().  */
+          if (!opt.s2k_count)
+            opt.s2k_count = encode_s2k_iterations (0);
+          s2k->count = opt.s2k_count;
+        }
+    }
+
   /* If we do not have a passphrase available in NEXT_PW and status
      information are request, we print them now. */
   if ( !next_pw && is_status_enabled() ) 
@@ -610,42 +634,24 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
     }
   else 
     {
+      if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
+       {
+         memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
+         *s2k_cacheidbuf = 'S';
+         bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
+         s2k_cacheid = s2k_cacheidbuf;
+       }
+
       /* Divert to the gpg-agent. */
-      pw = passphrase_get ( keyid, mode == 2? 1: 0, NULL,
-                            tryagain_text, NULL, NULL, canceled );
+      pw = passphrase_get (keyid, mode == 2, s2k_cacheid,
+                           (mode == 2 || mode == 4)? opt.passphrase_repeat : 0,
+                           tryagain_text, custdesc, custprompt, canceled);
       if (*canceled)
         {
           xfree (pw);
          write_status( STATUS_MISSING_PASSPHRASE );
           return NULL;
         }
-      if (!pw)
-        pw = xstrdup ("");
-      if ( *pw && mode == 2 )
-        {
-         int i;
-         for(i=0;i<opt.passwd_repeat;i++)
-           {
-             char *pw2 = passphrase_get ( keyid, 2, NULL, NULL, NULL,
-                                          NULL, canceled );
-              if (*canceled)
-                {
-                  xfree (pw);
-                  xfree (pw2);
-                 write_status( STATUS_MISSING_PASSPHRASE );
-                  return NULL;
-                }
-             if (!pw2)
-               pw2 = xstrdup ("");
-             if ( strcmp(pw, pw2) )
-               {
-                 xfree(pw2);
-                 xfree(pw);
-                 return NULL;
-               }
-             xfree(pw2);
-           }
-       }
     }
     
   if ( !pw || !*pw )
@@ -656,11 +662,24 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
      get_last_passphrase(). */
   dek = xmalloc_secure_clear ( sizeof *dek );
   dek->algo = cipher_algo;
-  if ( !*pw && mode == 2 )
+  if ( (!pw || !*pw) && (mode == 2 || mode == 4))
     dek->keylen = 0;
   else
-    hash_passphrase( dek, pw, s2k, mode==2 );
+    hash_passphrase (dek, pw, s2k);
+  if (s2k_cacheid)
+    memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
   xfree(last_pw);
   last_pw = pw;
   return dek;
 }
+
+
+DEK *
+passphrase_to_dek (u32 *keyid, int pubkey_algo,
+                  int cipher_algo, STRING2KEY *s2k, int mode,
+                   const char *tryagain_text, int *canceled)
+{
+  return passphrase_to_dek_ext (keyid, pubkey_algo, cipher_algo,
+                                s2k, mode, tryagain_text, NULL, NULL,
+                                canceled);
+}