Implement dynamic S2K count computation.

This commit is contained in:
Werner Koch 2009-12-14 20:12:56 +00:00
parent ed7bf14034
commit 4d693033ab
6 changed files with 190 additions and 5 deletions

3
NEWS
View File

@ -18,6 +18,9 @@ Noteworthy changes in version 2.1.x (under development)
* New GPGSM option --ignore-cert-extension. * New GPGSM option --ignore-cert-extension.
* New and changed passphrases are now created with an iteration count
requiring about 100ms of CPU work.
Noteworthy changes in version 2.0.13 (2009-09-04) Noteworthy changes in version 2.0.13 (2009-09-04)
------------------------------------------------- -------------------------------------------------

View File

@ -1,3 +1,12 @@
2009-12-14 Werner Koch <wk@g10code.com>
* protect.c (agent_unprotect): Decode the S2K count here and take
care of the new unencoded values. Add a lower limit sanity check.
(hash_passphrase): Do not decode here.
(get_standard_s2k_count, calibrate_s2k_count): New.
(calibrate_get_time, calibrate_elapsed_time): New.
(do_encryption): Use get_standard_s2k_count.
2009-12-08 Werner Koch <wk@g10code.com> 2009-12-08 Werner Koch <wk@g10code.com>
* protect.c (agent_unprotect): Avoid compiler warning. * protect.c (agent_unprotect): Avoid compiler warning.

View File

@ -285,6 +285,7 @@ int agent_genkey (ctrl_t ctrl,
int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey); int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
/*-- protect.c --*/ /*-- protect.c --*/
unsigned long get_standard_s2k_count (void);
int agent_protect (const unsigned char *plainkey, const char *passphrase, int agent_protect (const unsigned char *plainkey, const char *passphrase,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase, int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,

View File

@ -61,6 +61,7 @@ enum cmd_and_opt_values
oShadow, oShadow,
oShowShadowInfo, oShowShadowInfo,
oShowKeygrip, oShowKeygrip,
oS2Kcalibration,
oCanonical, oCanonical,
oP12Import, oP12Import,
@ -120,6 +121,8 @@ static ARGPARSE_OPTS opts[] = {
"import a pkcs#12 encoded private key"), "import a pkcs#12 encoded private key"),
ARGPARSE_c (oP12Export, "p12-export", ARGPARSE_c (oP12Export, "p12-export",
"export a private key pkcs#12 encoded"), "export a private key pkcs#12 encoded"),
ARGPARSE_c (oS2Kcalibration, "s2k-calibration", "@"),
ARGPARSE_group (301, N_("@\nOptions:\n ")), ARGPARSE_group (301, N_("@\nOptions:\n ")),
@ -1061,6 +1064,8 @@ main (int argc, char **argv )
case oP12Export: cmd = oP12Export; break; case oP12Export: cmd = oP12Export; break;
case oP12Charset: opt_p12_charset = pargs.r.ret_str; break; case oP12Charset: opt_p12_charset = pargs.r.ret_str; break;
case oS2Kcalibration: cmd = oS2Kcalibration; break;
case oPassphrase: opt_passphrase = pargs.r.ret_str; break; case oPassphrase: opt_passphrase = pargs.r.ret_str; break;
case oStore: opt_store = 1; break; case oStore: opt_store = 1; break;
case oForce: opt_force = 1; break; case oForce: opt_force = 1; break;
@ -1105,6 +1110,12 @@ main (int argc, char **argv )
import_p12_file (fname); import_p12_file (fname);
else if (cmd == oP12Export) else if (cmd == oP12Export)
export_p12_file (fname); export_p12_file (fname);
else if (cmd == oS2Kcalibration)
{
if (!opt.verbose)
opt.verbose++; /* We need to see something. */
get_standard_s2k_count ();
}
else else
show_file (fname); show_file (fname);

View File

@ -27,6 +27,11 @@
#include <assert.h> #include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef HAVE_W32_SYSTEM
# include <windows.h>
#else
# include <sys/times.h>
#endif
#include "agent.h" #include "agent.h"
@ -51,12 +56,133 @@ static struct {
}; };
/* A helper object for time measurement. */
struct calibrate_time_s
{
#ifdef HAVE_W32_SYSTEM
FILETIME creation_time, exit_time, kernel_time, user_time;
#else
clock_t ticks;
#endif
};
static int static int
hash_passphrase (const char *passphrase, int hashalgo, hash_passphrase (const char *passphrase, int hashalgo,
int s2kmode, int s2kmode,
const unsigned char *s2ksalt, unsigned long s2kcount, const unsigned char *s2ksalt, unsigned long s2kcount,
unsigned char *key, size_t keylen); unsigned char *key, size_t keylen);
/* Get the process time and store it in DATA. */
static void
calibrate_get_time (struct calibrate_time_s *data)
{
#ifdef HAVE_W32_SYSTEM
GetProcessTimes (GetCurrentProcess (),
&data->creation_time, &data->exit_time,
&data->kernel_time, &data->user_time);
#else
struct tms tmp;
times (&tmp);
data->ticks = tmp.tms_utime;
#endif
}
static unsigned long
calibrate_elapsed_time (struct calibrate_time_s *starttime)
{
struct calibrate_time_s stoptime;
calibrate_get_time (&stoptime);
#ifdef HAVE_W32_SYSTEM
{
unsigned long long t1, t2;
t1 = (((unsigned long long)starttime->kernel_time.dwHighDateTime << 32)
+ starttime->kernel_time.dwLowDateTime);
t1 += (((unsigned long long)starttime->user_time.dwHighDateTime << 32)
+ starttime->user_time.dwLowDateTime);
t2 = (((unsigned long long)stoptime.kernel_time.dwHighDateTime << 32)
+ stoptime.kernel_time.dwLowDateTime);
t2 += (((unsigned long long)stoptime.user_time.dwHighDateTime << 32)
+ stoptime.user_time.dwLowDateTime);
return (unsigned long)((t2 - t1)/10000);
}
#else
return (unsigned long)((((double) (stoptime.ticks - starttime->ticks))
/CLOCKS_PER_SEC)*10000000);
#endif
}
/* Run a test hashing for COUNT and return the time required in
milliseconds. */
static unsigned long
calibrate_s2k_count_one (unsigned long count)
{
int rc;
char keybuf[PROT_CIPHER_KEYLEN];
struct calibrate_time_s starttime;
calibrate_get_time (&starttime);
rc = hash_passphrase ("123456789abcdef0", GCRY_MD_SHA1,
3, "saltsalt", count, keybuf, sizeof keybuf);
if (rc)
BUG ();
return calibrate_elapsed_time (&starttime);
}
/* Measure the time we need to do the hash operations and deduce an
S2K count which requires about 100ms of time. */
static unsigned long
calibrate_s2k_count (void)
{
unsigned long count;
unsigned long ms;
for (count = 65536; count; count *= 2)
{
ms = calibrate_s2k_count_one (count);
if (opt.verbose > 1)
log_info ("S2K calibration: %lu -> %lums\n", count, ms);
if (ms > 100)
break;
}
count = (unsigned long)(((double)count / ms) * 100);
count /= 1024;
count *= 1024;
if (count < 65536)
count = 65536;
if (opt.verbose)
{
ms = calibrate_s2k_count_one (count);
log_info ("S2K calibration: %lu iterations for %lums\n", count, ms);
}
return count;
}
/* Return the standard S2K count. */
unsigned long
get_standard_s2k_count (void)
{
static unsigned long count;
if (!count)
count = calibrate_s2k_count ();
/* Enforce a lower limit. */
return count < 65536 ? 65536 : count;
}
/* Calculate the MIC for a private key S-Exp. SHA1HASH should point to /* Calculate the MIC for a private key S-Exp. SHA1HASH should point to
@ -193,7 +319,8 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
else else
{ {
rc = hash_passphrase (passphrase, GCRY_MD_SHA1, rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
3, iv+2*blklen, 96, key, keylen); 3, iv+2*blklen,
get_standard_s2k_count (), key, keylen);
if (!rc) if (!rc)
rc = gcry_cipher_setkey (hd, key, keylen); rc = gcry_cipher_setkey (hd, key, keylen);
xfree (key); xfree (key);
@ -757,9 +884,23 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
is nothing we should worry about */ is nothing we should worry about */
if (s[n] != ')' ) if (s[n] != ')' )
return gpg_error (GPG_ERR_INV_SEXP); return gpg_error (GPG_ERR_INV_SEXP);
/* Old versions of gpg-agent used the funny floating point number in
a byte encoding as specified by OpenPGP. However this is not
needed and thus we now store it as a plain unsigned integer. We
can easily distinguish the old format by looking at its value:
Less than 256 is an old-style encoded number; other values are
plain integers. In any case we check that they are at least
65536 because we never used a lower value in the past and we
should have a lower limit. */
s2kcount = strtoul ((const char*)s, NULL, 10); s2kcount = strtoul ((const char*)s, NULL, 10);
if (!s2kcount) if (!s2kcount)
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
if (s2kcount < 256)
s2kcount = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6);
if (s2kcount < 65536)
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
s += n; s += n;
s++; /* skip list end */ s++; /* skip list end */
@ -848,8 +989,7 @@ agent_private_key_type (const unsigned char *privatekey)
/* Transform a passphrase into a suitable key of length KEYLEN and /* Transform a passphrase into a suitable key of length KEYLEN and
store this key in the caller provided buffer KEY. The caller must store this key in the caller provided buffer KEY. The caller must
provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on
that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable that mode an S2KSALT of 8 random bytes and an S2KCOUNT.
value is 96).
Returns an error code on failure. */ Returns an error code on failure. */
static int static int
@ -891,7 +1031,7 @@ hash_passphrase (const char *passphrase, int hashalgo,
if (s2kmode == 3) if (s2kmode == 3)
{ {
count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6); count = s2kcount;
if (count < len2) if (count < len2)
count = len2; count = len2;
} }

View File

@ -601,6 +601,24 @@ cmd_getinfo (assuan_context_t ctx, char *line)
return rc; return rc;
} }
static const char hlp_passwd[] =
"PASSWD <userID>\n"
"\n"
"Change the passphrase of the secret key for USERID.";
static gpg_error_t
cmd_passwd (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
line = skip_options (line);
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
return err;
}
/* Helper to register our commands with libassuan. */ /* Helper to register our commands with libassuan. */
@ -611,6 +629,7 @@ register_commands (assuan_context_t ctx)
{ {
const char *name; const char *name;
assuan_handler_t handler; assuan_handler_t handler;
assuan_handler_t help;
} table[] = { } table[] = {
{ "RECIPIENT", cmd_recipient }, { "RECIPIENT", cmd_recipient },
{ "SIGNER", cmd_signer }, { "SIGNER", cmd_signer },
@ -628,13 +647,15 @@ register_commands (assuan_context_t ctx)
{ "GENKEY", cmd_genkey }, { "GENKEY", cmd_genkey },
{ "DELKEYS", cmd_delkeys }, { "DELKEYS", cmd_delkeys },
{ "GETINFO", cmd_getinfo }, { "GETINFO", cmd_getinfo },
{ "PASSWD", cmd_passwd, hlp_passwd},
{ NULL } { NULL }
}; };
int i, rc; int i, rc;
for (i=0; table[i].name; i++) for (i=0; table[i].name; i++)
{ {
rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); rc = assuan_register_command (ctx, table[i].name,
table[i].handler, table[i].help);
if (rc) if (rc)
return rc; return rc;
} }