# # # patch "cmd_db.cc" # from [0ff11a2e9a2a988c5dd1398b5c65e80837556068] # to [72d5856b81f2f563da6cd211078fdaa224e72ac3] # # patch "cmd_key_cert.cc" # from [db863871951d61b405d1a8831835110660a2bf5f] # to [c77148067ed49c1ca6749de2bd32900ebd8331e2] # # patch "cmd_merging.cc" # from [8253dd6a55ffc02d922f4dc21b634802a1ac5a2d] # to [fac02b5883018307117623169bd47145e1e85e14] # # patch "cmd_netsync.cc" # from [951e1911bb05335819bd88ec2254abc3a7a6b3e2] # to [e36dc5fe61eda0f39e7e8226f0c2621c583c1fff] # # patch "cmd_othervcs.cc" # from [d3af6bed52452e59abf3aa454d5d53a2e4a69f54] # to [fc9779a1fb9949632276824c09be2270eef5dc96] # # patch "cmd_packet.cc" # from [cfeb4d5078c62d2e7f9db843fa01db6011d535f8] # to [db20e73d643a42781dadfe5bfcac021e56998a5f] # # patch "cmd_ws_commit.cc" # from [1ccfaf790bb0726d0c797c230a6c85d67070d7ae] # to [50f419943282f0b1138993980ec4748dad8949b5] # # patch "database.cc" # from [d36a7cb2debc5ac21307fc66c441e90d9471dbbb] # to [0cfd4588fec0a128a97e4aa393d42313622f17ea] # # patch "key_store.cc" # from [31468d1502145cc28afe5982eec2fbc207aa5a74] # to [871ce1ff5604e2f73b0c9a96b9ea300318efbe66] # # patch "key_store.hh" # from [a6474bd1564c7658a85a839807e25c212b421fc3] # to [20869ae8f0391a7c642f4eaa6672c2165cd6a465] # # patch "keys.cc" # from [d7472aad14247172129668962aa73b019f812b33] # to [de15fab9dd9d0847d13af45aeba6321f785dd9dc] # # patch "keys.hh" # from [e34a1014fb262928b84c83c798081f6b24f10d7f] # to [f48a9a4a28dd60cc8b89cb4f0fc9ecf0dd8613c9] # # patch "lua_hooks.cc" # from [79bfcd5bed4ab408686979288677fa6f92105048] # to [a4d6b05a80590b68c3c3f75da2e718ea74c002f4] # # patch "lua_hooks.hh" # from [fe660f497db47dbd08c4324ab027ad54399b8e30] # to [b49564bdf2a32666d2498360b4d531462e5d7e29] # # patch "netsync.cc" # from [90ed45c81772a1b489cbec4c55a60126007d4e51] # to [fb07ceb02260250d7f15255ff362e79bddba60bd] # # patch "packet.cc" # from [f092ffd73ce8c46f055fe96f5a25f1566d5bbfc7] # to [36b0f681ca92776c2e60e16529ef7cd7d826a03f] # # patch "project.cc" # from [04cff64a5a395861e915b0787e453d8bc332dc2e] # to [399a73e5180b71fa74251a603920bff7cff8baef] # ============================================================ --- cmd_db.cc 0ff11a2e9a2a988c5dd1398b5c65e80837556068 +++ cmd_db.cc 72d5856b81f2f563da6cd211078fdaa224e72ac3 @@ -249,6 +249,7 @@ CMD(db_changesetify, "changesetify", "", { database db(app); key_store keys(app); + project_t project(db); E(args.size() == 0, origin::user, F("no arguments needed")); @@ -257,7 +258,7 @@ CMD(db_changesetify, "changesetify", "", db.check_is_not_rosterified(); // early short-circuit to avoid failure after lots of work - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); build_changesets_from_manifest_ancestry(db, keys, set()); } @@ -269,6 +270,7 @@ CMD(db_rosterify, "rosterify", "", CMD_R { database db(app); key_store keys(app); + project_t project(db); E(args.size() == 0, origin::user, F("no arguments needed")); @@ -277,7 +279,7 @@ CMD(db_rosterify, "rosterify", "", CMD_R db.check_is_not_rosterified(); // early short-circuit to avoid failure after lots of work - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); build_roster_style_revs_from_manifest_style_revs(db, keys, app.opts.attrs_to_drop); ============================================================ --- cmd_key_cert.cc db863871951d61b405d1a8831835110660a2bf5f +++ cmd_key_cert.cc c77148067ed49c1ca6749de2bd32900ebd8331e2 @@ -103,10 +103,10 @@ CMD(passphrase, "passphrase", "", CMD_RE if (args.size() != 1) throw usage(execid); - key_name ident; - internalize_key_name(idx(args, 0), ident); + key_name name; + internalize_key_name(idx(args, 0), name); - keys.change_key_passphrase(ident); + keys.change_key_passphrase(name); P(F("passphrase changed")); } @@ -118,12 +118,13 @@ CMD(ssh_agent_export, "ssh_agent_export" { database db(app); key_store keys(app); + project_t project(db); if (args.size() > 1) throw usage(execid); key_id id; - get_user_key(app.opts, app.lua, db, keys, id); + get_user_key(app.opts, app.lua, db, keys, project, id); if (args.empty()) keys.export_key_for_agent(id, cout); @@ -145,12 +146,13 @@ CMD(ssh_agent_add, "ssh_agent_add", "", { database db(app); key_store keys(app); + project_t project(db); if (args.size() > 1) throw usage(execid); key_id id; - get_user_key(app.opts, app.lua, db, keys, id); + get_user_key(app.opts, app.lua, db, keys, project, id); keys.add_key_to_agent(id); } @@ -175,7 +177,7 @@ CMD(cert, "cert", "", CMD_REF(key_and_ce cert_name cname; internalize_cert_name(idx(args, 1), cname); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); cert_value val; if (args.size() == 3) @@ -260,7 +262,7 @@ CMD(tag, "tag", "", CMD_REF(review), N_( revision_id r; complete(app.opts, app.lua, project, idx(args, 0)(), r); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); project.put_tag(keys, r, idx(args, 1)()); } @@ -281,7 +283,7 @@ CMD(testresult, "testresult", "", CMD_RE revision_id r; complete(app.opts, app.lua, project, idx(args, 0)(), r); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); project.put_revision_testresult(keys, r, idx(args, 1)()); } @@ -304,7 +306,7 @@ CMD(approve, "approve", "", CMD_REF(revi E(!app.opts.branch().empty(), origin::user, F("need --branch argument for approval")); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); project.put_revision_in_branch(keys, r, app.opts.branch); } @@ -326,7 +328,7 @@ CMD(suspend, "suspend", "", CMD_REF(revi E(!app.opts.branch().empty(), origin::user, F("need --branch argument to suspend")); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); project.suspend_revision_in_branch(keys, r, app.opts.branch); } @@ -361,7 +363,7 @@ CMD(comment, "comment", "", CMD_REF(revi revision_id r; complete(app.opts, app.lua, project, idx(args, 0)(), r); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); project.put_revision_comment(keys, r, comment); } ============================================================ --- cmd_merging.cc 8253dd6a55ffc02d922f4dc21b634802a1ac5a2d +++ cmd_merging.cc fac02b5883018307117623169bd47145e1e85e14 @@ -497,7 +497,7 @@ CMD(merge, "merge", "", CMD_REF(tree), " % heads.size() % app.opts.branch); // avoid failure after lots of work - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); size_t pass = 1, todo = heads.size() - 1; @@ -622,7 +622,7 @@ CMD(merge_into_dir, "merge_into_dir", "" return; } - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); P(F("propagating %s -> %s") % idx(args,0) % idx(args,1)); P(F("[left] %s") % *src_i); @@ -882,7 +882,7 @@ CMD(explicit_merge, "explicit_merge", "" % right % left); // avoid failure after lots of work - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); merge_two(app.opts, app.lua, project, keys, left, right, branch, string("explicit merge"), std::cout, false); ============================================================ --- cmd_netsync.cc 951e1911bb05335819bd88ec2254abc3a7a6b3e2 +++ cmd_netsync.cc e36dc5fe61eda0f39e7e8226f0c2621c583c1fff @@ -50,9 +50,10 @@ find_key(options & opts, static void find_key(options & opts, - lua_hooks & lua, database & db, key_store & keys, + lua_hooks & lua, + project_t & project, netsync_connection_info const & info, bool need_key = true) { @@ -60,7 +61,7 @@ find_key(options & opts, if (!info.client.u.host.empty()) host = utf8(info.client.u.host, origin::user); - cache_netsync_key(opts, lua, db, keys, host, + cache_netsync_key(opts, db, keys, lua, project, host, info.client.include_pattern, info.client.exclude_pattern, need_key ? KEY_REQUIRED : KEY_OPTIONAL); @@ -71,6 +72,7 @@ build_client_connection_info(options & o lua_hooks & lua, database & db, key_store & keys, + project_t & project, netsync_connection_info & info, bool address_given, bool include_or_exclude_given, @@ -190,7 +192,7 @@ build_client_connection_info(options & o opts.use_transport_auth = lua.hook_use_transport_auth(info.client.u); if (opts.use_transport_auth) { - find_key(opts, lua, db, keys, info, need_key); + find_key(opts, db, keys, lua, project, info, need_key); } } @@ -199,6 +201,7 @@ extract_client_connection_info(options & lua_hooks & lua, database & db, key_store & keys, + project_t & project, args_vector const & args, netsync_connection_info & info, bool need_key = true) @@ -218,7 +221,7 @@ extract_client_connection_info(options & info.client.include_pattern = globish(args.begin() + 1, args.end()); info.client.exclude_pattern = globish(opts.exclude_patterns); } - build_client_connection_info(opts, lua, db, keys, + build_client_connection_info(opts, lua, db, keys, project, info, have_address, have_include_exclude, need_key); } @@ -236,7 +239,8 @@ CMD(push, "push", "", CMD_REF(network), project_t project(db); netsync_connection_info info; - extract_client_connection_info(app.opts, app.lua, db, keys, args, info); + extract_client_connection_info(app.opts, app.lua, db, keys, + project, args, info); run_netsync_protocol(app.opts, app.lua, project, keys, client_voice, source_role, info); @@ -254,7 +258,7 @@ CMD(pull, "pull", "", CMD_REF(network), project_t project(db); netsync_connection_info info; - extract_client_connection_info(app.opts, app.lua, db, keys, + extract_client_connection_info(app.opts, app.lua, db, keys, project, args, info, false); if (!keys.have_signing_key()) @@ -277,7 +281,8 @@ CMD(sync, "sync", "", CMD_REF(network), project_t project(db); netsync_connection_info info; - extract_client_connection_info(app.opts, app.lua, db, keys, args, info); + extract_client_connection_info(app.opts, app.lua, db, keys, + project, args, info); if (app.opts.set_default && workspace::found) { @@ -385,7 +390,7 @@ CMD(clone, "clone", "", CMD_REF(network) info.client.include_pattern = globish(branchname(), origin::user); info.client.exclude_pattern = globish(app.opts.exclude_patterns); - build_client_connection_info(app.opts, app.lua, db, keys, + build_client_connection_info(app.opts, app.lua, db, keys, project, info, true, true, false); if (!keys.have_signing_key()) @@ -512,7 +517,7 @@ CMD_NO_WORKSPACE(serve, "serve", "", CMD info.client.exclude_pattern = globish("", origin::internal); if (!app.opts.bind_uris.empty()) info.client.unparsed = *app.opts.bind_uris.begin(); - find_key(app.opts, app.lua, db, keys, info); + find_key(app.opts, db, keys, app.lua, project, info); } else if (!app.opts.bind_stdio) W(F("The --no-transport-auth option is usually only used " ============================================================ --- cmd_othervcs.cc d3af6bed52452e59abf3aa454d5d53a2e4a69f54 +++ cmd_othervcs.cc fc9779a1fb9949632276824c09be2270eef5dc96 @@ -63,7 +63,7 @@ CMD(cvs_import, "cvs_import", "", CMD_RE // make sure we can sign certs using the selected key; also requests // the password (if necessary) up front rather than after some arbitrary // amount of work - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); import_cvs_repo(project, keys, cvsroot, app.opts.branch); } ============================================================ --- cmd_packet.cc cfeb4d5078c62d2e7f9db843fa01db6011d535f8 +++ cmd_packet.cc db20e73d643a42781dadfe5bfcac021e56998a5f @@ -56,7 +56,9 @@ CMD(pubkey, "pubkey", "", CMD_REF(packet F("public key '%s' does not exist") % idx(args, 0)()); packet_writer pw(cout); - pw.consume_public_key(ident, key); + key_name canonical_name; + project.get_canonical_name_of_key(keys, ident, canonical_name); + pw.consume_public_key(canonical_name, key); } CMD(privkey, "privkey", "", CMD_REF(packet_io), N_("ID"), @@ -64,20 +66,25 @@ CMD(privkey, "privkey", "", CMD_REF(pack "", options::opts::none) { + database db(app); key_store keys(app); + project_t project(db); if (args.size() != 1) throw usage(execid); - key_name ident = typecast_vocab(idx(args, 0)); + key_name name = typecast_vocab(idx(args, 0)); + key_id ident; + project.lookup_key_by_name(name, ident); E(keys.key_pair_exists(ident), origin::user, F("public and private key '%s' do not exist in keystore") % idx(args, 0)()); packet_writer pw(cout); keypair kp; - keys.get_key_pair(ident, kp); - pw.consume_key_pair(ident, kp); + key_name given_name; + keys.get_key_pair(ident, given_name, kp); + pw.consume_key_pair(given_name, kp); } namespace ============================================================ --- cmd_ws_commit.cc 1ccfaf790bb0726d0c797c230a6c85d67070d7ae +++ cmd_ws_commit.cc 50f419943282f0b1138993980ec4748dad8949b5 @@ -370,7 +370,7 @@ CMD(disapprove, "disapprove", "", CMD_RE % r).str(), origin::internal)); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); edge_entry const & old_edge (*rev.edges.begin()); db.get_revision_manifest(edge_old_revision(old_edge), @@ -1163,7 +1163,7 @@ CMD(commit, "commit", "ci", CMD_REF(work E(message_validated, origin::user, F("log message rejected by hook: %s") % reason); - cache_user_key(app.opts, app.lua, db, keys); + cache_user_key(app.opts, app.lua, db, keys, project); // for the divergence check, below set heads; ============================================================ --- database.cc d36a7cb2debc5ac21307fc66c441e90d9471dbbb +++ database.cc 0cfd4588fec0a128a97e4aa393d42313622f17ea @@ -188,7 +188,7 @@ namespace typedef hashmap::hash_map > parent_id_map; typedef hashmap::hash_map height_map; - typedef hashmap::hash_map, shared_ptr > > verifier_cache; @@ -2882,7 +2882,7 @@ void // crypto key management void -database::get_key_ids(vector & pubkeys) +database::get_key_ids(vector & pubkeys) { pubkeys.clear(); results res; @@ -2890,24 +2890,10 @@ database::get_key_ids(vector & imp->fetch(res, one_col, any_rows, query("SELECT id FROM public_keys")); for (size_t i = 0; i < res.size(); ++i) - pubkeys.push_back(key_name(res[i][0], origin::database)); + pubkeys.push_back(key_id(res[i][0], origin::database)); } void -database::get_key_ids(globish const & pattern, - vector & pubkeys) -{ - pubkeys.clear(); - results res; - - imp->fetch(res, one_col, any_rows, query("SELECT id FROM public_keys")); - - for (size_t i = 0; i < res.size(); ++i) - if (pattern.matches(res[i][0])) - pubkeys.push_back(key_name(res[i][0], origin::database)); -} - -void database_impl::get_keys(string const & table, vector & keys) { keys.clear(); @@ -2924,12 +2910,12 @@ bool } bool -database::public_key_exists(id const & hash) +database::public_key_exists(key_id const & hash) { results res; imp->fetch(res, one_col, any_rows, query("SELECT id FROM public_keys WHERE hash = ?") - % blob(hash())); + % blob(hash.inner()())); I((res.size() == 1) || (res.empty())); if (res.size() == 1) return true; @@ -2963,13 +2949,13 @@ void } void -database::get_key(key_name const & pub_id, +database::get_key(key_id const & pub_id, rsa_pub_key & pub) { results res; imp->fetch(res, one_col, one_row, query("SELECT keydata FROM public_keys WHERE id = ?") - % text(pub_id())); + % text(pub_id.inner()())); pub = rsa_pub_key(res[0][0], origin::database); } @@ -2977,22 +2963,18 @@ database::put_key(key_name const & pub_i database::put_key(key_name const & pub_id, rsa_pub_key const & pub) { - if (public_key_exists(pub_id)) + key_id thash; + key_hash_code(pub_id, pub, thash); + I(!public_key_exists(thash)); + + if (public_key_exists(thash)) { - rsa_pub_key tmp; - get_key(pub_id, tmp); - if (!keys_match(pub_id, tmp, pub_id, pub)) - W(F("key '%s' is not equal to key '%s' in database") % pub_id % pub_id); L(FL("skipping existing public key %s") % pub_id); return false; } L(FL("putting public key %s") % pub_id); - key_id thash; - key_hash_code(pub_id, pub, thash); - I(!public_key_exists(thash)); - imp->execute(query("INSERT INTO public_keys VALUES(?, ?, ?)") % blob(thash.inner()()) % text(pub_id()) @@ -3002,14 +2984,14 @@ void } void -database::delete_public_key(key_name const & pub_id) +database::delete_public_key(key_id const & pub_id) { imp->execute(query("DELETE FROM public_keys WHERE id = ?") - % text(pub_id())); + % text(pub_id.inner()())); } void -database::encrypt_rsa(key_name const & pub_id, +database::encrypt_rsa(key_id const & pub_id, string const & plaintext, rsa_oaep_sha_data & ciphertext) { @@ -3074,7 +3056,7 @@ database::check_signature(key_id const & shared_ptr pub_key = boost::shared_dynamic_cast(x509_key); - E(pub_key, id.made_from, + E(pub_key, id.inner().made_from, F("Failed to get RSA verifying key for %s") % id); verifier.reset(get_pk_verifier(*pub_key, "EMSA3(SHA-1)")); @@ -3183,7 +3165,7 @@ database_impl::oldstyle_results_to_certs { results key_res; query lookup_key("SELECT id FROM public_keys WHERE name = ?"); - fetch(key_res, 1, any_rows, lookup_key % k_name); + fetch(key_res, 1, any_rows, lookup_key % text(k_name())); if (key_res.size() == 0) break; // no key, cert is bogus else if (key_res.size() == 1) ============================================================ --- key_store.cc 31468d1502145cc28afe5982eec2fbc207aa5a74 +++ key_store.cc 871ce1ff5604e2f73b0c9a96b9ea300318efbe66 @@ -55,22 +55,26 @@ using Botan::get_cipher; using Botan::get_pk_decryptor; using Botan::get_cipher; + +typedef pair key_info; +typedef pair full_key_info; +typedef map key_map; + struct key_store_state { system_path const key_dir; string const ssh_sign_mode; bool have_read; lua_hooks & lua; - map keys; - map hashes; + key_map keys; #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) boost::shared_ptr rng; #endif // These are used to cache keys and signers (if the hook allows). - map > privkey_cache; - map > signer_cache; + map > privkey_cache; + map > signer_cache; // Initialized when first required. scoped_ptr agent; @@ -91,16 +95,15 @@ struct key_store_state } // internal methods - void get_key_file(key_name const & ident, system_path & file); - void write_key(key_name const & ident, keypair const & kp); + void get_key_file(key_id const & ident, system_path & file); + void write_key(full_key_info const & info); void maybe_read_key_dir(); - shared_ptr decrypt_private_key(key_name const & id, + shared_ptr decrypt_private_key(key_id const & id, bool force_from_user = false); // just like put_key_pair except that the key is _not_ written to disk. // for internal use in reading keys back from disk. - bool put_key_pair_memory(key_name const & ident, - keypair const & kp); + bool put_key_pair_memory(full_key_info const & info); // wrapper around accesses to agent, initializes as needed ssh_agent & get_agent() @@ -112,10 +115,10 @@ struct key_store_state // duplicates of key_store interfaces for use by key_store_state methods // and the keyreader. - bool maybe_get_key_pair(key_name const & ident, + bool maybe_get_key_pair(key_id const & ident, + key_name & name, keypair & kp); - bool put_key_pair(key_name const & ident, - keypair const & kp); + bool put_key_pair(full_key_info const & info); void migrate_old_key_pair(key_name const & id, old_arc4_rsa_priv_key const & old_priv, rsa_pub_key const & pub); @@ -147,13 +150,16 @@ namespace rsa_pub_key const & k) {E(false, origin::system, F("Extraneous data in key store."));} - virtual void consume_key_pair(key_name const & ident, + virtual void consume_key_pair(key_name const & name, keypair const & kp) { - L(FL("reading key pair '%s' from key store") % ident); + L(FL("reading key pair '%s' from key store") % name); - E(kss.put_key_pair_memory(ident, kp), origin::system, - F("Key store has multiple keys with id '%s'.") % ident); + key_id ident; + key_hash_code(name, kp.pub, ident); + E(kss.put_key_pair_memory(full_key_info(ident, key_info(name, kp))), + origin::system, + F("Key store has multiple copies of the key with id '%s'.") % ident); L(FL("successfully read key pair '%s' from key store") % ident); } @@ -182,7 +188,7 @@ key_store::have_signing_key() const bool key_store::have_signing_key() const { - return !signing_key().empty(); + return !signing_key.inner()().empty(); } #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) @@ -231,24 +237,11 @@ void } void -key_store::get_key_ids(globish const & pattern, - vector & priv) +key_store::get_key_ids(vector & priv) { s->maybe_read_key_dir(); priv.clear(); - for (map::const_iterator - i = s->keys.begin(); i != s->keys.end(); ++i) - if (pattern.matches((i->first)())) - priv.push_back(i->first); -} - -void -key_store::get_key_ids(vector & priv) -{ - s->maybe_read_key_dir(); - priv.clear(); - for (map::const_iterator - i = s->keys.begin(); i != s->keys.end(); ++i) + for (key_map::const_iterator i = s->keys.begin(); i != s->keys.end(); ++i) priv.push_back(i->first); } @@ -260,26 +253,29 @@ bool } bool -key_store_state::maybe_get_key_pair(key_name const & ident, +key_store_state::maybe_get_key_pair(key_id const & ident, + key_name & name, keypair & kp) { maybe_read_key_dir(); - map::const_iterator i = keys.find(ident); + key_map::const_iterator i = keys.find(ident); if (i == keys.end()) return false; - kp = i->second; + name = i->second.first; + kp = i->second.second; return true; } bool -key_store::maybe_get_key_pair(key_name const & ident, +key_store::maybe_get_key_pair(key_id const & ident, keypair & kp) { - return s->maybe_get_key_pair(ident, kp); + key_name name; + return s->maybe_get_key_pair(ident, name, kp); } void -key_store::get_key_pair(key_name const & ident, +key_store::get_key_pair(key_id const & ident, keypair & kp) { bool found = maybe_get_key_pair(ident, kp); @@ -287,109 +283,94 @@ bool } bool -key_store::maybe_get_key_pair(id const & hash, +key_store::maybe_get_key_pair(key_id const & hash, key_name & keyid, keypair & kp) { - s->maybe_read_key_dir(); - map::const_iterator hi = s->hashes.find(hash); - if (hi == s->hashes.end()) - return false; - - map::const_iterator ki = s->keys.find(hi->second); + key_map::const_iterator ki = s->keys.find(hash); if (ki == s->keys.end()) return false; - keyid = hi->second; - kp = ki->second; + keyid = ki->second.first; + kp = ki->second.second; return true; } void -key_store_state::get_key_file(key_name const & ident, +key_store::get_key_pair(key_id const & hash, + key_name & keyid, + keypair & kp) +{ + bool found = maybe_get_key_pair(hash, keyid, kp); + I(found); +} + +void +key_store_state::get_key_file(key_id const & ident, system_path & file) { - // filename is the keypair id, except that some characters can't be put in - // filenames (especially on windows). - string leaf = ident(); - for (unsigned int i = 0; i < leaf.size(); ++i) - if (leaf.at(i) == '+') - leaf.at(i) = '_'; + hexenc encoded; + encode_hexenc(ident.inner(), encoded); - file = key_dir / path_component(leaf, origin::internal); + file = key_dir / path_component(encoded(), origin::internal); } void -key_store_state::write_key(key_name const & ident, - keypair const & kp) +key_store_state::write_key(full_key_info const & info) { ostringstream oss; packet_writer pw(oss); - pw.consume_key_pair(ident, kp); - data dat(oss.str(), ident.made_from); + pw.consume_key_pair(info.second.first, info.second.second); + data dat(oss.str(), info.second.first.made_from); system_path file; - get_key_file(ident, file); + get_key_file(info.first, file); // Make sure the private key is not readable by anyone other than the user. - L(FL("writing key '%s' to file '%s' in dir '%s'") % ident % file % key_dir); + L(FL("writing key '%s' to file '%s' in dir '%s'") + % info.first % file % key_dir); write_data_userprivate(file, dat, key_dir); } bool -key_store_state::put_key_pair(key_name const & ident, - keypair const & kp) +key_store_state::put_key_pair(full_key_info const & info) { maybe_read_key_dir(); - bool newkey = put_key_pair_memory(ident, kp); + bool newkey = put_key_pair_memory(info); if (newkey) - write_key(ident, kp); + write_key(info); return newkey; } bool -key_store::put_key_pair(key_name const & ident, +key_store::put_key_pair(key_name const & name, keypair const & kp) { - return s->put_key_pair(ident, kp); + key_id ident; + key_hash_code(name, kp.pub, ident); + return s->put_key_pair(full_key_info(ident, key_info(name, kp))); } bool -key_store_state::put_key_pair_memory(key_name const & ident, - keypair const & kp) +key_store_state::put_key_pair_memory(full_key_info const & info) { - L(FL("putting key pair '%s'") % ident); - pair::iterator, bool> res; - res = keys.insert(make_pair(ident, kp)); - if (res.second) + L(FL("putting key pair '%s'") % info.first); + pair res; + res = keys.insert(info); + if (!res.second) { - id hash; - key_hash_code(ident, kp.pub, hash); - I(hashes.insert(make_pair(hash, ident)).second); - return true; - } - else - { - E(keys_match(ident, res.first->second.pub, ident, kp.pub), - origin::system, - F("Cannot store key '%s': a different key by that name exists.") - % ident); - L(FL("skipping existing key pair %s") % ident); + L(FL("skipping existing key pair %s") % info.first); return false; } + return true; } void key_store::delete_key(key_id const & ident) { s->maybe_read_key_dir(); - map::iterator i = s->keys.find(ident); + key_map::iterator i = s->keys.find(ident); if (i != s->keys.end()) { - id hash; - key_hash_code(ident, i->second.pub, hash); - map::iterator j = s->hashes.find(hash); - I(j != s->hashes.end()); - s->hashes.erase(j); s->keys.erase(i); s->signer_cache.erase(ident); s->privkey_cache.erase(ident); @@ -409,7 +390,8 @@ get_passphrase(utf8 & phrase, // match. Prompts are worded slightly differently if GENERATING_KEY is true. static void get_passphrase(utf8 & phrase, - key_name const & keyid, + key_name const & keyname, + key_id const & keyid, bool confirm_phrase, bool generating_key) { @@ -418,13 +400,20 @@ get_passphrase(utf8 & phrase, char pass2[constants::maxpasswd]; int i = 0; + hexenc hexid; + encode_hexenc(keyid.inner(), hexid); + string const short_id = hexid().substr(0, 8) + "..."; + if (confirm_phrase && !generating_key) - prompt1 = (F("enter new passphrase for key ID [%s]: ") % keyid).str(); + prompt1 = (F("enter new passphrase for key ID [%s] (%s): ") + % keyname % short_id).str(); else - prompt1 = (F("enter passphrase for key ID [%s]: ") % keyid).str(); + prompt1 = (F("enter passphrase for key ID [%s] (%s): ") + % keyname % short_id).str(); if (confirm_phrase) - prompt2 = (F("confirm passphrase for key ID [%s]: ") % keyid).str(); + prompt2 = (F("confirm passphrase for key ID [%s] (%s): ") + % keyname % short_id).str(); try { @@ -463,17 +452,18 @@ shared_ptr shared_ptr -key_store_state::decrypt_private_key(key_name const & id, +key_store_state::decrypt_private_key(key_id const & id, bool force_from_user) { // See if we have this key in the decrypted key cache. - map >::const_iterator + map >::const_iterator cpk = privkey_cache.find(id); if (cpk != privkey_cache.end()) return cpk->second; keypair kp; - E(maybe_get_key_pair(id, kp), origin::user, + key_name name; + E(maybe_get_key_pair(id, name, kp), origin::user, F("no key pair '%s' found in key store '%s'") % id % key_dir); L(FL("%d-byte private key") % kp.priv().size()); @@ -495,10 +485,10 @@ key_store_state::decrypt_private_key(key utf8 phrase; string lua_phrase; // See whether a lua hook will tell us the passphrase. - if (!force_from_user && lua.hook_get_passphrase(id, lua_phrase)) + if (!force_from_user && lua.hook_get_passphrase(name, id, lua_phrase)) phrase = utf8(lua_phrase, origin::user); else - get_passphrase(phrase, id, false, false); + get_passphrase(phrase, name, id, false, false); int cycles = 1; for (;;) @@ -520,7 +510,7 @@ key_store_state::decrypt_private_key(key F("failed to decrypt old private RSA key, " "probably incorrect passphrase")); - get_passphrase(phrase, id, false, false); + get_passphrase(phrase, name, id, false, false); cycles++; continue; } @@ -541,7 +531,7 @@ void } void -key_store::cache_decrypted_key(const key_name & id) +key_store::cache_decrypted_key(const key_id & id) { signing_key = id; keypair key; @@ -560,22 +550,22 @@ key_store::create_key_pair(database & db key_store::create_key_pair(database & db, key_name const & ident, utf8 const * maybe_passphrase, - id * maybe_hash) + key_id * const maybe_hash) { conditional_transaction_guard guard(db); - bool exists = key_pair_exists(ident); - if (db.database_specified()) + bool exists = false; + for (key_map::iterator i = s->keys.begin(); i != s->keys.end(); ++i) { - guard.acquire(); - exists = exists || db.public_key_exists(ident); + if (i->second.first == ident) + exists = true; } E(!exists, origin::user, F("key '%s' already exists") % ident); utf8 prompted_passphrase; if (!maybe_passphrase) { - get_passphrase(prompted_passphrase, ident, true, true); + get_passphrase(prompted_passphrase, ident, key_id(), true, true); maybe_passphrase = &prompted_passphrase; } @@ -636,14 +626,29 @@ void } void -key_store::change_key_passphrase(key_name const & id) +key_store::change_key_passphrase(key_name const & name) { + key_id id; keypair kp; - load_key_pair(*this, id, kp); + { + bool found = false; + for (key_map::const_iterator i = s->keys.begin(); + i != s->keys.end(); ++i) + { + if (i->second.first == name) + { + E(!found, origin::user, + F("you have multiple private keys name '%s'") % name); + found = true; + id = i->first; + kp = i->second.second; + } + } + } shared_ptr priv = s->decrypt_private_key(id, true); utf8 new_phrase; - get_passphrase(new_phrase, id, true, false); + get_passphrase(new_phrase, name, id, true, false); unfiltered_pipe->start_msg(); Botan::PKCS8::encrypt_key(*priv, *unfiltered_pipe, @@ -658,11 +663,11 @@ key_store::change_key_passphrase(key_nam origin::internal); delete_key(id); - put_key_pair(id, kp); + put_key_pair(name, kp); } void -key_store::decrypt_rsa(key_name const & id, +key_store::decrypt_rsa(key_id const & id, rsa_oaep_sha_data const & ciphertext, string & plaintext) { @@ -690,16 +695,17 @@ key_store::make_signature(database & db, void key_store::make_signature(database & db, - key_name const & id, + key_id const & id, string const & tosign, rsa_sha1_signature & signature) { + key_name name; keypair key; - get_key_pair(id, key); + get_key_pair(id, name, key); // If the database doesn't have this public key, add it now. if (!db.public_key_exists(id)) - db.put_key(id, key.pub); + db.put_key(name, key.pub); string sig_string; ssh_agent & agent = s->get_agent(); @@ -763,8 +769,8 @@ key_store::make_signature(database & db, if (agent.connected() && s->ssh_sign_mode != "only" && s->ssh_sign_mode != "no") { - L(FL("make_signature: adding private key (%s) to ssh-agent") % id()); - agent.add_identity(*priv_key, id()); + L(FL("make_signature: adding private key (%s) to ssh-agent") % id); + agent.add_identity(*priv_key, name()); } signer = shared_ptr(get_pk_signer(*priv_key, "EMSA3(SHA-1)")); @@ -814,23 +820,34 @@ void // void -key_store::add_key_to_agent(key_name const & id) +key_store::add_key_to_agent(key_id const & id) { ssh_agent & agent = s->get_agent(); E(agent.connected(), origin::user, F("no ssh-agent is available, cannot add key '%s'") % id); shared_ptr priv = s->decrypt_private_key(id); - agent.add_identity(*priv, id()); + + key_name name; + keypair kp; + s->maybe_get_key_pair(id, name, kp); + agent.add_identity(*priv, name()); } void -key_store::export_key_for_agent(key_name const & id, +key_store::export_key_for_agent(key_id const & id, std::ostream & os) { shared_ptr priv = s->decrypt_private_key(id); + + key_name name; + { + keypair kp; + I(s->maybe_get_key_pair(id, name, kp)); + } + utf8 new_phrase; - get_passphrase(new_phrase, id, true, false); + get_passphrase(new_phrase, name, id, true, false); // This pipe cannot sensibly be recycled. Pipe p(new Botan::DataSink_Stream(os)); @@ -867,10 +884,10 @@ key_store_state::migrate_old_key_pair // See whether a lua hook will tell us the passphrase. string lua_phrase; - if (lua.hook_get_passphrase(id, lua_phrase)) + if (lua.hook_get_passphrase(id, key_id(), lua_phrase)) phrase = utf8(lua_phrase, origin::user); else - get_passphrase(phrase, id, false, false); + get_passphrase(phrase, id, key_id(), false, false); int cycles = 1; for (;;) @@ -905,7 +922,7 @@ key_store_state::migrate_old_key_pair F("failed to decrypt old private RSA key, " "probably incorrect passphrase")); - get_passphrase(phrase, id, false, false); + get_passphrase(phrase, id, key_id(), false, false); cycles++; continue; } @@ -941,7 +958,9 @@ key_store_state::migrate_old_key_pair if (!pub().empty() && !keys_match(id, pub, id, kp.pub)) W(F("public and private keys for %s don't match") % id); - put_key_pair(id, kp); + key_id hash; + key_hash_code(id, kp.pub, hash); + put_key_pair(full_key_info(hash, key_info(id, kp))); } void ============================================================ --- key_store.hh a6474bd1564c7658a85a839807e25c212b421fc3 +++ key_store.hh 20869ae8f0391a7c642f4eaa6672c2165cd6a465 @@ -69,11 +69,14 @@ public: keypair & kp); bool maybe_get_key_pair(key_id const & ident, keypair & kp); - bool maybe_get_key_pair(id const & hash, + void get_key_pair(key_id const & hash, + key_name & ident, + keypair & kp); + bool maybe_get_key_pair(key_id const & hash, key_name & ident, keypair & kp); - bool put_key_pair(key_name const & ident, + bool put_key_pair(key_name const & name, keypair const & kp); void delete_key(key_id const & ident); @@ -84,9 +87,11 @@ public: void create_key_pair(database & db, key_name const & ident, utf8 const * maybe_passphrase = NULL, - id * maybe_hash = NULL); + key_id * const maybe_hash = NULL); - void change_key_passphrase(key_id const & id); + // This is always your own key, so you probably want to + // always use the given name. + void change_key_passphrase(key_name const & id); void decrypt_rsa(key_id const & id, rsa_oaep_sha_data const & ciphertext, ============================================================ --- keys.cc d7472aad14247172129668962aa73b019f812b33 +++ keys.cc de15fab9dd9d0847d13af45aeba6321f785dd9dc @@ -19,6 +19,7 @@ #include "charset.hh" #include "lua_hooks.hh" #include "options.hh" +#include "project.hh" #include "key_store.hh" #include "database.hh" @@ -49,26 +50,37 @@ load_key_pair(key_store & keys, keys.get_key_pair(id, kp); } +void +load_key_pair(key_store & keys, + key_id const & id, + key_name & name, + keypair & kp) +{ + load_key_pair(keys, id); + keys.get_key_pair(id, name, kp); +} + namespace { void check_and_save_chosen_key(database & db, key_store & keys, - key_name const & chosen_key) + key_id const & chosen_key) { // Ensure that the specified key actually exists. + key_name name; keypair priv_key; - load_key_pair(keys, chosen_key, priv_key); + load_key_pair(keys, chosen_key, name, priv_key); if (db.database_specified()) { // If the database doesn't have this public key, add it now; otherwise // make sure the database and key-store agree on the public key. if (!db.public_key_exists(chosen_key)) - db.put_key(chosen_key, priv_key.pub); + db.put_key(name, priv_key.pub); else { rsa_pub_key pub_key; db.get_key(chosen_key, pub_key); - E(keys_match(chosen_key, pub_key, chosen_key, priv_key.pub), + E(keys_match(name, pub_key, name, priv_key.pub), origin::no_fault, F("The key '%s' stored in your database does\n" "not match the version in your local key store!") @@ -79,9 +91,9 @@ namespace { // Decrypt and cache the key now. keys.cache_decrypted_key(chosen_key); } - bool get_only_key(key_store & keys, bool required, key_name & key) + bool get_only_key(key_store & keys, bool required, key_id & key) { - vector all_privkeys; + vector all_privkeys; keys.get_key_ids(all_privkeys); E(!required || !all_privkeys.empty(), origin::user, F("you have no private key to make signatures with\n" @@ -105,7 +117,8 @@ get_user_key(options const & opts, lua_h void get_user_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys, key_id & key) + database & db, key_store & keys, + project_t & project, key_id & key) { if (keys.have_signing_key()) { @@ -118,7 +131,14 @@ get_user_key(options const & opts, lua_h { if (!opts.signing_key().empty()) { - key = opts.signing_key; + if (!opts.signing_key_id.inner()().empty()) + { + key = opts.signing_key_id; + } + else if (!opts.signing_key().empty()) + { + project.lookup_key_by_name(opts.signing_key, key); + } } else { @@ -127,7 +147,7 @@ get_user_key(options const & opts, lua_h "was given with an empty argument")); } } - else if (lua.hook_get_branch_key(opts.branch, key)) + else if (lua.hook_get_branch_key(opts.branch, project, key)) ; // the lua hook sets the key else { @@ -138,8 +158,11 @@ void } void -cache_netsync_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys, +cache_netsync_key(options const & opts, + database & db, + key_store & keys, + lua_hooks & lua, + project_t & project, utf8 const & host, globish const & include, globish const & exclude, @@ -151,16 +174,21 @@ cache_netsync_key(options const & opts, } bool found_key = false; - key_name key; + key_id key; // key_given is not set if the key option was extracted from the workspace if (opts.key_given || !opts.signing_key().empty()) { - if (!opts.signing_key().empty()) + if (!opts.signing_key_id.inner()().empty()) { - key = opts.signing_key; + key = opts.signing_key_id; found_key = true; } + else if (!opts.signing_key().empty()) + { + project.lookup_key_by_name(opts.signing_key, key); + found_key = true; + } } else if (lua.hook_get_netsync_key(host, include, exclude, key)) { @@ -179,33 +207,23 @@ cache_user_key(options const & opts, lua void cache_user_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys) + database & db, key_store & keys, + project_t & project) { - key_name key; - get_user_key(opts, lua, db, keys, key); + key_id key; + get_user_key(opts, lua, db, keys, project, key); } void -get_netsync_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys, - netsync_key_requiredness key_requiredness, - key_id & key) -{ - if (!keys.signing_key().empty()) - { - key = keys.signing_key; - return; - } -} - -void key_hash_code(key_name const & ident, rsa_pub_key const & pub, - id & out) + key_id & out) { data tdat(ident() + ":" + remove_ws(encode_base64(pub)()), origin::internal); - calculate_ident(tdat, out); + id tmp; + calculate_ident(tdat, tmp); + out = key_id(tmp); } // helper to compare if two keys have the same hash @@ -216,7 +234,7 @@ keys_match(key_name const & id1, key_name const & id2, rsa_pub_key const & key2) { - id hash1, hash2; + key_id hash1, hash2; key_hash_code(id1, key1, hash1); key_hash_code(id2, key2, hash2); return hash1 == hash2; ============================================================ --- keys.hh e34a1014fb262928b84c83c798081f6b24f10d7f +++ keys.hh f48a9a4a28dd60cc8b89cb4f0fc9ecf0dd8613c9 @@ -13,6 +13,7 @@ struct options; #include "vocab.hh" struct options; +class project_t; class lua_hooks; class key_store; class database; @@ -29,19 +30,23 @@ void get_user_key(options const & opts, // form, so as not to bother the user for their passphrase later. void get_user_key(options const & opts, lua_hooks & lua, database & db, key_store & keys, - key_id & key); + project_t & project, key_id & key); // As above, but does not report which key has been selected; for use when // the important thing is to have selected one and cached the decrypted key. void cache_user_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys); + database & db, key_store & keys, + project_t & project); // Find the key to be used for netsync authentication. If possible, ensure the // database and the key_store agree on that key, and cache it in decrypted // form, so as not to bother the user for their passphrase later. enum netsync_key_requiredness {KEY_OPTIONAL, KEY_REQUIRED}; -void cache_netsync_key(options const & opts, lua_hooks & lua, - database & db, key_store & keys, +void cache_netsync_key(options const & opts, + database & db, + key_store & keys, + lua_hooks & lua, + project_t & project, utf8 const & host, globish const & include, globish const & exclude, @@ -58,7 +63,7 @@ void key_hash_code(key_name const & iden void key_hash_code(key_name const & ident, rsa_pub_key const & pub, - id & out); + key_id & out); bool keys_match(key_name const & id1, rsa_pub_key const & key1, ============================================================ --- lua_hooks.cc 79bfcd5bed4ab408686979288677fa6f92105048 +++ lua_hooks.cc a4d6b05a80590b68c3c3f75da2e718ea74c002f4 @@ -25,6 +25,7 @@ #include "vocab.hh" #include "transforms.hh" #include "paths.hh" +#include "project.hh" #include "uri.hh" #include "cmd.hh" #include "commands.hh" @@ -229,12 +230,15 @@ bool // nb: if you're hooking lua to return your passphrase, you don't care if we // keep a couple extra temporaries of your passphrase around. bool -lua_hooks::hook_get_passphrase(key_name const & k, string & phrase) +lua_hooks::hook_get_passphrase(key_name const & name, + key_id const & id, + string & phrase) { return Lua(st) .func("get_passphrase") - .push_str(k()) - .call(1,1) + .push_str(name()) + .push_str(id.inner()()) + .call(2,1) .extract_classified_str(phrase) .ok(); } @@ -279,7 +283,8 @@ lua_hooks::hook_get_branch_key(branch_na bool lua_hooks::hook_get_branch_key(branch_name const & branchname, - key_name & k) + project_t & project, + key_id & k) { string key; bool ok = Lua(st) @@ -289,7 +294,8 @@ lua_hooks::hook_get_branch_key(branch_na .extract_str(key) .ok(); - k = key_name(key, origin::user); + key_name name(key, origin::user); + project.lookup_key_by_name(name, k); return ok; } ============================================================ --- lua_hooks.hh fe660f497db47dbd08c4324ab027ad54399b8e30 +++ lua_hooks.hh b49564bdf2a32666d2498360b4d531462e5d7e29 @@ -27,6 +27,7 @@ struct options; struct lua_State; struct globish; struct options; +class project_t; extern app_state* get_app_state(lua_State *LS); @@ -48,8 +49,11 @@ public: // cert hooks bool hook_expand_selector(std::string const & sel, std::string & exp); bool hook_expand_date(std::string const & sel, std::string & exp); - bool hook_get_branch_key(branch_name const & branchname, key_name & k); - bool hook_get_passphrase(key_name const & k, std::string & phrase); + bool hook_get_branch_key(branch_name const & branchname, + project_t & project, key_id & k); + bool hook_get_passphrase(key_name const & name, + key_id const & id, + std::string & phrase); bool hook_get_author(branch_name const & branchname, key_id const & k, std::string & author); ============================================================ --- netsync.cc 90ed45c81772a1b489cbec4c55a60126007d4e51 +++ netsync.cc fb07ceb02260250d7f15255ff362e79bddba60bd @@ -3477,7 +3477,7 @@ session::rebuild_merkle_trees(set> certid; validate_id(certid); string name; iss >> name; validate_certname(name); - string keyid; iss >> keyid; validate_key(keyid); + string keyid; iss >> keyid; validate_id(keyid); string val; read_rest(iss,val); validate_arg_base64(val); @@ -228,7 +228,7 @@ namespace cert t = cert(hash, cert_name(name, made_from), decode_base64_as(val, made_from), - key_name(keyid, made_from), + decode_hexenc_as(keyid, made_from), decode_base64_as(body, made_from)); cons.consume_revision_cert(t); } ============================================================ --- project.cc 04cff64a5a395861e915b0787e453d8bc332dc2e +++ project.cc 399a73e5180b71fa74251a603920bff7cff8baef @@ -367,12 +367,12 @@ project_t::put_standard_certs_from_optio if (author.empty()) { key_id key; - get_user_key(opts, lua, db, keys, key); + get_user_key(opts, lua, db, keys, *this, key); if (!lua.hook_get_author(branch, key, author)) { key_name name; - get_name_of_key(key, name); + get_name_of_key(keys, key, name); author = name(); } }