mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
Exporting secret keys via gpg-agent is now basically supported.
A couple of forward ported changes. Doc updates.
This commit is contained in:
parent
aeb31977ec
commit
bfbd80feb9
72 changed files with 1930 additions and 546 deletions
|
@ -1,3 +1,47 @@
|
|||
2010-09-30 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c (agent_exit): Run cleanup.
|
||||
(cleanup): Run only once.
|
||||
|
||||
* call-pinentry.c (close_button_status_cb): New.
|
||||
(agent_askpin): Add arg R_CANCEL_ALL. Change all callers.
|
||||
* genkey.c (agent_ask_new_passphrase): Ditto.
|
||||
* findkey.c (unprotect): Return GPG_ERR_FULLY_CANCELED if needed.
|
||||
|
||||
* command.c (cmd_export_key): Add support for OpenPGP keys.
|
||||
* findkey.c (unprotect): Add optional arg R_PASSPHRASE.
|
||||
(agent_key_from_file): Ditto. Change all callers.
|
||||
|
||||
* findkey.c (unprotect): Do not put the passphrase into the cache
|
||||
if it has been changed.
|
||||
|
||||
* cvt-openpgp.c (convert_to_openpgp, apply_protection)
|
||||
(key_from_sexp): New.
|
||||
|
||||
2010-09-29 Werner Koch <wk@g10code.com>
|
||||
|
||||
* cvt-openpgp.c (convert_openpgp): Rename to convert_from_openpgp.
|
||||
|
||||
* command.c (has_option): Stop at "--".
|
||||
(has_option_name, option_value): Ditto.
|
||||
(skip_options): Skip initial spaces.
|
||||
|
||||
2010-09-24 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c (main, reread_configuration): Always test whether
|
||||
the default configuration file has been created in the meantime.
|
||||
Fixes bug#1285.
|
||||
|
||||
2010-09-17 Werner Koch <wk@g10code.com>
|
||||
|
||||
* command.c (cmd_havekey): Allow testing of several keygrips.
|
||||
|
||||
2010-09-15 Werner Koch <wk@g10code.com>
|
||||
|
||||
* protect.c (calculate_mic): Take care of shared secret format.
|
||||
|
||||
* agent.h (PROTECTED_SHARED_SECRET): New.
|
||||
|
||||
2010-09-02 Werner Koch <wk@g10code.com>
|
||||
|
||||
* cache.c (new_data): Change arg and callers to use a string and
|
||||
|
|
|
@ -182,7 +182,8 @@ enum
|
|||
PRIVATE_KEY_UNKNOWN = 0,
|
||||
PRIVATE_KEY_CLEAR = 1,
|
||||
PRIVATE_KEY_PROTECTED = 2,
|
||||
PRIVATE_KEY_SHADOWED = 3
|
||||
PRIVATE_KEY_SHADOWED = 3,
|
||||
PROTECTED_SHARED_SECRET = 4
|
||||
};
|
||||
|
||||
|
||||
|
@ -233,7 +234,8 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl,
|
|||
unsigned char **shadow_info,
|
||||
cache_mode_t cache_mode,
|
||||
lookup_ttl_t lookup_ttl,
|
||||
gcry_sexp_t *result);
|
||||
gcry_sexp_t *result,
|
||||
char **r_passphrase);
|
||||
gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result);
|
||||
|
@ -251,7 +253,7 @@ int pinentry_active_p (ctrl_t ctrl, int waitseconds);
|
|||
int agent_askpin (ctrl_t ctrl,
|
||||
const char *desc_text, const char *prompt_text,
|
||||
const char *inital_errtext,
|
||||
struct pin_entry_info_s *pininfo);
|
||||
struct pin_entry_info_s *pininfo, int *r_cancelall);
|
||||
int agent_get_passphrase (ctrl_t ctrl, char **retpass,
|
||||
const char *desc, const char *prompt,
|
||||
const char *errtext, int with_qualitybar);
|
||||
|
@ -289,7 +291,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
|
|||
/*-- genkey.c --*/
|
||||
int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
|
||||
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
||||
char **r_passphrase);
|
||||
char **r_passphrase, int *r_cancelall);
|
||||
int agent_genkey (ctrl_t ctrl, const char *cache_nonce,
|
||||
const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
|
||||
int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
|
||||
|
|
|
@ -696,6 +696,29 @@ setup_qualitybar (void)
|
|||
}
|
||||
|
||||
|
||||
/* Check the button_info line for a close action. */
|
||||
static gpg_error_t
|
||||
close_button_status_cb (void *opaque, const char *line)
|
||||
{
|
||||
int *flag = opaque;
|
||||
const char *keyword = line;
|
||||
int keywordlen;
|
||||
|
||||
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
|
||||
;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
if (keywordlen == 11 && !memcmp (keyword, "BUTTON_INFO", keywordlen))
|
||||
{
|
||||
if ( !strcmp (line, "close") )
|
||||
*flag = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Call the Entry and ask for the PIN. We do check for a valid PIN
|
||||
number here and repeat it as long as we have invalid formed
|
||||
|
@ -704,7 +727,7 @@ int
|
|||
agent_askpin (ctrl_t ctrl,
|
||||
const char *desc_text, const char *prompt_text,
|
||||
const char *initial_errtext,
|
||||
struct pin_entry_info_s *pininfo)
|
||||
struct pin_entry_info_s *pininfo, int *r_cancel_all)
|
||||
{
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
|
@ -712,6 +735,10 @@ agent_askpin (ctrl_t ctrl,
|
|||
const char *errtext = NULL;
|
||||
int is_pin = 0;
|
||||
int saveflag;
|
||||
int close_button;
|
||||
|
||||
if (r_cancel_all)
|
||||
*r_cancel_all = 0;
|
||||
|
||||
if (opt.batch)
|
||||
return 0; /* fixme: we should return BAD PIN */
|
||||
|
@ -791,8 +818,10 @@ agent_askpin (ctrl_t ctrl,
|
|||
|
||||
saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
|
||||
assuan_begin_confidential (entry_ctx);
|
||||
close_button = 0;
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
|
||||
inq_quality, entry_ctx, NULL, NULL);
|
||||
inq_quality, entry_ctx,
|
||||
close_button_status_cb, &close_button);
|
||||
assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
|
||||
/* Most pinentries out in the wild return the old Assuan error code
|
||||
for canceled which gets translated to an assuan Cancel error and
|
||||
|
@ -801,6 +830,11 @@ agent_askpin (ctrl_t ctrl,
|
|||
&& gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
|
||||
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
|
||||
|
||||
/* Set a flag in case the window close button was clicked to
|
||||
cancel the operation. */
|
||||
if (close_button && r_cancel_all && gpg_err_code (rc) == GPG_ERR_CANCELED)
|
||||
*r_cancel_all = 1;
|
||||
|
||||
if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
|
||||
errtext = is_pin? _("PIN too long")
|
||||
: _("Passphrase too long");
|
||||
|
|
|
@ -2425,7 +2425,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
|
|||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
next_try:
|
||||
err = agent_askpin (ctrl, description, NULL, initial_errtext, pi);
|
||||
err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL);
|
||||
initial_errtext = NULL;
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -2433,7 +2433,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
|
|||
/* Unless the passphrase is empty, ask to confirm it. */
|
||||
if (pi->pin && *pi->pin)
|
||||
{
|
||||
err = agent_askpin (ctrl, description2, NULL, NULL, pi2);
|
||||
err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL);
|
||||
if (err == -1)
|
||||
{ /* The re-entered one did not match and the user did not
|
||||
hit cancel. */
|
||||
|
|
151
agent/command.c
151
agent/command.c
|
@ -169,6 +169,23 @@ reset_notify (assuan_context_t ctx, char *line)
|
|||
}
|
||||
|
||||
|
||||
/* Skip over options.
|
||||
Blanks after the options are also removed. */
|
||||
static char *
|
||||
skip_options (const char *line)
|
||||
{
|
||||
while (spacep (line))
|
||||
line++;
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
return (char*)line;
|
||||
}
|
||||
|
||||
/* Check whether the option NAME appears in LINE */
|
||||
static int
|
||||
has_option (const char *line, const char *name)
|
||||
|
@ -177,6 +194,8 @@ has_option (const char *line, const char *name)
|
|||
int n = strlen (name);
|
||||
|
||||
s = strstr (line, name);
|
||||
if (s && s >= skip_options (line))
|
||||
return 0;
|
||||
return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
|
||||
}
|
||||
|
||||
|
@ -190,6 +209,8 @@ has_option_name (const char *line, const char *name)
|
|||
int n = strlen (name);
|
||||
|
||||
s = strstr (line, name);
|
||||
if (s && s >= skip_options (line))
|
||||
return 0;
|
||||
return (s && (s == line || spacep (s-1))
|
||||
&& (!s[n] || spacep (s+n) || s[n] == '='));
|
||||
}
|
||||
|
@ -203,6 +224,8 @@ option_value (const char *line, const char *name)
|
|||
int n = strlen (name);
|
||||
|
||||
s = strstr (line, name);
|
||||
if (s && s >= skip_options (line))
|
||||
return NULL;
|
||||
if (s && (s == line || spacep (s-1))
|
||||
&& s[n] && (spacep (s+n) || s[n] == '='))
|
||||
{
|
||||
|
@ -215,23 +238,6 @@ option_value (const char *line, const char *name)
|
|||
}
|
||||
|
||||
|
||||
/* Skip over options. It is assumed that leading spaces have been
|
||||
removed (this is the case for lines passed to a handler from
|
||||
assuan). Blanks after the options are also removed. */
|
||||
static char *
|
||||
skip_options (char *line)
|
||||
{
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
/* Replace all '+' by a blank. */
|
||||
static void
|
||||
plus_to_blank (char *s)
|
||||
|
@ -530,23 +536,35 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
|
|||
|
||||
|
||||
static const char hlp_havekey[] =
|
||||
"HAVEKEY <hexstring_with_keygrip>\n"
|
||||
"HAVEKEY <hexstrings_with_keygrips>\n"
|
||||
"\n"
|
||||
"Return success when the secret key is available.";
|
||||
"Return success if at least one of the secret keys with the given\n"
|
||||
"keygrips is available.";
|
||||
static gpg_error_t
|
||||
cmd_havekey (assuan_context_t ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
gpg_error_t err;
|
||||
unsigned char buf[20];
|
||||
|
||||
rc = parse_keygrip (ctx, line, buf);
|
||||
if (rc)
|
||||
return rc;
|
||||
do
|
||||
{
|
||||
err = parse_keygrip (ctx, line, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!agent_key_available (buf))
|
||||
return 0; /* Found. */
|
||||
|
||||
if (agent_key_available (buf))
|
||||
return gpg_error (GPG_ERR_NO_SECKEY);
|
||||
|
||||
return 0;
|
||||
while (*line && *line != ' ' && *line != '\t')
|
||||
line++;
|
||||
while (*line == ' ' || *line == '\t')
|
||||
line++;
|
||||
}
|
||||
while (*line);
|
||||
|
||||
/* No leave_cmd() here because errors are expected and would clutter
|
||||
the log. */
|
||||
return gpg_error (GPG_ERR_NO_SECKEY);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1316,9 +1334,14 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
|||
ctrl->in_passwd++;
|
||||
rc = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc,
|
||||
grip, &shadow_info, CACHE_MODE_IGNORE, NULL,
|
||||
&s_skey);
|
||||
&s_skey, NULL);
|
||||
if (rc)
|
||||
;
|
||||
{
|
||||
/* Not all users of gpg-agent know about fully cancled; thus we
|
||||
map it back. */
|
||||
if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
|
||||
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
|
||||
}
|
||||
else if (!s_skey)
|
||||
{
|
||||
log_error ("changing a smartcard PIN is not yet supported\n");
|
||||
|
@ -1590,9 +1613,9 @@ cmd_import_key (assuan_context_t ctx, char *line)
|
|||
used to protect the key using the same code as for regular
|
||||
key import. */
|
||||
|
||||
err = convert_openpgp (ctrl, openpgp_sexp, grip,
|
||||
ctrl->server_local->keydesc, cache_nonce,
|
||||
&key, &passphrase);
|
||||
err = convert_from_openpgp (ctrl, openpgp_sexp, grip,
|
||||
ctrl->server_local->keydesc, cache_nonce,
|
||||
&key, &passphrase);
|
||||
if (err)
|
||||
goto leave;
|
||||
realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
|
||||
|
@ -1620,7 +1643,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
|
|||
err = agent_ask_new_passphrase
|
||||
(ctrl, _("Please enter the passphrase to protect the "
|
||||
"imported object within the GnuPG system."),
|
||||
&passphrase);
|
||||
&passphrase, NULL);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
@ -1650,7 +1673,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
|
|||
|
||||
|
||||
static const char hlp_export_key[] =
|
||||
"EXPORT_KEY <hexstring_with_keygrip>\n"
|
||||
"EXPORT_KEY [--cache-nonce=<nonce>] [--openpgp] <hexstring_with_keygrip>\n"
|
||||
"\n"
|
||||
"Export a secret key from the key store. The key will be encrypted\n"
|
||||
"using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n"
|
||||
|
@ -1668,6 +1691,26 @@ cmd_export_key (assuan_context_t ctx, char *line)
|
|||
gcry_cipher_hd_t cipherhd = NULL;
|
||||
unsigned char *wrappedkey = NULL;
|
||||
size_t wrappedkeylen;
|
||||
int openpgp;
|
||||
char *cache_nonce;
|
||||
char *passphrase = NULL;
|
||||
|
||||
openpgp = has_option (line, "--openpgp");
|
||||
cache_nonce = option_value (line, "--cache-nonce");
|
||||
if (cache_nonce)
|
||||
{
|
||||
for (; *line && !spacep (line); line++)
|
||||
;
|
||||
if (*line)
|
||||
*line++ = '\0';
|
||||
cache_nonce = xtrystrdup (cache_nonce);
|
||||
if (!cache_nonce)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
line = skip_options (line);
|
||||
|
||||
if (!ctrl->server_local->export_key)
|
||||
{
|
||||
|
@ -1685,8 +1728,11 @@ cmd_export_key (assuan_context_t ctx, char *line)
|
|||
goto leave;
|
||||
}
|
||||
|
||||
/* Get the key from the file. With the openpgp flag we also ask for
|
||||
the passphrase so that we can use it to re-encrypt it. */
|
||||
err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip,
|
||||
NULL, CACHE_MODE_IGNORE, NULL, &s_skey);
|
||||
NULL, CACHE_MODE_IGNORE, NULL, &s_skey,
|
||||
openpgp ? &passphrase : NULL);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (!s_skey)
|
||||
|
@ -1697,8 +1743,33 @@ cmd_export_key (assuan_context_t ctx, char *line)
|
|||
err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = make_canon_sexp_pad (s_skey, 1, &key, &keylen);
|
||||
|
||||
if (openpgp)
|
||||
{
|
||||
/* The openpgp option changes the key format into the OpenPGP
|
||||
key transfer format. The result is already a padded
|
||||
canonical S-expression. */
|
||||
if (!passphrase)
|
||||
{
|
||||
int fully_canceled;
|
||||
err = agent_ask_new_passphrase
|
||||
(ctrl, _("This key (or subkey) is not protected with a passphrase."
|
||||
" Please enter a new passphrase to export it."),
|
||||
&passphrase, &fully_canceled);
|
||||
if (err)
|
||||
{
|
||||
if (fully_canceled)
|
||||
err = gpg_error (GPG_ERR_FULLY_CANCELED);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
err = convert_to_openpgp (ctrl, s_skey, passphrase, &key, &keylen);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert into a canonical S-expression and wrap that. */
|
||||
err = make_canon_sexp_pad (s_skey, 1, &key, &keylen);
|
||||
}
|
||||
if (err)
|
||||
goto leave;
|
||||
gcry_sexp_release (s_skey);
|
||||
|
@ -1735,12 +1806,18 @@ cmd_export_key (assuan_context_t ctx, char *line)
|
|||
|
||||
|
||||
leave:
|
||||
xfree (passphrase);
|
||||
xfree (wrappedkey);
|
||||
gcry_cipher_close (cipherhd);
|
||||
xfree (key);
|
||||
gcry_sexp_release (s_skey);
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
ctrl->server_local->keydesc = NULL;
|
||||
|
||||
/* Not all users of gpg-agent know about fully cancled; thus we map
|
||||
it back unless we know that it is okay. */
|
||||
if (!openpgp && gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
|
||||
err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
|
|
@ -525,10 +525,10 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
|
|||
pointed to by GRIP. On error NULL is stored at all return
|
||||
arguments. */
|
||||
gpg_error_t
|
||||
convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
||||
unsigned char *grip, const char *prompt,
|
||||
const char *cache_nonce,
|
||||
unsigned char **r_key, char **r_passphrase)
|
||||
convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
||||
unsigned char *grip, const char *prompt,
|
||||
const char *cache_nonce,
|
||||
unsigned char **r_key, char **r_passphrase)
|
||||
{
|
||||
gpg_error_t err;
|
||||
gcry_sexp_t top_list;
|
||||
|
@ -779,7 +779,7 @@ convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
|||
err = try_do_unprotect_cb (pi);
|
||||
}
|
||||
if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
|
||||
err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
|
||||
err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL);
|
||||
skeyidx = pi_arg.skeyidx;
|
||||
if (!err)
|
||||
{
|
||||
|
@ -824,4 +824,267 @@ convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
|||
}
|
||||
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
gcry_sexp_t l2;
|
||||
int idx;
|
||||
|
||||
for (idx=0; *elems; elems++, idx++)
|
||||
{
|
||||
l2 = gcry_sexp_find_token (sexp, elems, 1);
|
||||
if (!l2)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found. */
|
||||
goto leave;
|
||||
}
|
||||
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l2);
|
||||
if (!array[idx])
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid. */
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
if (err)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < idx; i++)
|
||||
{
|
||||
gcry_mpi_release (array[i]);
|
||||
array[i] = NULL;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Given an ARRAY of mpis with the key parameters, protect the secret
|
||||
parameters in that array and replace them by one opaque encoded
|
||||
mpi. NPKEY is the number of public key parameters and NSKEY is
|
||||
the number of secret key parameters (including the public ones).
|
||||
On success the array will have NPKEY+1 elements. */
|
||||
static gpg_error_t
|
||||
apply_protection (gcry_mpi_t *array, int npkey, int nskey,
|
||||
const char *passphrase,
|
||||
int protect_algo, void *protect_iv, size_t protect_ivlen,
|
||||
int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int i, j;
|
||||
gcry_cipher_hd_t cipherhd;
|
||||
unsigned char *bufarr[10];
|
||||
size_t narr[10];
|
||||
unsigned int nbits[10];
|
||||
int ndata;
|
||||
unsigned char *p, *data;
|
||||
|
||||
assert (npkey < nskey);
|
||||
assert (nskey < DIM (bufarr));
|
||||
|
||||
/* Collect only the secret key parameters into BUFARR et al and
|
||||
compute the required size of the data buffer. */
|
||||
ndata = 20; /* Space for the SHA-1 checksum. */
|
||||
for (i = npkey, j = 0; i < nskey; i++, j++ )
|
||||
{
|
||||
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
|
||||
if (err)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
for (i = 0; i < j; i++)
|
||||
xfree (bufarr[i]);
|
||||
return err;
|
||||
}
|
||||
nbits[j] = gcry_mpi_get_nbits (array[i]);
|
||||
ndata += 2 + narr[j];
|
||||
}
|
||||
|
||||
/* Allocate data buffer and stuff it with the secret key parameters. */
|
||||
data = xtrymalloc_secure (ndata);
|
||||
if (!data)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
for (i = 0; i < (nskey-npkey); i++ )
|
||||
xfree (bufarr[i]);
|
||||
return err;
|
||||
}
|
||||
p = data;
|
||||
for (i = 0; i < (nskey-npkey); i++ )
|
||||
{
|
||||
*p++ = nbits[i] >> 8 ;
|
||||
*p++ = nbits[i];
|
||||
memcpy (p, bufarr[i], narr[i]);
|
||||
p += narr[i];
|
||||
xfree (bufarr[i]);
|
||||
bufarr[i] = NULL;
|
||||
}
|
||||
assert (p == data + ndata - 20);
|
||||
|
||||
/* Append a hash of the secret key parameters. */
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20);
|
||||
|
||||
/* Encrypt it. */
|
||||
err = gcry_cipher_open (&cipherhd, protect_algo,
|
||||
GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
|
||||
if (!err)
|
||||
err = hash_passphrase_and_set_key (passphrase, cipherhd, protect_algo,
|
||||
s2k_mode, s2k_algo, s2k_salt, s2k_count);
|
||||
if (!err)
|
||||
err = gcry_cipher_setiv (cipherhd, protect_iv, protect_ivlen);
|
||||
if (!err)
|
||||
err = gcry_cipher_encrypt (cipherhd, data, ndata, NULL, 0);
|
||||
gcry_cipher_close (cipherhd);
|
||||
if (err)
|
||||
{
|
||||
xfree (data);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Replace the secret key parameters in the array by one opaque value. */
|
||||
for (i = npkey; i < nskey; i++ )
|
||||
{
|
||||
gcry_mpi_release (array[i]);
|
||||
array[i] = NULL;
|
||||
}
|
||||
array[npkey] = gcry_mpi_set_opaque (NULL, data, ndata*8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Convert our key S_KEY into an OpenPGP key transfer format. On
|
||||
success a canonical encoded S-expression is stored at R_TRANSFERKEY
|
||||
and its length at R_TRANSFERKEYLEN; this S-expression is also
|
||||
padded to a multiple of 64 bits. */
|
||||
gpg_error_t
|
||||
convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
|
||||
unsigned char **r_transferkey, size_t *r_transferkeylen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
gcry_sexp_t list, l2;
|
||||
char *name;
|
||||
int algo;
|
||||
const char *algoname;
|
||||
const char *elems;
|
||||
int npkey, nskey;
|
||||
gcry_mpi_t array[10];
|
||||
char protect_iv[16];
|
||||
char salt[8];
|
||||
unsigned long s2k_count;
|
||||
int i, j;
|
||||
|
||||
(void)ctrl;
|
||||
|
||||
*r_transferkey = NULL;
|
||||
|
||||
for (i=0; i < DIM (array); i++)
|
||||
array[i] = NULL;
|
||||
|
||||
list = gcry_sexp_find_token (s_key, "private-key", 0);
|
||||
if (!list)
|
||||
return gpg_error (GPG_ERR_NO_OBJ); /* Does not contain a key object. */
|
||||
l2 = gcry_sexp_cadr (list);
|
||||
gcry_sexp_release (list);
|
||||
list = l2;
|
||||
name = gcry_sexp_nth_string (list, 0);
|
||||
if (!name)
|
||||
{
|
||||
gcry_sexp_release (list);
|
||||
return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
|
||||
}
|
||||
|
||||
algo = gcry_pk_map_name (name);
|
||||
xfree (name);
|
||||
|
||||
switch (algo)
|
||||
{
|
||||
case GCRY_PK_RSA: algoname = "rsa"; npkey = 2; elems = "nedpqu"; break;
|
||||
case GCRY_PK_ELG: algoname = "elg"; npkey = 3; elems = "pgyx"; break;
|
||||
case GCRY_PK_ELG_E: algoname = "elg"; npkey = 3; elems = "pgyx"; break;
|
||||
case GCRY_PK_DSA: algoname = "dsa"; npkey = 4; elems = "pqgyx"; break;
|
||||
case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break;
|
||||
default: algoname = ""; npkey = 0; elems = NULL; break;
|
||||
}
|
||||
assert (!elems || strlen (elems) < DIM (array) );
|
||||
nskey = elems? strlen (elems) : 0;
|
||||
|
||||
if (!elems)
|
||||
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
|
||||
else
|
||||
err = key_from_sexp (list, elems, array);
|
||||
gcry_sexp_release (list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
gcry_create_nonce (protect_iv, sizeof protect_iv);
|
||||
gcry_create_nonce (salt, sizeof salt);
|
||||
s2k_count = get_standard_s2k_count ();
|
||||
err = apply_protection (array, npkey, nskey, passphrase,
|
||||
GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
|
||||
3, GCRY_MD_SHA1, salt, s2k_count);
|
||||
/* Turn it into the transfer key S-expression. Note that we always
|
||||
return a protected key. */
|
||||
if (!err)
|
||||
{
|
||||
char countbuf[35];
|
||||
membuf_t mbuf;
|
||||
void *format_args_buf_ptr[1];
|
||||
int format_args_buf_int[1];
|
||||
void *format_args[10+2];
|
||||
size_t n;
|
||||
gcry_sexp_t tmpkey, tmpsexp;
|
||||
|
||||
snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
|
||||
|
||||
init_membuf (&mbuf, 50);
|
||||
put_membuf_str (&mbuf, "(skey");
|
||||
for (i=j=0; i < npkey; i++)
|
||||
{
|
||||
put_membuf_str (&mbuf, " _ %m");
|
||||
format_args[j++] = array + i;
|
||||
}
|
||||
put_membuf_str (&mbuf, " e %b");
|
||||
format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n);
|
||||
format_args_buf_int[0] = (n+7)/8;
|
||||
format_args[j++] = format_args_buf_int;
|
||||
format_args[j++] = format_args_buf_ptr;
|
||||
put_membuf_str (&mbuf, ")\n");
|
||||
put_membuf (&mbuf, "", 1);
|
||||
|
||||
tmpkey = NULL;
|
||||
{
|
||||
char *format = get_membuf (&mbuf, NULL);
|
||||
if (!format)
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args);
|
||||
xfree (format);
|
||||
}
|
||||
if (!err)
|
||||
err = gcry_sexp_build (&tmpsexp, NULL,
|
||||
"(openpgp-private-key\n"
|
||||
" (version 1:4)\n"
|
||||
" (algo %s)\n"
|
||||
" %S\n"
|
||||
" (protection sha1 aes %b 1:3 sha1 %b %s))\n",
|
||||
algoname,
|
||||
tmpkey,
|
||||
(int)sizeof protect_iv, protect_iv,
|
||||
(int)sizeof salt, salt,
|
||||
countbuf);
|
||||
gcry_sexp_release (tmpkey);
|
||||
if (!err)
|
||||
err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen);
|
||||
gcry_sexp_release (tmpsexp);
|
||||
}
|
||||
|
||||
for (i=0; i < DIM (array); i++)
|
||||
gcry_mpi_release (array[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,14 @@
|
|||
#ifndef GNUPG_AGENT_CVT_OPENPGP_H
|
||||
#define GNUPG_AGENT_CVT_OPENPGP_H
|
||||
|
||||
gpg_error_t convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
||||
unsigned char *grip, const char *prompt,
|
||||
const char *cache_nonce,
|
||||
unsigned char **r_key, char **r_passphrase);
|
||||
gpg_error_t convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
|
||||
unsigned char *grip, const char *prompt,
|
||||
const char *cache_nonce,
|
||||
unsigned char **r_key, char **r_passphrase);
|
||||
|
||||
gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key,
|
||||
const char *passphrase,
|
||||
unsigned char **r_transferkey,
|
||||
size_t *r_transferkeylen);
|
||||
|
||||
#endif /*GNUPG_AGENT_CVT_OPENPGP_H*/
|
||||
|
|
|
@ -266,7 +266,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
|
|||
|
||||
if (any_flags)
|
||||
{
|
||||
rc = agent_askpin (ctrl, info, prompt, again_text, pi);
|
||||
rc = agent_askpin (ctrl, info, prompt, again_text, pi, NULL);
|
||||
again_text = NULL;
|
||||
if (!rc && newpin)
|
||||
{
|
||||
|
@ -288,7 +288,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
|
|||
is_puk?
|
||||
_("Repeat this PUK"):
|
||||
_("Repeat this PIN")),
|
||||
prompt, NULL, pi2);
|
||||
prompt, NULL, pi2, NULL);
|
||||
if (!rc && strcmp (pi->pin, pi2->pin))
|
||||
{
|
||||
again_text = (resetcode?
|
||||
|
@ -312,7 +312,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
|
|||
info? info:"",
|
||||
info? "')":"") < 0)
|
||||
desc = NULL;
|
||||
rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi);
|
||||
rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi, NULL);
|
||||
xfree (desc);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ agent_write_private_key (const unsigned char *grip,
|
|||
}
|
||||
|
||||
|
||||
/* Callback function to try the unprotection from the passpharse query
|
||||
/* Callback function to try the unprotection from the passphrase query
|
||||
code. */
|
||||
static int
|
||||
try_unprotect_cb (struct pin_entry_info_s *pi)
|
||||
|
@ -273,11 +273,16 @@ modify_description (const char *in, const char *comment, char **result)
|
|||
should be the hex encoded keygrip of that key to be used with the
|
||||
caching mechanism. DESC_TEXT may be set to override the default
|
||||
description used for the pinentry. If LOOKUP_TTL is given this
|
||||
function is used to lookup the default ttl. */
|
||||
function is used to lookup the default ttl. If R_PASSPHRASE is not
|
||||
NULL, the function succeeded and the key was protected the used
|
||||
passphrase (entered or from the cache) is stored there; if not NULL
|
||||
will be stored. The caller needs to free the returned
|
||||
passphrase. */
|
||||
static int
|
||||
unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
||||
unsigned char **keybuf, const unsigned char *grip,
|
||||
cache_mode_t cache_mode, lookup_ttl_t lookup_ttl)
|
||||
cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
|
||||
char **r_passphrase)
|
||||
{
|
||||
struct pin_entry_info_s *pi;
|
||||
struct try_unprotect_arg_s arg;
|
||||
|
@ -285,6 +290,10 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char hexgrip[40+1];
|
||||
int fully_canceled;
|
||||
|
||||
if (r_passphrase)
|
||||
*r_passphrase = NULL;
|
||||
|
||||
bin2hex (grip, 20, hexgrip);
|
||||
|
||||
|
@ -297,13 +306,17 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||
if (pw)
|
||||
{
|
||||
rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
|
||||
xfree (pw);
|
||||
if (!rc)
|
||||
{
|
||||
if (r_passphrase)
|
||||
*r_passphrase = pw;
|
||||
else
|
||||
xfree (pw);
|
||||
xfree (*keybuf);
|
||||
*keybuf = result;
|
||||
return 0;
|
||||
}
|
||||
xfree (pw);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,13 +331,17 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||
if (pw)
|
||||
{
|
||||
rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
|
||||
xfree (pw);
|
||||
if (!rc)
|
||||
{
|
||||
if (r_passphrase)
|
||||
*r_passphrase = pw;
|
||||
else
|
||||
xfree (pw);
|
||||
xfree (*keybuf);
|
||||
*keybuf = result;
|
||||
return 0;
|
||||
}
|
||||
xfree (pw);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
|
@ -366,7 +383,9 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||
arg.change_required = 0;
|
||||
pi->check_cb_arg = &arg;
|
||||
|
||||
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
|
||||
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, &fully_canceled);
|
||||
if (gpg_err_code (rc) == GPG_ERR_CANCELED && fully_canceled)
|
||||
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
|
||||
if (!rc)
|
||||
{
|
||||
assert (arg.unprotected_key);
|
||||
|
@ -400,8 +419,13 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||
return rc;
|
||||
}
|
||||
}
|
||||
agent_put_cache (hexgrip, cache_mode, pi->pin,
|
||||
lookup_ttl? lookup_ttl (hexgrip) : 0);
|
||||
else
|
||||
{
|
||||
agent_put_cache (hexgrip, cache_mode, pi->pin,
|
||||
lookup_ttl? lookup_ttl (hexgrip) : 0);
|
||||
if (r_passphrase && *pi->pin)
|
||||
*r_passphrase = xtrystrdup (pi->pin);
|
||||
}
|
||||
xfree (*keybuf);
|
||||
*keybuf = arg.unprotected_key;
|
||||
}
|
||||
|
@ -501,13 +525,17 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
|
|||
not simply pass the TTL value because the value is only needed if
|
||||
an unprotect action was needed and looking up the TTL may have some
|
||||
overhead (e.g. scanning the sshcontrol file). If a CACHE_NONCE is
|
||||
given that cache item is first tried to get a passphrase. */
|
||||
given that cache item is first tried to get a passphrase. If
|
||||
R_PASSPHRASE is not NULL, the function succeeded and the key was
|
||||
protected the used passphrase (entered or from the cache) is stored
|
||||
there; if not NULL will be stored. The caller needs to free the
|
||||
returned passphrase. */
|
||||
gpg_error_t
|
||||
agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
||||
const char *desc_text,
|
||||
const unsigned char *grip, unsigned char **shadow_info,
|
||||
cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
|
||||
gcry_sexp_t *result)
|
||||
gcry_sexp_t *result, char **r_passphrase)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *buf;
|
||||
|
@ -518,6 +546,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
|||
*result = NULL;
|
||||
if (shadow_info)
|
||||
*shadow_info = NULL;
|
||||
if (r_passphrase)
|
||||
*r_passphrase = NULL;
|
||||
|
||||
rc = read_key_file (grip, &s_skey);
|
||||
if (rc)
|
||||
|
@ -579,7 +609,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
|||
if (!rc)
|
||||
{
|
||||
rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip,
|
||||
cache_mode, lookup_ttl);
|
||||
cache_mode, lookup_ttl, r_passphrase);
|
||||
if (rc)
|
||||
log_error ("failed to unprotect the secret key: %s\n",
|
||||
gpg_strerror (rc));
|
||||
|
@ -626,6 +656,11 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
|||
if (rc || got_shadow_info)
|
||||
{
|
||||
xfree (buf);
|
||||
if (r_passphrase)
|
||||
{
|
||||
xfree (*r_passphrase);
|
||||
*r_passphrase = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -637,6 +672,11 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
|||
{
|
||||
log_error ("failed to build S-Exp (off=%u): %s\n",
|
||||
(unsigned int)erroff, gpg_strerror (rc));
|
||||
if (r_passphrase)
|
||||
{
|
||||
xfree (*r_passphrase);
|
||||
*r_passphrase = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -290,10 +290,12 @@ reenter_compare_cb (struct pin_entry_info_s *pi)
|
|||
function returns 0 and store the passphrase at R_PASSPHRASE; if the
|
||||
user opted not to use a passphrase NULL will be stored there. The
|
||||
user needs to free the returned string. In case of an error and
|
||||
error code is returned and NULL stored at R_PASSPHRASE. */
|
||||
error code is returned and NULL stored at R_PASSPHRASE. If
|
||||
R_CANCEL_ALL is not NULL and the user canceled by directly closing
|
||||
the window true will be stored at this address. */
|
||||
gpg_error_t
|
||||
agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
||||
char **r_passphrase)
|
||||
char **r_passphrase, int *r_cancel_all)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *text1 = prompt;
|
||||
|
@ -314,7 +316,7 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
|||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
next_try:
|
||||
err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
|
||||
err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, r_cancel_all);
|
||||
initial_errtext = NULL;
|
||||
if (!err)
|
||||
{
|
||||
|
@ -327,7 +329,7 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
|||
/* Unless the passphrase is empty, ask to confirm it. */
|
||||
if (pi->pin && *pi->pin)
|
||||
{
|
||||
err = agent_askpin (ctrl, text2, NULL, NULL, pi2);
|
||||
err = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL);
|
||||
if (err == -1)
|
||||
{ /* The re-entered one did not match and the user did not
|
||||
hit cancel. */
|
||||
|
@ -379,7 +381,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce,
|
|||
rc = agent_ask_new_passphrase (ctrl,
|
||||
_("Please enter the passphrase to%0A"
|
||||
"to protect your new key"),
|
||||
&passphrase);
|
||||
&passphrase, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -471,7 +473,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
|
|||
|
||||
rc = agent_ask_new_passphrase (ctrl,
|
||||
_("Please enter the new passphrase"),
|
||||
&passphrase);
|
||||
&passphrase, NULL);
|
||||
if (!rc)
|
||||
{
|
||||
rc = store_key (s_skey, passphrase, 1);
|
||||
|
|
|
@ -440,6 +440,11 @@ remove_socket (char *name)
|
|||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
static int done;
|
||||
|
||||
if (done)
|
||||
return;
|
||||
done = 1;
|
||||
deinitialize_module_cache ();
|
||||
remove_socket (socket_name);
|
||||
remove_socket (socket_name_ssh);
|
||||
|
@ -724,6 +729,12 @@ main (int argc, char **argv )
|
|||
if( parse_debug )
|
||||
log_info (_("NOTE: no default option file `%s'\n"),
|
||||
configname );
|
||||
/* Save the default conf file name so that
|
||||
reread_configuration is able to test whether the
|
||||
config file has been created in the meantime. */
|
||||
xfree (config_filename);
|
||||
config_filename = configname;
|
||||
configname = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -811,10 +822,15 @@ main (int argc, char **argv )
|
|||
fclose( configfp );
|
||||
configfp = NULL;
|
||||
/* Keep a copy of the name so that it can be read on SIGHUP. */
|
||||
config_filename = configname;
|
||||
if (config_filename != configname)
|
||||
{
|
||||
xfree (config_filename);
|
||||
config_filename = configname;
|
||||
}
|
||||
configname = NULL;
|
||||
goto next_pass;
|
||||
}
|
||||
|
||||
xfree (configname);
|
||||
configname = NULL;
|
||||
if (log_get_errorcount(0))
|
||||
|
@ -1262,6 +1278,12 @@ void
|
|||
agent_exit (int rc)
|
||||
{
|
||||
/*FIXME: update_random_seed_file();*/
|
||||
|
||||
/* We run our cleanup handler because that may close cipher contexts
|
||||
stored in secure memory and thus this needs to be done before we
|
||||
explicitly terminate secure memory. */
|
||||
cleanup ();
|
||||
|
||||
#if 1
|
||||
/* at this time a bit annoying */
|
||||
if (opt.debug & DBG_MEMSTAT_VALUE)
|
||||
|
@ -1337,8 +1359,8 @@ reread_configuration (void)
|
|||
fp = fopen (config_filename, "r");
|
||||
if (!fp)
|
||||
{
|
||||
log_error (_("option file `%s': %s\n"),
|
||||
config_filename, strerror(errno) );
|
||||
log_info (_("option file `%s': %s\n"),
|
||||
config_filename, strerror(errno) );
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ the ~/.gnupg home directory. This directory is named
|
|||
and should have permissions 700.
|
||||
|
||||
The secret keys are stored in files with a name matching the
|
||||
hexadecimal representation of the keygrip[2].
|
||||
hexadecimal representation of the keygrip[2] and suffixed with ".key".
|
||||
|
||||
|
||||
Unprotected Private Key Format
|
||||
==============================
|
||||
|
@ -166,21 +167,23 @@ This format is used to transfer keys between gpg and gpg-agent.
|
|||
|
||||
(openpgp-private-key
|
||||
(version V)
|
||||
(protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)
|
||||
(algo PUBKEYALGO)
|
||||
(skey CSUM c P1 c P2 c P3 ... e PN))
|
||||
(skey _ P1 _ P2 _ P3 ... e PN)
|
||||
(csum n)
|
||||
(protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT))
|
||||
|
||||
|
||||
* V is the packet version number (3 or 4).
|
||||
* PUBKEYALGO is a Libgcrypt algo name
|
||||
* CSUM is the 16 bit checksum as defined by OpenPGP.
|
||||
* P1 .. PN are the parameters; the public parameters are never encrypted
|
||||
the secrect key parameters are encrypted if the "protection" list is
|
||||
given. To make this more explicit each parameter is preceded by a
|
||||
flag "_" for cleartext or "e" for encrypted text.
|
||||
* CSUM is the depreciated 16 bit checksum as defined by OpenPGP. This
|
||||
is an optional element.
|
||||
* If PROTTYPE is "sha1" the new style SHA1 checksum is used if it is "sum"
|
||||
the old 16 bit checksum is used and if it is "none" no protection at
|
||||
all is used.
|
||||
the old 16 bit checksum (above) is used and if it is "none" no
|
||||
protection at all is used.
|
||||
* PROTALGO is a Libgcrypt style cipher algorithm name
|
||||
* IV is the initialization verctor.
|
||||
* S2KMODE is the value from RFC-4880.
|
||||
|
@ -189,6 +192,105 @@ This format is used to transfer keys between gpg and gpg-agent.
|
|||
* S2KCOUNT is the count value from RFC-4880.
|
||||
|
||||
|
||||
Persistent Passphrase Format
|
||||
============================
|
||||
|
||||
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
|
||||
one master passphrase. It is possible to have several of those files
|
||||
each protected by a dedicated master passphrase. Clear text keywords
|
||||
allow to list the available protected passphrases.
|
||||
|
||||
The name of the files with these protected secrets have this form:
|
||||
pw-<string>.dat. STRING may be an arbitrary string, as a default name
|
||||
for the passphrase storage the name "pw-default.dat" is suggested.
|
||||
|
||||
|
||||
(protected-shared-secret
|
||||
((desc descriptive_text)
|
||||
(key [key_1] (keyword_1 keyword_2 keyword_n))
|
||||
(key [key_2] (keyword_21 keyword_22 keyword_2n))
|
||||
(key [key_n] (keyword_n1 keyword_n2 keyword_nn))
|
||||
(protected mode (parms) encrypted_octet_string)
|
||||
(protected-at <isotimestamp>)
|
||||
)
|
||||
)
|
||||
|
||||
After decryption the encrypted_octet_string yields this S-expression:
|
||||
|
||||
(
|
||||
(
|
||||
(value key_1 value_1)
|
||||
(value key_2 value_2)
|
||||
(value key_n value_n)
|
||||
)
|
||||
(hash sha1 #...[hashvalue]...#)
|
||||
)
|
||||
|
||||
The "descriptive_text" is displayed with the prompt to enter the
|
||||
unprotection passphrase.
|
||||
|
||||
KEY_1 to KEY_N are unique identifiers for the shared secret, for
|
||||
example an URI. In case this information should be kept confidential
|
||||
as well, they may not appear in the unprotected part; however they are
|
||||
mandatory in the encrypted_octet_string. The list of keywords is
|
||||
optional. The oder of the "key" lists and the order of the "value"
|
||||
lists mut match, that is the first "key"-list is associated with the
|
||||
first "value" list in the encrypted_octet_string.
|
||||
|
||||
The protection mode etc. is indentical to the protection mode as
|
||||
decribed for the private key format.
|
||||
|
||||
list of the secret key parameters. The protected-at expression is
|
||||
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
|
||||
|
||||
The "hash" in the encrypted_octet_string is calculated on the
|
||||
concatenation of the key list and value lists: i.e it is required to
|
||||
hash the concatenation of all these lists, including the
|
||||
parenthesis and (if used) the protected-at list.
|
||||
|
||||
Example:
|
||||
|
||||
(protected-shared-secret
|
||||
((desc "List of system passphrases")
|
||||
(key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
|
||||
(key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
|
||||
(key)
|
||||
(protected mode (parms) encrypted_octet_string)
|
||||
(protected-at "20100915T111722")
|
||||
)
|
||||
)
|
||||
|
||||
with "encrypted_octet_string" decoding to:
|
||||
|
||||
(
|
||||
(
|
||||
(value 4:1002 "signal flags at the lock")
|
||||
(value 4:1001 "taocp")
|
||||
(value 1:0 "premature optimization is the root of all evil")
|
||||
)
|
||||
(hash sha1 #0102030405060708091011121314151617181920#)
|
||||
)
|
||||
|
||||
To compute the hash this S-expression (in canoncical format) was
|
||||
hashed:
|
||||
|
||||
((desc "List of system passphrases")
|
||||
(key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
|
||||
(key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
|
||||
(key)
|
||||
(value 4:1002 "signal flags at the lock")
|
||||
(value 4:1001 "taocp")
|
||||
(value 1:0 "premature optimization is the root of all evil")
|
||||
(protected-at "20100915T111722")
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Notes:
|
||||
======
|
||||
|
|
|
@ -66,7 +66,9 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
|
|||
}
|
||||
rc = agent_key_from_file (ctrl, NULL, desc_text,
|
||||
ctrl->keygrip, &shadow_info,
|
||||
CACHE_MODE_NORMAL, NULL, &s_skey);
|
||||
CACHE_MODE_NORMAL, NULL, &s_skey, NULL);
|
||||
if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
|
||||
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
|
||||
if (rc)
|
||||
{
|
||||
if (gpg_err_code (rc) == GPG_ERR_ENOENT)
|
||||
|
|
|
@ -255,7 +255,9 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
|
|||
|
||||
rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip,
|
||||
&shadow_info, cache_mode, lookup_ttl,
|
||||
&s_skey);
|
||||
&s_skey, NULL);
|
||||
if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
|
||||
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to read the secret key\n");
|
||||
|
|
|
@ -191,14 +191,16 @@ get_standard_s2k_count (void)
|
|||
|
||||
|
||||
|
||||
/* Calculate the MIC for a private key S-Exp. SHA1HASH should point to
|
||||
a 20 byte buffer. This function is suitable for any algorithms. */
|
||||
/* Calculate the MIC for a private key or shared secret S-expression.
|
||||
SHA1HASH should point to a 20 byte buffer. This function is
|
||||
suitable for all algorithms. */
|
||||
static int
|
||||
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
||||
{
|
||||
const unsigned char *hash_begin, *hash_end;
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
int is_shared_secret;
|
||||
|
||||
s = plainkey;
|
||||
if (*s != '(')
|
||||
|
@ -207,16 +209,23 @@ calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
|||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
if (!smatch (&s, n, "private-key"))
|
||||
if (smatch (&s, n, "private-key"))
|
||||
is_shared_secret = 0;
|
||||
else if (smatch (&s, n, "shared-secret"))
|
||||
is_shared_secret = 1;
|
||||
else
|
||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||
if (*s != '(')
|
||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||
hash_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
s += n; /* skip over the algorithm name */
|
||||
if (!is_shared_secret)
|
||||
{
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
s += n; /* Skip the algorithm name. */
|
||||
}
|
||||
|
||||
while (*s == '(')
|
||||
{
|
||||
|
@ -955,7 +964,7 @@ 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
|
||||
/* Now remove the part which is included in the MIC but should not
|
||||
go into the final thing. */
|
||||
if (cutlen)
|
||||
{
|
||||
|
|
|
@ -289,6 +289,13 @@ test_agent_get_shadow_info (void)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
test_agent_protect_shared_secret (void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
|
@ -305,6 +312,7 @@ main (int argc, char **argv)
|
|||
test_make_shadow_info ();
|
||||
test_agent_shadow_key ();
|
||||
test_agent_get_shadow_info ();
|
||||
test_agent_protect_shared_secret ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue