diff options
Diffstat (limited to '')
-rw-r--r-- | agent/agent.h | 3 | ||||
-rw-r--r-- | agent/command.c | 22 | ||||
-rw-r--r-- | agent/cvt-openpgp.c | 365 | ||||
-rw-r--r-- | agent/cvt-openpgp.h | 4 | ||||
-rw-r--r-- | agent/findkey.c | 9 | ||||
-rw-r--r-- | agent/keyformat.txt | 126 | ||||
-rw-r--r-- | agent/protect-tool.c | 14 | ||||
-rw-r--r-- | agent/protect.c | 32 | ||||
-rw-r--r-- | agent/t-protect.c | 11 |
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); +} |