mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
Add more passphrase policy rules.
(--max-passphrase-days).
This commit is contained in:
parent
15d0cb42a1
commit
f268889b8f
45 changed files with 8136 additions and 5736 deletions
|
@ -1,3 +1,21 @@
|
|||
2007-08-28 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c (main): Add option --faked-system-time.
|
||||
|
||||
* protect-tool.c (read_and_unprotect): Print the protected-at date.
|
||||
|
||||
* agent.h (struct server_control_s): Add member IN_PASSWD.
|
||||
* command.c (cmd_passwd): Set it.
|
||||
* findkey.c (try_unprotect_cb): Use it.
|
||||
|
||||
* protect.c (do_encryption): Replace asprintf by xtryasprint.
|
||||
(agent_protect): Create the protected-at item.
|
||||
(agent_unprotect): Add optional arg PROTECTED_AT.
|
||||
(merge_lists): Add args CUTOFF and CUTLEN.
|
||||
(agent_unprotect): Use them.
|
||||
* findkey.c (try_unprotect_cb): Add code to test for expired keys.
|
||||
(unprotect): Allow changing the passphrase.
|
||||
|
||||
2007-08-27 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c: Add options --min-passphrase-nonalpha,
|
||||
|
|
|
@ -88,7 +88,12 @@ struct
|
|||
unsigned int min_passphrase_nonalpha;
|
||||
/* File name with a patternfile or NULL if not enabled. */
|
||||
const char *check_passphrase_pattern;
|
||||
|
||||
/* If not 0 the user is asked to change his passphrase after these
|
||||
number of days. */
|
||||
unsigned int max_passphrase_days;
|
||||
/* If set, a passphrase history will be written and checked at each
|
||||
passphrase change. */
|
||||
int enable_passhrase_history;
|
||||
|
||||
int running_detached; /* We are running detached from the tty. */
|
||||
|
||||
|
@ -153,6 +158,8 @@ struct server_control_s
|
|||
|
||||
int use_auth_call; /* Hack to send the PKAUTH command instead of the
|
||||
PKSIGN command to the scdaemon. */
|
||||
int in_passwd; /* Hack to inhibit enforced passphrase change
|
||||
during an explicit passwd command. */
|
||||
};
|
||||
|
||||
|
||||
|
@ -182,7 +189,7 @@ enum
|
|||
/* Values for the cache_mode arguments. */
|
||||
typedef enum
|
||||
{
|
||||
CACHE_MODE_IGNORE = 0, /* Special mode to by pass the cache. */
|
||||
CACHE_MODE_IGNORE = 0, /* Special mode to bypass the cache. */
|
||||
CACHE_MODE_ANY, /* Any mode except ignore matches. */
|
||||
CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */
|
||||
CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */
|
||||
|
@ -271,6 +278,7 @@ int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
|
|||
int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
gnupg_isotime_t protected_at,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_private_key_type (const unsigned char *privatekey);
|
||||
unsigned char *make_shadow_info (const char *serialno, const char *idstring);
|
||||
|
|
|
@ -1039,6 +1039,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
|||
if (rc)
|
||||
goto leave;
|
||||
|
||||
ctrl->in_passwd++;
|
||||
rc = agent_key_from_file (ctrl, ctrl->server_local->keydesc,
|
||||
grip, &shadow_info, CACHE_MODE_IGNORE, &s_skey);
|
||||
if (rc)
|
||||
|
@ -1050,6 +1051,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
|||
}
|
||||
else
|
||||
rc = agent_protect_and_store (ctrl, s_skey);
|
||||
ctrl->in_passwd--;
|
||||
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
ctrl->server_local->keydesc = NULL;
|
||||
|
|
115
agent/findkey.c
115
agent/findkey.c
|
@ -1,5 +1,6 @@
|
|||
/* findkey.c - locate the secret key
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
/* findkey.c - Locate the secret key
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
|
||||
* 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
|
@ -31,15 +32,20 @@
|
|||
#include <pth.h> /* (we use pth_sleep) */
|
||||
|
||||
#include "agent.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
/* Helper to pass data to the check callback of the unprotect function. */
|
||||
struct try_unprotect_arg_s {
|
||||
struct try_unprotect_arg_s
|
||||
{
|
||||
ctrl_t ctrl;
|
||||
const unsigned char *protected_key;
|
||||
unsigned char *unprotected_key;
|
||||
int change_required; /* Set by the callback to indicate that the
|
||||
user should chnage the passphrase. */
|
||||
};
|
||||
|
||||
|
||||
|
@ -132,10 +138,71 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
|
|||
{
|
||||
struct try_unprotect_arg_s *arg = pi->check_cb_arg;
|
||||
size_t dummy;
|
||||
gpg_error_t err;
|
||||
gnupg_isotime_t now, protected_at, tmptime;
|
||||
char *desc = NULL;
|
||||
|
||||
assert (!arg->unprotected_key);
|
||||
return agent_unprotect (arg->protected_key, pi->pin,
|
||||
&arg->unprotected_key, &dummy);
|
||||
|
||||
arg->change_required = 0;
|
||||
err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
|
||||
&arg->unprotected_key, &dummy);
|
||||
if (err)
|
||||
return err;
|
||||
if (!opt.max_passphrase_days || arg->ctrl->in_passwd)
|
||||
return 0; /* No regular passphrase change required. */
|
||||
|
||||
if (!*protected_at)
|
||||
{
|
||||
/* No protection date known - must force passphrase change. */
|
||||
desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A"
|
||||
"Please change it now."));
|
||||
if (!desc)
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
else
|
||||
{
|
||||
gnupg_get_isotime (now);
|
||||
gnupg_copy_time (tmptime, protected_at);
|
||||
err = add_days_to_isotime (tmptime, opt.max_passphrase_days);
|
||||
if (err)
|
||||
return err;
|
||||
if (strcmp (now, tmptime) > 0 )
|
||||
{
|
||||
/* Passphrase "expired". */
|
||||
desc = xtryasprintf
|
||||
(_("This passphrase has not been changed%%0A"
|
||||
"since %.4s-%.2s-%.2s. Please change it now."),
|
||||
protected_at, protected_at+4, protected_at+6);
|
||||
if (!desc)
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
}
|
||||
|
||||
if (desc)
|
||||
{
|
||||
/* Change required. */
|
||||
if (opt.enforce_passphrase_constraints)
|
||||
{
|
||||
err = agent_get_confirmation (arg->ctrl, desc,
|
||||
_("Change passphrase"), NULL);
|
||||
if (!err)
|
||||
arg->change_required = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = agent_get_confirmation (arg->ctrl, desc,
|
||||
_("Change passphrase"),
|
||||
_("I'll change it later"));
|
||||
if (!err)
|
||||
arg->change_required = 1;
|
||||
else if (gpg_err_code (err) == GPG_ERR_CANCELED)
|
||||
err = 0;
|
||||
}
|
||||
xfree (desc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -260,7 +327,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
|||
pw = agent_get_cache (hexgrip, cache_mode, &cache_marker);
|
||||
if (pw)
|
||||
{
|
||||
rc = agent_unprotect (*keybuf, pw, &result, &resultlen);
|
||||
rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
|
||||
agent_unlock_cache_entry (&cache_marker);
|
||||
if (!rc)
|
||||
{
|
||||
|
@ -272,7 +339,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
|||
}
|
||||
|
||||
/* If the pinentry is currently in use, we wait up to 60 seconds
|
||||
for it close and check the cache again. This solves a common
|
||||
for it to close and check the cache again. This solves a common
|
||||
situation where several requests for unprotecting a key have
|
||||
been made but the user is still entering the passphrase for
|
||||
the first request. Because all requests to agent_askpin are
|
||||
|
@ -294,7 +361,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
|||
/* Timeout - better call pinentry now the plain way. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
|
||||
if (!pi)
|
||||
return gpg_error_from_syserror ();
|
||||
|
@ -303,14 +370,46 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
|||
pi->max_digits = 8;
|
||||
pi->max_tries = 3;
|
||||
pi->check_cb = try_unprotect_cb;
|
||||
arg.ctrl = ctrl;
|
||||
arg.protected_key = *keybuf;
|
||||
arg.unprotected_key = NULL;
|
||||
arg.change_required = 0;
|
||||
pi->check_cb_arg = &arg;
|
||||
|
||||
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
|
||||
if (!rc)
|
||||
{
|
||||
assert (arg.unprotected_key);
|
||||
if (arg.change_required)
|
||||
{
|
||||
size_t canlen, erroff;
|
||||
gcry_sexp_t s_skey;
|
||||
|
||||
assert (arg.unprotected_key);
|
||||
canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
|
||||
rc = gcry_sexp_sscan (&s_skey, &erroff,
|
||||
(char*)arg.unprotected_key, canlen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to build S-Exp (off=%u): %s\n",
|
||||
(unsigned int)erroff, gpg_strerror (rc));
|
||||
wipememory (arg.unprotected_key, canlen);
|
||||
xfree (arg.unprotected_key);
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
rc = agent_protect_and_store (ctrl, s_skey);
|
||||
gcry_sexp_release (s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("changing the passphrase failed: %s\n",
|
||||
gpg_strerror (rc));
|
||||
wipememory (arg.unprotected_key, canlen);
|
||||
xfree (arg.unprotected_key);
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
agent_put_cache (hexgrip, cache_mode, pi->pin, 0);
|
||||
xfree (*keybuf);
|
||||
*keybuf = arg.unprotected_key;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* pksign.c - Generate a keypair
|
||||
/* genkey.c - Generate a keypair
|
||||
* Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
|
|
|
@ -92,8 +92,11 @@ enum cmd_and_opt_values
|
|||
oMinPassphraseLen,
|
||||
oMinPassphraseNonalpha,
|
||||
oCheckPassphrasePattern,
|
||||
oMaxPassphraseDays,
|
||||
oEnablePassphraseHistory,
|
||||
oUseStandardSocket,
|
||||
oNoUseStandardSocket,
|
||||
oFakedSystemTime,
|
||||
|
||||
oIgnoreCacheForSigning,
|
||||
oAllowMarkTrusted,
|
||||
|
@ -137,6 +140,7 @@ static ARGPARSE_OPTS opts[] = {
|
|||
{ oScdaemonProgram, "scdaemon-program", 2 ,
|
||||
N_("|PGM|use PGM as the SCdaemon program") },
|
||||
{ oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
|
||||
{ oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
|
||||
|
||||
{ oDisplay, "display", 2, "@" },
|
||||
{ oTTYname, "ttyname", 2, "@" },
|
||||
|
@ -157,6 +161,8 @@ static ARGPARSE_OPTS opts[] = {
|
|||
{ oMinPassphraseLen, "min-passphrase-len", 4, "@" },
|
||||
{ oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
|
||||
{ oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
|
||||
{ oMaxPassphraseDays, "max-passphrase-days", 4, "@" },
|
||||
{ oEnablePassphraseHistory, "enable-passphrase-history", 0, "@" },
|
||||
|
||||
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
|
||||
N_("do not use the PIN cache when signing")},
|
||||
|
@ -177,6 +183,7 @@ static ARGPARSE_OPTS opts[] = {
|
|||
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
|
||||
#define MIN_PASSPHRASE_LEN (8)
|
||||
#define MIN_PASSPHRASE_NONALPHA (1)
|
||||
#define MAX_PASSPHRASE_DAYS (0)
|
||||
|
||||
/* The timer tick used for housekeeping stuff. For Windows we use a
|
||||
longer period as the SetWaitableTimer seems to signal earlier than
|
||||
|
@ -375,6 +382,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
|
|||
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
|
||||
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
|
||||
opt.check_passphrase_pattern = NULL;
|
||||
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
|
||||
opt.enable_passhrase_history = 0;
|
||||
opt.ignore_cache_for_signing = 0;
|
||||
opt.allow_mark_trusted = 0;
|
||||
opt.disable_scdaemon = 0;
|
||||
|
@ -424,6 +433,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
|
|||
case oCheckPassphrasePattern:
|
||||
opt.check_passphrase_pattern = pargs->r.ret_str;
|
||||
break;
|
||||
case oMaxPassphraseDays:
|
||||
opt.max_passphrase_days = pargs->r.ret_ulong;
|
||||
break;
|
||||
case oEnablePassphraseHistory:
|
||||
opt.enable_passhrase_history = 1;
|
||||
break;
|
||||
|
||||
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
|
||||
|
||||
|
@ -646,6 +661,15 @@ main (int argc, char **argv )
|
|||
case oUseStandardSocket: standard_socket = 1; break;
|
||||
case oNoUseStandardSocket: standard_socket = 0; break;
|
||||
|
||||
case oFakedSystemTime:
|
||||
{
|
||||
time_t faked_time = isotime2epoch (pargs.r.ret_str);
|
||||
if (faked_time == (time_t)(-1))
|
||||
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
|
||||
gnupg_set_time (faked_time, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case oKeepTTY: opt.keep_tty = 1; break;
|
||||
case oKeepDISPLAY: opt.keep_display = 1; break;
|
||||
|
||||
|
@ -753,6 +777,11 @@ main (int argc, char **argv )
|
|||
MIN_PASSPHRASE_NONALPHA);
|
||||
printf ("check-passphrase-pattern:%lu:\n",
|
||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("max-passphrase-days:%lu:%d:\n",
|
||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
|
||||
MAX_PASSPHRASE_DAYS);
|
||||
printf ("enable-passphrase-history:%lu:\n",
|
||||
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("no-grab:%lu:\n",
|
||||
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("ignore-cache-for-signing:%lu:\n",
|
||||
|
|
|
@ -69,6 +69,7 @@ A protected key is like this:
|
|||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
(e #010001#)
|
||||
(protected mode (parms) encrypted_octet_string)
|
||||
(protected-at <isotimestamp>)
|
||||
)
|
||||
(uri http://foo.bar x-foo:whatever_you_want)
|
||||
(comment whatever)
|
||||
|
@ -79,7 +80,8 @@ In this scheme the encrypted_octet_string is encrypted according to
|
|||
the algorithm described after the keyword protected; most protection
|
||||
algorithms need some parameters, which are given in a list before the
|
||||
encrypted_octet_string. The result of the decryption process is a
|
||||
list of the secret key parameters.
|
||||
list of the secret key parameters. The protected-at expression is
|
||||
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
|
||||
|
||||
The only available protection mode for now is
|
||||
|
||||
|
@ -110,12 +112,13 @@ representation) after decryption:
|
|||
)
|
||||
|
||||
For padding reasons, random bytes are appended to this list - they can
|
||||
easily be stripped by looking for the end of the list.
|
||||
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
|
||||
concatenation of these 6 canonical encoded lists for RSA, including
|
||||
the parenthesis and the algorithm keyword.
|
||||
the parenthesis, the algorithm keyword and (if used) the protected-at
|
||||
list.
|
||||
|
||||
(rsa
|
||||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
|
@ -124,6 +127,7 @@ the parenthesis and the algorithm keyword.
|
|||
(p #00e861b..[some bytes not shown]..f1#)
|
||||
(q #00f7a7c..[some bytes not shown]..61#)
|
||||
(u #304559a..[some bytes not shown]..9b#)
|
||||
(protected-at "18950523T000000")
|
||||
)
|
||||
|
||||
After decryption the hash must be recalculated and compared against
|
||||
|
|
|
@ -366,12 +366,14 @@ read_and_unprotect (const char *fname)
|
|||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char *pw;
|
||||
|
||||
gnupg_isotime_t protected_at;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)), &result, &resultlen);
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
|
||||
protected_at, &result, &resultlen);
|
||||
release_passphrase (pw);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
|
@ -381,7 +383,12 @@ read_and_unprotect (const char *fname)
|
|||
log_error ("unprotecting the key failed: %s\n", gpg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("key protection done at %.4s-%.2s-%.2s %.2s:%.2s:%s\n",
|
||||
protected_at, protected_at+4, protected_at+6,
|
||||
protected_at+9, protected_at+11, protected_at+13);
|
||||
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
|
@ -883,7 +890,8 @@ export_p12_file (const char *fname)
|
|||
unsigned char *tmpkey;
|
||||
size_t tmplen;
|
||||
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)), &tmpkey, &tmplen);
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
|
||||
NULL, &tmpkey, &tmplen);
|
||||
release_passphrase (pw);
|
||||
if (rc)
|
||||
{
|
||||
|
|
141
agent/protect.c
141
agent/protect.c
|
@ -163,7 +163,7 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||
((<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 */
|
||||
encrypt only what is needed for a full blocksize. */
|
||||
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
||||
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
|
||||
enclen = outlen/blklen * blklen;
|
||||
|
@ -229,21 +229,13 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||
encrypted_octet_string)
|
||||
|
||||
in canoncical format of course. We use asprintf and %n modifier
|
||||
and spaces as palceholders. */
|
||||
asprintf (&p,
|
||||
"(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
|
||||
(int)strlen (modestr), modestr,
|
||||
&saltpos,
|
||||
blklen, &ivpos, blklen, "",
|
||||
enclen, &encpos, enclen, "");
|
||||
if (p)
|
||||
{ /* asprintf does not use our malloc system */
|
||||
char *psave = p;
|
||||
p = xtrymalloc (strlen (psave)+1);
|
||||
if (p)
|
||||
strcpy (p, psave);
|
||||
free (psave);
|
||||
}
|
||||
and dummy values as placeholders. */
|
||||
p = xtryasprintf
|
||||
("(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
|
||||
(int)strlen (modestr), modestr,
|
||||
&saltpos,
|
||||
blklen, &ivpos, blklen, "",
|
||||
enclen, &encpos, enclen, "");
|
||||
if (!p)
|
||||
{
|
||||
gpg_error_t tmperr = out_of_core ();
|
||||
|
@ -276,11 +268,19 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||
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;
|
||||
|
||||
/* Create an S-expression with the procted-at timestamp. */
|
||||
memcpy (timestamp_exp, "(12:protected-at15:", 19);
|
||||
gnupg_get_isotime (timestamp_exp+19);
|
||||
timestamp_exp[19+15] = ')';
|
||||
|
||||
/* Parse original key. */
|
||||
s = plainkey;
|
||||
if (*s != '(')
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
|
@ -345,8 +345,18 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||
assert (!depth);
|
||||
real_end = s-1;
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
|
||||
hash_begin, hash_end - hash_begin + 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,
|
||||
|
@ -356,10 +366,12 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||
|
||||
/* Now create the protected version of the key. Note that the 10
|
||||
extra bytes are for for the inserted "protected-" string (the
|
||||
beginning of the plaintext reads: "((11:private-key(" ). */
|
||||
beginning of the plaintext reads: "((11:private-key(" ). The 35
|
||||
term is the space for (12:protected-at15:<timestamp>). */
|
||||
*resultlen = (10
|
||||
+ (prot_begin-plainkey)
|
||||
+ protectedlen
|
||||
+ 35
|
||||
+ (real_end-prot_end));
|
||||
*result = p = xtrymalloc (*resultlen);
|
||||
if (!p)
|
||||
|
@ -374,10 +386,15 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||
p += prot_begin - plainkey - 4;
|
||||
memcpy (p, protected, protectedlen);
|
||||
p += protectedlen;
|
||||
|
||||
memcpy (p, timestamp_exp, 35);
|
||||
p += 35;
|
||||
|
||||
memcpy (p, prot_end+1, real_end - prot_end);
|
||||
p += real_end - prot_end;
|
||||
assert ( p - *result == *resultlen);
|
||||
xfree (protected);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -457,13 +474,16 @@ 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. */
|
||||
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. */
|
||||
static int
|
||||
merge_lists (const unsigned char *protectedkey,
|
||||
size_t replacepos,
|
||||
const unsigned char *cleartext,
|
||||
unsigned char *sha1hash,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
unsigned char **result, size_t *resultlen,
|
||||
size_t *cutoff, size_t *cutlen)
|
||||
{
|
||||
size_t n, newlistlen;
|
||||
unsigned char *newlist, *p;
|
||||
|
@ -473,6 +493,8 @@ merge_lists (const unsigned char *protectedkey,
|
|||
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
*cutoff = 0;
|
||||
*cutlen = 0;
|
||||
|
||||
if (replacepos < 26)
|
||||
return gpg_error (GPG_ERR_BUG);
|
||||
|
@ -522,7 +544,7 @@ merge_lists (const unsigned char *protectedkey,
|
|||
goto invalid_sexp;
|
||||
endpos = s;
|
||||
s++;
|
||||
/* short intermezzo: Get the MIC */
|
||||
/* Intermezzo: Get the MIC */
|
||||
if (*s != '(')
|
||||
goto invalid_sexp;
|
||||
s++;
|
||||
|
@ -539,13 +561,13 @@ merge_lists (const unsigned char *protectedkey,
|
|||
s += n;
|
||||
if (*s != ')')
|
||||
goto invalid_sexp;
|
||||
/* end intermezzo */
|
||||
/* End intermezzo */
|
||||
|
||||
/* append the parameter list */
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
/* skip overt the protected list element in the original list */
|
||||
/* Skip over the protected list element in the original list. */
|
||||
s = protectedkey + replacepos;
|
||||
assert (*s == '(');
|
||||
s++;
|
||||
|
@ -553,6 +575,22 @@ merge_lists (const unsigned char *protectedkey,
|
|||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
/* Record the position of the optional protected-at expression. */
|
||||
if (*s == '(')
|
||||
{
|
||||
const unsigned char *save_s = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (smatch (&s, n, "protected-at"))
|
||||
{
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
*cutlen = s - save_s;
|
||||
}
|
||||
s = save_s;
|
||||
}
|
||||
startpos = s;
|
||||
i = 2; /* we are inside this level */
|
||||
rc = sskip (&s, &i);
|
||||
|
@ -561,9 +599,12 @@ merge_lists (const unsigned char *protectedkey,
|
|||
assert (s[-1] == ')');
|
||||
endpos = s; /* one behind the end of the list */
|
||||
|
||||
/* append the rest */
|
||||
/* Append the rest. */
|
||||
if (*cutlen)
|
||||
*cutoff = p - newlist;
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
|
||||
/* ready */
|
||||
*result = newlist;
|
||||
|
@ -584,13 +625,16 @@ merge_lists (const unsigned char *protectedkey,
|
|||
|
||||
|
||||
/* Unprotect the key encoded in canonical format. We assume a valid
|
||||
S-Exp here. */
|
||||
S-Exp here. If a protected-at item is available, its value will
|
||||
be stored at protocted_at unless this is NULL. */
|
||||
int
|
||||
agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
gnupg_isotime_t protected_at,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int rc;
|
||||
const unsigned char *s;
|
||||
const unsigned char *protect_list;
|
||||
size_t n;
|
||||
int infidx, i;
|
||||
unsigned char sha1hash[20], sha1hash2[20];
|
||||
|
@ -601,6 +645,10 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
|||
unsigned char *cleartext;
|
||||
unsigned char *final;
|
||||
size_t finallen;
|
||||
size_t cutoff, cutlen;
|
||||
|
||||
if (protected_at)
|
||||
*protected_at = 0;
|
||||
|
||||
s = protectedkey;
|
||||
if (*s != '(')
|
||||
|
@ -624,12 +672,44 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
|||
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)
|
||||
{
|
||||
while (*s == '(')
|
||||
{
|
||||
prot_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
if (smatch (&s, n, "protected-at"))
|
||||
{
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
if (n != 15)
|
||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||
memcpy (protected_at, s, 15);
|
||||
protected_at[15] = 0;
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now find the list with the protected information. Here is an
|
||||
example for such a list:
|
||||
(protected openpgp-s2k3-sha1-aes-cbc
|
||||
((sha1 <salt> <count>) <Initialization_Vector>)
|
||||
<encrypted_data>)
|
||||
*/
|
||||
s = protect_list;
|
||||
for (;;)
|
||||
{
|
||||
if (*s != '(')
|
||||
|
@ -700,7 +780,7 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
|||
return rc;
|
||||
|
||||
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
|
||||
sha1hash, &final, &finallen);
|
||||
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. */
|
||||
|
@ -718,6 +798,13 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
|||
xfree (final);
|
||||
return rc;
|
||||
}
|
||||
/* Now remove tha part which is included in the MIC but should not
|
||||
go into the final thing. */
|
||||
if (cutlen)
|
||||
{
|
||||
memmove (final+cutoff, final+cutoff+cutlen, finallen-cutoff-cutlen);
|
||||
finallen -= cutlen;
|
||||
}
|
||||
|
||||
*result = final;
|
||||
*resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue