aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agent/ChangeLog18
-rw-r--r--agent/agent.h12
-rw-r--r--agent/command.c2
-rw-r--r--agent/findkey.c115
-rw-r--r--agent/genkey.c2
-rw-r--r--agent/gpg-agent.c29
-rw-r--r--agent/keyformat.txt10
-rw-r--r--agent/protect-tool.c16
-rw-r--r--agent/protect.c141
9 files changed, 300 insertions, 45 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 4b8d5d3b1..46f1a25d8 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,21 @@
+2007-08-28 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* gpg-agent.c: Add options --min-passphrase-nonalpha,
diff --git a/agent/agent.h b/agent/agent.h
index 8531f395a..c37a22c3d 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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);
diff --git a/agent/command.c b/agent/command.c
index 56492e107..bb3d52fbb 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -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;
diff --git a/agent/findkey.c b/agent/findkey.c
index d8dc52696..183af2c5e 100644
--- a/agent/findkey.c
+++ b/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;
diff --git a/agent/genkey.c b/agent/genkey.c
index 09cd9f738..11b093d86 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -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.
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 453e9a9d5..64424d975 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -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",
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 2fa53adba..e246e888c 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -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
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index b12b3809d..0e062a230 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -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)
{
diff --git a/agent/protect.c b/agent/protect.c
index ce723fe9a..ebb02ac89 100644
--- a/agent/protect.c
+++ b/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);