aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agent/agent.h3
-rw-r--r--agent/command.c22
-rw-r--r--agent/cvt-openpgp.c365
-rw-r--r--agent/cvt-openpgp.h4
-rw-r--r--agent/findkey.c9
-rw-r--r--agent/keyformat.txt126
-rw-r--r--agent/protect-tool.c14
-rw-r--r--agent/protect.c32
-rw-r--r--agent/t-protect.c11
9 files changed, 439 insertions, 147 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 2fd0b8b8a..7445061ac 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -374,7 +374,8 @@ unsigned char get_standard_s2k_count_rfc4880 (void);
int agent_protect (const unsigned char *plainkey, const char *passphrase,
unsigned char **result, size_t *resultlen,
unsigned long s2k_count);
-int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
+int agent_unprotect (ctrl_t ctrl,
+ 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);
diff --git a/agent/command.c b/agent/command.c
index e57c69d05..036486856 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1,6 +1,7 @@
/* command.c - gpg-agent command handler
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010,
* 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
*
* This file is part of GnuPG.
*
@@ -1807,18 +1808,21 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
static const char hlp_import_key[] =
- "IMPORT_KEY [<cache_nonce>]\n"
+ "IMPORT_KEY [--unattended] [<cache_nonce>]\n"
"\n"
"Import a secret key into the key store. The key is expected to be\n"
"encrypted using the current session's key wrapping key (cf. command\n"
"KEYWRAP_KEY) using the AESWRAP-128 algorithm. This function takes\n"
"no arguments but uses the inquiry \"KEYDATA\" to ask for the actual\n"
- "key data. The unwrapped key must be a canonical S-expression.";
+ "key data. The unwrapped key must be a canonical S-expression. The\n"
+ "option --unattended tries to import the key as-is without any\n"
+ "re-encryption";
static gpg_error_t
cmd_import_key (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
+ int opt_unattended;
unsigned char *wrappedkey = NULL;
size_t wrappedkeylen;
gcry_cipher_hd_t cipherhd = NULL;
@@ -1838,6 +1842,9 @@ cmd_import_key (assuan_context_t ctx, char *line)
goto leave;
}
+ opt_unattended = has_option (line, "--unattended");
+ line = skip_options (line);
+
p = line;
for (p=line; *p && *p != ' ' && *p != '\t'; p++)
;
@@ -1921,7 +1928,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
key = NULL;
err = convert_from_openpgp (ctrl, openpgp_sexp, grip,
ctrl->server_local->keydesc, cache_nonce,
- &key, &passphrase);
+ &key, opt_unattended? NULL : &passphrase);
if (err)
goto leave;
realkeylen = gcry_sexp_canon_len (key, 0, NULL, &err);
@@ -1929,6 +1936,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
goto leave; /* Invalid canonical encoded S-expression. */
if (passphrase)
{
+ assert (!opt_unattended);
if (!cache_nonce)
{
char buf[12];
@@ -1941,6 +1949,12 @@ cmd_import_key (assuan_context_t ctx, char *line)
assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
}
}
+ else if (opt_unattended)
+ {
+ err = set_error (GPG_ERR_ASS_PARAMETER,
+ "\"--unattended\" may only be used with OpenPGP keys");
+ goto leave;
+ }
else
{
if (!agent_key_available (grip))
@@ -1957,7 +1971,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
if (passphrase)
{
err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
- ctrl->s2k_count);
+ ctrl->s2k_count);
if (!err)
err = agent_write_private_key (grip, finalkey, finalkeylen, 0);
}
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index ec0fd0a89..205b9533a 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1,6 +1,7 @@
/* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
* 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
*
* This file is part of GnuPG.
*
@@ -160,6 +161,72 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
}
+/* Convert a secret key given as algorithm id, an array of key
+ parameters, and an S-expression of the original OpenPGP transfer
+ key into our s-expression based format. This is a variant of
+ convert_secret_key which is used for the openpgp-native protection
+ mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */
+static gpg_error_t
+convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
+ gcry_sexp_t transfer_key)
+{
+ gpg_error_t err;
+ gcry_sexp_t s_skey = NULL;
+
+ *r_key = NULL;
+
+ switch (pubkey_algo)
+ {
+ case GCRY_PK_DSA:
+ err = gcry_sexp_build
+ (&s_skey, NULL,
+ "(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)"
+ "(protected openpgp-native%S)))",
+ skey[0], skey[1], skey[2], skey[3], transfer_key);
+ break;
+
+ case GCRY_PK_ELG:
+ case GCRY_PK_ELG_E:
+ err = gcry_sexp_build
+ (&s_skey, NULL,
+ "(protected-private-key(elg(p%m)(g%m)(y%m)"
+ "(protected openpgp-native%S)))",
+ skey[0], skey[1], skey[2], transfer_key);
+ break;
+
+
+ case GCRY_PK_RSA:
+ case GCRY_PK_RSA_E:
+ case GCRY_PK_RSA_S:
+ err = gcry_sexp_build
+ (&s_skey, NULL,
+ "(protected-private-key(rsa(n%m)(e%m)",
+ "(protected openpgp-native%S)))",
+ skey[0], skey[1], transfer_key );
+ break;
+
+ case GCRY_PK_ECDSA:
+ case GCRY_PK_ECDH:
+ /* Although our code would work with "ecc" we explicitly use
+ "ecdh" or "ecdsa" to implicitly set the key capabilities. */
+ err = gcry_sexp_build
+ (&s_skey, NULL,
+ "(protected-private-key(%s(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
+ "(protected openpgp-native%S)))",
+ pubkey_algo == GCRY_PK_ECDSA?"ecdsa":"ecdh",
+ skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key);
+ break;
+
+ default:
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ break;
+ }
+
+ if (!err)
+ *r_key = s_skey;
+ return err;
+}
+
/* Hash the passphrase and set the key. */
static gpg_error_t
@@ -202,30 +269,19 @@ checksum (const unsigned char *p, unsigned int n)
}
-/* Note that this function modified SKEY. SKEYSIZE is the allocated
- size of the array including the NULL item; this is used for a
- bounds check. On success a converted key is stored at R_KEY. */
+/* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number.
+ On success R_NPKEY and R_NSKEY receive the number or parameters for
+ the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
+ SKEY. */
static int
-do_unprotect (const char *passphrase,
- int pkt_version, int pubkey_algo, int is_protected,
- gcry_mpi_t *skey, size_t skeysize,
- int protect_algo, void *protect_iv, size_t protect_ivlen,
- int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
- u16 desired_csum, gcry_sexp_t *r_key)
+prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
+ int s2k_mode,
+ unsigned int *r_npkey, unsigned int *r_nskey,
+ unsigned int *r_skeylen)
{
gpg_error_t err;
size_t npkey, nskey, skeylen;
- gcry_cipher_hd_t cipher_hd = NULL;
- u16 actual_csum;
- size_t nbytes;
int i;
- gcry_mpi_t tmpmpi;
-
- *r_key = NULL;
-
- /* Unfortunately, the OpenPGP PK algorithm numbers need to be
- re-mapped for Libgcrypt. */
- pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo);
/* Count the actual number of MPIs is in the array and set the
remainder to NULL for easier processing later on. */
@@ -264,6 +320,54 @@ do_unprotect (const char *passphrase,
if (nskey+1 >= skeysize)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+ /* Check that the public key parameters are all available and not
+ encrypted. */
+ for (i=0; i < npkey; i++)
+ {
+ if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+ return gpg_error (GPG_ERR_BAD_SECKEY);
+ }
+
+ if (r_npkey)
+ *r_npkey = npkey;
+ if (r_nskey)
+ *r_nskey = nskey;
+ if (r_skeylen)
+ *r_skeylen = skeylen;
+ return 0;
+}
+
+
+/* Note that this function modifies SKEY. SKEYSIZE is the allocated
+ size of the array including the NULL item; this is used for a
+ bounds check. On success a converted key is stored at R_KEY. */
+static int
+do_unprotect (const char *passphrase,
+ int pkt_version, int pubkey_algo, int is_protected,
+ gcry_mpi_t *skey, size_t skeysize,
+ int protect_algo, void *protect_iv, size_t protect_ivlen,
+ int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
+ u16 desired_csum, gcry_sexp_t *r_key)
+{
+ gpg_error_t err;
+ unsigned int npkey, nskey, skeylen;
+ gcry_cipher_hd_t cipher_hd = NULL;
+ u16 actual_csum;
+ size_t nbytes;
+ int i;
+ gcry_mpi_t tmpmpi;
+
+ *r_key = NULL;
+
+ /* Unfortunately, the OpenPGP PK algorithm numbers need to be
+ re-mapped for Libgcrypt. */
+ pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo);
+
+ err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode,
+ &npkey, &nskey, &skeylen);
+ if (err)
+ return err;
+
/* Check whether SKEY is at all protected. If it is not protected
merely verify the checksum. */
if (!is_protected)
@@ -512,7 +616,7 @@ do_unprotect (const char *passphrase,
}
-/* Callback function to try the unprotection from the passpharse query
+/* Callback function to try the unprotection from the passphrase query
code. */
static int
try_do_unprotect_cb (struct pin_entry_info_s *pi)
@@ -536,24 +640,19 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
}
-/* Convert an OpenPGP transfer key into our internal format. Before
- asking for a passphrase we check whether the key already exists in
- our key storage. S_PGP is the OpenPGP key in transfer format. If
- CACHE_NONCE is given the passphrase will be looked up in the cache.
- On success R_KEY will receive a canonical encoded S-expression with
- the unprotected key in our internal format; the caller needs to
- release that memory. The passphrase used to decrypt the OpenPGP
- key will be returned at R_PASSPHRASE; the caller must release this
- passphrase. The keygrip will be stored at the 20 byte buffer
- pointed to by GRIP. On error NULL is stored at all return
- arguments. */
-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)
+/* See convert_from_openpgp for the core of the description. This
+ function adds an optional PASSPHRASE argument and uses this to
+ silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be
+ NULL in this mode. */
+static gpg_error_t
+convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
+ unsigned char *grip, const char *prompt,
+ const char *cache_nonce, const char *passphrase,
+ unsigned char **r_key, char **r_passphrase)
{
gpg_error_t err;
+ int unattended;
+ int from_native;
gcry_sexp_t top_list;
gcry_sexp_t list = NULL;
const char *value;
@@ -573,12 +672,13 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
u16 desired_csum;
int skeyidx = 0;
- gcry_sexp_t s_skey;
- struct pin_entry_info_s *pi;
- struct try_do_unprotect_arg_s pi_arg;
+ gcry_sexp_t s_skey = NULL;
*r_key = NULL;
- *r_passphrase = NULL;
+ if (r_passphrase)
+ *r_passphrase = NULL;
+ unattended = !r_passphrase;
+ from_native = (!cache_nonce && passphrase && !r_passphrase);
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
if (!top_list)
@@ -607,6 +707,7 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
is_protected = 0;
else
goto bad_seckey;
+
if (is_protected)
{
string = gcry_sexp_nth_string (list, 2);
@@ -755,64 +856,89 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
if (err)
goto leave;
- if (!agent_key_available (grip))
+ if (!from_native && !agent_key_available (grip))
{
err = gpg_error (GPG_ERR_EEXIST);
goto leave;
}
- pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
- if (!pi)
- return gpg_error_from_syserror ();
- pi->max_length = 100;
- pi->min_digits = 0; /* We want a real passphrase. */
- pi->max_digits = 16;
- pi->max_tries = 3;
- pi->check_cb = try_do_unprotect_cb;
- pi->check_cb_arg = &pi_arg;
- pi_arg.is_v4 = is_v4;
- pi_arg.is_protected = is_protected;
- pi_arg.pubkey_algo = pubkey_algo;
- pi_arg.protect_algo = protect_algo;
- pi_arg.iv = iv;
- pi_arg.ivlen = ivlen;
- pi_arg.s2k_mode = s2k_mode;
- pi_arg.s2k_algo = s2k_algo;
- pi_arg.s2k_salt = s2k_salt;
- pi_arg.s2k_count = s2k_count;
- pi_arg.desired_csum = desired_csum;
- pi_arg.skey = skey;
- pi_arg.skeysize = DIM (skey);
- pi_arg.skeyidx = skeyidx;
- pi_arg.r_key = &s_skey;
-
- err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
- if (cache_nonce)
+ if (unattended && !from_native)
{
- char *cache_value;
+ int pubkey_g_algo = map_pk_openpgp_to_gcry (pubkey_algo);
- cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
- if (cache_value)
- {
- if (strlen (cache_value) < pi->max_length)
- strcpy (pi->pin, cache_value);
- xfree (cache_value);
- }
- if (*pi->pin)
- err = try_do_unprotect_cb (pi);
+ err = prepare_unprotect (pubkey_g_algo, skey, DIM(skey), s2k_mode,
+ NULL, NULL, NULL);
+ if (err)
+ goto leave;
+
+ err = convert_transfer_key (&s_skey, pubkey_g_algo, skey, s_pgp);
+ if (err)
+ goto leave;
}
- if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
- err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
- skeyidx = pi_arg.skeyidx;
- if (!err)
+ else
{
- *r_passphrase = xtrystrdup (pi->pin);
- if (!*r_passphrase)
- err = gpg_error_from_syserror ();
+ struct pin_entry_info_s *pi;
+ struct try_do_unprotect_arg_s pi_arg;
+
+ pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+ if (!pi)
+ return gpg_error_from_syserror ();
+ pi->max_length = 100;
+ pi->min_digits = 0; /* We want a real passphrase. */
+ pi->max_digits = 16;
+ pi->max_tries = 3;
+ pi->check_cb = try_do_unprotect_cb;
+ pi->check_cb_arg = &pi_arg;
+ pi_arg.is_v4 = is_v4;
+ pi_arg.is_protected = is_protected;
+ pi_arg.pubkey_algo = pubkey_algo;
+ pi_arg.protect_algo = protect_algo;
+ pi_arg.iv = iv;
+ pi_arg.ivlen = ivlen;
+ pi_arg.s2k_mode = s2k_mode;
+ pi_arg.s2k_algo = s2k_algo;
+ pi_arg.s2k_salt = s2k_salt;
+ pi_arg.s2k_count = s2k_count;
+ pi_arg.desired_csum = desired_csum;
+ pi_arg.skey = skey;
+ pi_arg.skeysize = DIM (skey);
+ pi_arg.skeyidx = skeyidx;
+ pi_arg.r_key = &s_skey;
+
+ err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
+ if (cache_nonce)
+ {
+ char *cache_value;
+
+ cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+ if (cache_value)
+ {
+ if (strlen (cache_value) < pi->max_length)
+ strcpy (pi->pin, cache_value);
+ xfree (cache_value);
+ }
+ if (*pi->pin)
+ err = try_do_unprotect_cb (pi);
+ }
+ else if (from_native)
+ {
+ if (strlen (passphrase) < pi->max_length)
+ strcpy (pi->pin, passphrase);
+ err = try_do_unprotect_cb (pi);
+ }
+ if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native)
+ err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+ skeyidx = pi_arg.skeyidx;
+ if (!err && r_passphrase)
+ {
+ *r_passphrase = xtrystrdup (pi->pin);
+ if (!*r_passphrase)
+ err = gpg_error_from_syserror ();
+ }
+ xfree (pi);
+ if (err)
+ goto leave;
}
- xfree (pi);
- if (err)
- goto leave;
/* Save some memory and get rid of the SKEY array now. */
for (idx=0; idx < skeyidx; idx++)
@@ -820,16 +946,16 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
skeyidx = 0;
/* Note that the padding is not required - we use it only because
- that function allows us to created the result in secure memory. */
+ that function allows us to create the result in secure memory. */
err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
- gcry_sexp_release (s_skey);
leave:
+ gcry_sexp_release (s_skey);
gcry_sexp_release (list);
gcry_sexp_release (top_list);
for (idx=0; idx < skeyidx; idx++)
gcry_mpi_release (skey[idx]);
- if (err)
+ if (err && r_passphrase)
{
xfree (*r_passphrase);
*r_passphrase = NULL;
@@ -847,6 +973,63 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
}
+/* Convert an OpenPGP transfer key into our internal format. Before
+ asking for a passphrase we check whether the key already exists in
+ our key storage. S_PGP is the OpenPGP key in transfer format. If
+ CACHE_NONCE is given the passphrase will be looked up in the cache.
+ On success R_KEY will receive a canonical encoded S-expression with
+ the unprotected key in our internal format; the caller needs to
+ release that memory. The passphrase used to decrypt the OpenPGP
+ key will be returned at R_PASSPHRASE; the caller must release this
+ passphrase. If R_PASSPHRASE is NULL the unattended conversion mode
+ will be used which uses the openpgp-native protection format for
+ the key. The keygrip will be stored at the 20 byte buffer pointed
+ to by GRIP. On error NULL is stored at all return arguments. */
+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)
+{
+ return convert_from_openpgp_main (ctrl, s_pgp, grip, prompt,
+ cache_nonce, NULL,
+ r_key, r_passphrase);
+}
+
+/* This function is called by agent_unprotect to re-protect an
+ openpgp-native protected private-key into the standard private-key
+ protection format. */
+gpg_error_t
+convert_from_openpgp_native (ctrl_t ctrl,
+ gcry_sexp_t s_pgp, const char *passphrase,
+ unsigned char **r_key)
+{
+ gpg_error_t err;
+ unsigned char grip[20];
+
+ if (!passphrase)
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ err = convert_from_openpgp_main (ctrl, s_pgp, grip, NULL,
+ NULL, passphrase,
+ r_key, NULL);
+
+ /* On success try to re-write the key. */
+ if (!err)
+ {
+ unsigned char *protectedkey = NULL;
+ size_t protectedkeylen;
+
+ if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen,
+ ctrl->s2k_count))
+ agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
+ xfree (protectedkey);
+ }
+
+ return err;
+}
+
+
static gpg_error_t
key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
diff --git a/agent/cvt-openpgp.h b/agent/cvt-openpgp.h
index 3c48d0319..d27a776ef 100644
--- a/agent/cvt-openpgp.h
+++ b/agent/cvt-openpgp.h
@@ -23,6 +23,10 @@ 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_from_openpgp_native (ctrl_t ctrl,
+ gcry_sexp_t s_pgp,
+ const char *passphrase,
+ unsigned char **r_key);
gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key,
const char *passphrase,
diff --git a/agent/findkey.c b/agent/findkey.c
index ebdcc038e..d11f0888a 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -66,6 +66,9 @@ agent_write_private_key (const unsigned char *grip,
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
+ /* FIXME: Write to a temp file first so that write failures during
+ key updates won't lead to a key loss. */
+
if (!force && !access (fname, F_OK))
{
log_error ("secret key file '%s' already exists\n", fname);
@@ -119,7 +122,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
assert (!arg->unprotected_key);
arg->change_required = 0;
- err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
+ err = agent_unprotect (arg->ctrl, arg->protected_key, pi->pin, protected_at,
&arg->unprotected_key, &dummy);
if (err)
return err;
@@ -325,7 +328,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
if (pw)
{
- rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
+ rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
if (!rc)
{
if (r_passphrase)
@@ -350,7 +353,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
pw = agent_get_cache (hexgrip, cache_mode);
if (pw)
{
- rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
+ rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
if (!rc)
{
if (r_passphrase)
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 7ba6af2fb..3f95dae03 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -84,56 +84,94 @@ encrypted_octet_string. The result of the decryption process is a
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
+The currently defined protection modes are:
- openpgp-s2k3-sha1-aes-cbc
+1. openpgp-s2k3-sha1-aes-cbc
-which describes an algorithm using using AES in CBC mode for
-encryption, SHA-1 for integrity protection and the String to Key
-algorithm 3 from OpenPGP (rfc2440).
+ This describes an algorithm using using AES in CBC mode for
+ encryption, SHA-1 for integrity protection and the String to Key
+ algorithm 3 from OpenPGP (rfc2440).
-Example:
-
-(protected openpgp-s2k3-sha1-aes-cbc
- ((sha1 16byte_salt no_of_iterations) 16byte_iv)
- encrypted_octet_string
-)
+ Example:
-The encrypted_octet string should yield this S-Exp (in canonical
-representation) after decryption:
+ (protected openpgp-s2k3-sha1-aes-cbc
+ ((sha1 16byte_salt no_of_iterations) 16byte_iv)
+ encrypted_octet_string
+ )
-(
- (
- (d #046129F..[some bytes not shown]..81#)
- (p #00e861b..[some bytes not shown]..f1#)
- (q #00f7a7c..[some bytes not shown]..61#)
- (u #304559a..[some bytes not shown]..9b#)
- )
- (hash sha1 #...[hashvalue]...#)
-)
+ The encrypted_octet string should yield this S-Exp (in canonical
+ 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.
-
-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, the algorithm keyword and (if used) the protected-at
-list.
-
-(rsa
- (n #00e0ce9..[some bytes not shown]..51#)
- (e #010001#)
- (d #046129F..[some bytes not shown]..81#)
- (p #00e861b..[some bytes not shown]..f1#)
- (q #00f7a7c..[some bytes not shown]..61#)
- (u #304559a..[some bytes not shown]..9b#)
- (protected-at "18950523T000000")
-)
+ (
+ (
+ (d #046129F..[some bytes not shown]..81#)
+ (p #00e861b..[some bytes not shown]..f1#)
+ (q #00f7a7c..[some bytes not shown]..61#)
+ (u #304559a..[some bytes not shown]..9b#)
+ )
+ (hash sha1 #...[hashvalue]...#)
+ )
+
+ For padding reasons, random bytes are appended to this list - they can
+ 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, the algorithm keyword and (if used) the protected-at
+ list.
+
+ (rsa
+ (n #00e0ce9..[some bytes not shown]..51#)
+ (e #010001#)
+ (d #046129F..[some bytes not shown]..81#)
+ (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
+ the stored one - If they don't match the integrity of the key is not
+ given.
+
+2. openpgp-native
+
+ This is a wrapper around the OpenPGP Private Key Transport format
+ which resembles the standard OpenPGP format and allows the use of an
+ existing key without re-encrypting to the default protection format.
+
+ Example:
+
+ (protected openpgp-native
+ (openpgp-private-key
+ (version V)
+ (algo PUBKEYALGO)
+ (skey _ P1 _ P2 _ P3 ... e PN)
+ (csum n)
+ (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)))
+
+ Note that the public key paramaters in SKEY are duplicated and
+ should be identical to their copies in the standard parameter
+ elements. Here is an example of an entire protected private key
+ using this format:
+
+ (protected-private-key
+ (rsa
+ (n #00e0ce9..[some bytes not shown]..51#)
+ (e #010001#)
+ (protected openpgp-native
+ (openpgp-private-key
+ (version 4)
+ (algo rsa)
+ (skey _ #00e0ce9..[some bytes not shown]..51#
+ _ #010001#
+ e #.........................#)
+ (protection sha1 aes #aabbccddeeff00112233445566778899#
+ 3 sha1 #2596f93e85f41e53# 3:190))))
+ (uri http://foo.bar x-foo:whatever_you_want)
+ (comment whatever))
-After decryption the hash must be recalculated and compared against
-the stored one - If they don't match the integrity of the key is not
-given.
Shadowed Private Key Format
@@ -184,7 +222,7 @@ This format is used to transfer keys between gpg and gpg-agent.
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
+* CSUM is the deprecated 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 (above) is used and if it is "none" no
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index d59f5f0fa..faa0e2458 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -372,7 +372,7 @@ read_and_unprotect (const char *fname)
if (!key)
return;
- rc = agent_unprotect (key, (pw=get_passphrase (1)),
+ rc = agent_unprotect (NULL, key, (pw=get_passphrase (1)),
protected_at, &result, &resultlen);
release_passphrase (pw);
xfree (key);
@@ -728,3 +728,15 @@ release_passphrase (char *pw)
xfree (pw);
}
}
+
+
+/* Stub function. */
+gpg_error_t
+convert_from_openpgp_native (gcry_sexp_t s_pgp, const char *passphrase,
+ unsigned char **r_key)
+{
+ (void)s_pgp;
+ (void)passphrase;
+ (void)r_key;
+ return gpg_error (GPG_ERR_BUG);
+}
diff --git a/agent/protect.c b/agent/protect.c
index 3e2cbb94e..cb2c098a9 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1,6 +1,7 @@
/* protect.c - Un/Protect a secret key
* Copyright (C) 1998, 1999, 2000, 2001, 2002,
* 2003, 2007, 2009, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
*
* This file is part of GnuPG.
*
@@ -604,6 +605,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
return 0;
}
+
/* Do the actual decryption and check the return list for consistency. */
static int
@@ -832,9 +834,10 @@ merge_lists (const unsigned char *protectedkey,
/* Unprotect the key encoded in canonical format. We assume a valid
S-Exp here. If a protected-at item is available, its value will
- be stored at protocted_at unless this is NULL. */
+ be stored at protected_at unless this is NULL. */
int
-agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
+agent_unprotect (ctrl_t ctrl,
+ const unsigned char *protectedkey, const char *passphrase,
gnupg_isotime_t protected_at,
unsigned char **result, size_t *resultlen)
{
@@ -938,7 +941,30 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
- return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+ {
+ if (smatch (&s, n, "openpgp-native"))
+ {
+ gcry_sexp_t s_prot_begin;
+
+ rc = gcry_sexp_sscan (&s_prot_begin, NULL,
+ prot_begin,
+ gcry_sexp_canon_len (prot_begin, 0,NULL,NULL));
+ if (rc)
+ return rc;
+
+ rc = convert_from_openpgp_native (ctrl,
+ s_prot_begin, passphrase, &final);
+ gcry_sexp_release (s_prot_begin);
+ if (!rc)
+ {
+ *result = final;
+ *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
+ }
+ return rc;
+ }
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+ }
if (*s != '(' || s[1] != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s += 2;
diff --git a/agent/t-protect.c b/agent/t-protect.c
index 02b614a38..9096cb2e0 100644
--- a/agent/t-protect.c
+++ b/agent/t-protect.c
@@ -337,3 +337,14 @@ main (int argc, char **argv)
return 0;
}
+
+/* Stub function. */
+gpg_error_t
+convert_from_openpgp_native (gcry_sexp_t s_pgp, const char *passphrase,
+ unsigned char **r_key)
+{
+ (void)s_pgp;
+ (void)passphrase;
+ (void)r_key;
+ return gpg_error (GPG_ERR_BUG);
+}