# # add_file "key_store.cc" # # add_file "key_store.hh" # # patch "ChangeLog" # from [21b992ed451a886bfd5ce7934d11bdbe4a9762e0] # to [e3cb4410fbf089f5ba258314b15d2fb0494ec871] # # patch "Makefile.am" # from [37d234104aa51fb9f169ed0be12208e4b5be0669] # to [c3ca41e70b719d5d345383a893a7d4ba7823160c] # # patch "app_state.cc" # from [4c8ba6a5cdc3fa945a945e19ea63e1fc857d3ca1] # to [58db112140d05dcb5ffaea7e7eea44f04662312f] # # patch "app_state.hh" # from [869c3e0393d765e37fc17ae117ccbbe69776b177] # to [b310058ad20b794c7b4347eb35d495b980f149cd] # # patch "cert.cc" # from [43de7f5240b7caa27fc590630afe3ffeea0bfa0b] # to [fdf5188e96328a6d86fcac31ff3caf0b8ebb7602] # # patch "cert.hh" # from [e2b1d86c8f2610e854afed7ab661c4c2554e7004] # to [7e7e906bb36445343533611871cd5d3680cb7f99] # # patch "commands.cc" # from [61185ae5cc7cd776c25ca1655199a314bdd40378] # to [5e3307a36823404a734e65224da9966461d8e382] # # patch "database.cc" # from [83c0e4fe4b672da030bc50966c118ac0c2c98017] # to [a1452325ac8f6aef8804ec53785ecbac5ee4cea6] # # patch "database.hh" # from [1ac20ac00fe3bb34f5a72c03d3abbfbc57f318b3] # to [be5bc6fef85bbfd317b0d94d1b6329167a015a28] # # patch "file_io.cc" # from [e39a1c13030c5378ea4cb3aa9621148ce0c8fb7d] # to [d57bf07416e3446ccc8235334a0f890da2f12fc4] # # patch "file_io.hh" # from [16f383f0e4eb6fa8454fce1071880f066fc69bdb] # to [26fcbfa41357c4f16c935aaa6227808f35c292c6] # # patch "key_store.cc" # from [] # to [05ee19ff4146a3ef4b9b1751e5dbf39a55f26a50] # # patch "key_store.hh" # from [] # to [ed83144ff6fdb7f931e8f950b36362bf48b45273] # # patch "keys.cc" # from [cf21506cfc9735f800dccf4d1c3b575fa97bab90] # to [0a144d76856af7fe643c529d9d2536a197fe15cb] # # patch "keys.hh" # from [16c620ec000b13c0ef656a92a59c18660d4c1aaf] # to [ec2036295cdf27e6ac7994fb9952360c364221f6] # # patch "lua.cc" # from [fc053070a0c0a106e6f9a3e5678ef2a427ad1fcd] # to [05347870adbee4f4ceb65293c2b9d1ef7e7eb72a] # # patch "lua.hh" # from [950e24bc4b2c16b258f00aee0c4923ae93f256df] # to [a36a8bff73690208d4d879fc868696315881a465] # # patch "monotone.cc" # from [21be582ccd322a35a5d1864ee1251e5773054619] # to [a9105108afab139e9cad54cc92e5f5bf672f0594] # # patch "netsync.cc" # from [c4933c5062e859a98b69e9dfa872b45a5be919da] # to [f3d10c633250d6a96bc32d11859c6064d243772c] # # patch "options.hh" # from [e3f8114ba090e819215b0b8b370181b273bf16cc] # to [ac7bfa54b40a56f68dcdd40c83f994613aa7ab9f] # # patch "packet.cc" # from [407e15cf1c485e09665a372588be2f99491a17d7] # to [f4ca19df46ef590bb112762f222325ae2ddb98be] # # patch "packet.hh" # from [ec7178c2332473305c0aa7d00c727e338fc7810d] # to [aff613af14460ebbedebdbc0e95c3dfc136f5c02] # # patch "tests/t_cross.at" # from [3c97886280aa50bb3dcef375373f9207175cc927] # to [d4a0e27dcd1f8ad7859485a1785ed3e3ff7c0272] # # patch "tests/t_lua_privkey.at" # from [e85cec14dbef01453f2307c787d8144502bfba77] # to [4ab69dc88079acf06ee05abf7368abcab84019a9] # # patch "tests/t_netsync_absorbs.at" # from [192d6c27f26a2553f8a0044c95832ae611861c49] # to [754c3f6173c7f3a22380904f8dbc20c4174447c6] # # patch "tests/t_netsync_permissions.at" # from [2b2fdda0e34c5c71bad5451e5acdec7416f54162] # to [2e18d7f1e43cc50034a417b6ea6f18c79c5467c3] # # patch "tests/t_short_opts.at" # from [65969eb19ec23c4f37e9583f6556104ac8e9f6d2] # to [f633e5c60ec847a5b9cc42643e1be2d6c89788bd] # # patch "tests/t_subdirs.at" # from [07ccac7d46f5adba7cc61ad03277398004d4232d] # to [6dc74192163aea63aaa8796c6864ab291fb73120] # # patch "testsuite.at" # from [9843a5c53ec471614fc6d5ef5dfcf16fc6995608] # to [925c7087ae848ad85724183ab80d3a042686571d] # # patch "vocab.hh" # from [22382ac1bdffec21170a88ff2580fe39b508243f] # to [f14bf372280ec9b5bc886598249ec518dfa114d3] # ======================================================================== --- ChangeLog 21b992ed451a886bfd5ce7934d11bdbe4a9762e0 +++ ChangeLog e3cb4410fbf089f5ba258314b15d2fb0494ec871 @@ -1,3 +1,25 @@ +2005-09-25 Timothy Brownawell + + Move private keys outside the database. + They're now stored as keypairs in ~/.monotone/keys + Details: + * keystore.{cc,hh}: handle storage of keypairs + * tests*: Specify keystore location when running the testsuite. + This prevents it from polluting the user's keystore. + * database{cc,hh}: remove ability to put privkeys. They can still + be listed, retrieved, and deleted, to support + * commands.cc: New command, extract_keys. Copies private keys from + the database to the keystore. + * vocab.hh: add keypair type, like pair, except that + the members are named pub and priv. + * packet.cc: Clean up read_packets. + * packet.{cc,hh}: privkey packets no longer exists, handle keypair + packets instead. + * file_io.{cc,hh}: add read_directory because tree_walker needs to + be in the wc. add a write_data that works outside the wc. + * Makefile.am: add keystore.{cc,hh} + * others: update to work with new key storage + 2005-09-21 Timothy Brownawell * contrib/usher.cc: better error checking ======================================================================== --- Makefile.am 37d234104aa51fb9f169ed0be12208e4b5be0669 +++ Makefile.am c3ca41e70b719d5d345383a893a7d4ba7823160c @@ -11,6 +11,7 @@ work.cc work.hh \ cert.cc cert.hh \ database.cc database.hh \ + key_store.cc key_store.hh \ file_io.cc file_io.hh \ keys.cc keys.hh \ manifest.cc manifest.hh \ ======================================================================== --- app_state.cc 4c8ba6a5cdc3fa945a945e19ea63e1fc857d3ca1 +++ app_state.cc 58db112140d05dcb5ffaea7e7eea44f04662312f @@ -31,9 +31,11 @@ static string const database_option("database"); static string const branch_option("branch"); static string const key_option("key"); +static string const keydir_option("keydir"); app_state::app_state() - : branch_name(""), db(system_path()), stdhooks(true), rcfiles(true), diffs(false), + : branch_name(""), db(system_path()), keys(this), stdhooks(true), + rcfiles(true), diffs(false), no_merges(false), set_default(false), verbose(false), search_root("/"), depth(-1), last(-1), diff_format(unified_diff), diff_args_provided(false), use_lca(false), execute(false) @@ -253,6 +255,14 @@ } void +app_state::set_key_dir(system_path const & filename) +{ + if (!filename.empty()) keys.set_key_dir(filename); + + options[keydir_option] = filename.as_internal(); +} + +void app_state::set_branch(utf8 const & branch) { branch_name = branch(); ======================================================================== --- app_state.hh 869c3e0393d765e37fc17ae117ccbbe69776b177 +++ app_state.hh b310058ad20b794c7b4347eb35d495b980f149cd @@ -20,6 +20,7 @@ #include "work.hh" #include "vocab.hh" #include "paths.hh" +#include "key_store.hh" // this class is supposed to hold all (or.. well, most) of the state of the // application, barring some unfortunate static objects like the debugging / @@ -34,6 +35,7 @@ utf8 branch_name; database db; lua_hooks lua; + key_store keys; bool stdhooks; bool rcfiles; bool diffs; @@ -92,6 +94,7 @@ void make_branch_sticky(); void set_database(system_path const & filename); + void set_key_dir(system_path const & filename); void set_signing_key(utf8 const & key); void set_root(system_path const & root); void set_message(utf8 const & message); ======================================================================== --- cert.cc 43de7f5240b7caa27fc590630afe3ffeea0bfa0b +++ cert.cc fdf5188e96328a6d86fcac31ff3caf0b8ebb7602 @@ -319,69 +319,71 @@ priv_key_exists(app_state & app, rsa_keypair_id const & id) { - if (app.db.private_key_exists(id)) + if (app.keys.key_pair_exists(id)) return true; - - base64< arc4 > dummy; - if (app.lua.hook_get_priv_key(id, dummy)) + keypair kp; + + if (app.lua.hook_get_key_pair(id, kp)) return true; return false; } -// Loads a private key for a given key id, from either a lua hook -// or the database. This will bomb out if the same keyid exists +// Loads a key pair for a given key id, from either a lua hook +// or the key store. This will bomb out if the same keyid exists // in both with differing contents. void -load_priv_key(app_state & app, +load_key_pair(app_state & app, rsa_keypair_id const & id, - base64< arc4 > & priv) + keypair & kp) { - static std::map > > privkeys; - bool persist_ok = (!privkeys.empty()) || app.lua.hook_persist_phrase_ok(); + static std::map keys; + bool persist_ok = (!keys.empty()) || app.lua.hook_persist_phrase_ok(); - if (persist_ok - && privkeys.find(id) != privkeys.end()) + if (persist_ok && keys.find(id) != keys.end()) { - priv = privkeys[id]; + kp = keys[id]; } else { - base64< arc4 > dbkey, luakey; - bool havedb = false, havelua = false; + keypair kskeys, luakeys; + bool haveks = false, havelua = false; - if (app.db.private_key_exists(id)) + if (app.keys.key_pair_exists(id)) { - app.db.get_key(id, dbkey); - havedb = true; + app.keys.get_key_pair(id, kskeys); + haveks = true; } - havelua = app.lua.hook_get_priv_key(id, luakey); + havelua = app.lua.hook_get_key_pair(id, luakeys); - N(havedb || havelua, - F("no private key '%s' found in database or get_priv_key hook") % id); + N(haveks || havelua, + F("no private key '%s' found in key store or get_priv_key hook") % id); if (havelua) { - if (havedb) + if (haveks) { // We really don't want the database key and the rcfile key // to differ. - N(keys_match(id, dbkey, id, luakey), - F("mismatch between private key '%s' in database" - " and get_priv_key hook") % id); + N(keys_match(id, kskeys.priv, id, luakeys.priv) + && keys_match(id, kskeys.pub, id, luakeys.pub), + F("mismatch between key '%s' in key store" + " and get_key_pair hook") % id); } - priv = luakey; + kp = luakeys; } - else if (havedb) + else if (haveks) { - priv = dbkey; + kp = kskeys; } if (persist_ok) - privkeys.insert(make_pair(id, priv)); + { + keys.insert(make_pair(id, kp)); + } } } @@ -389,12 +391,14 @@ calculate_cert(app_state & app, cert & t) { string signed_text; - base64< arc4 > priv; + keypair kp; cert_signable_text(t, signed_text); - load_priv_key(app, t.key, priv); + load_key_pair(app, t.key, kp); + if (!app.db.public_key_exists(t.key)) + app.db.put_key(t.key, kp.pub); - make_signature(app, t.key, priv, signed_text, t.sig); + make_signature(app, t.key, kp.priv, signed_text, t.sig); } cert_status @@ -452,7 +456,7 @@ } vector all_privkeys; - app.db.get_private_keys(all_privkeys); + app.keys.get_keys(all_privkeys); if (all_privkeys.size() != 1) return false; else ======================================================================== --- cert.hh e2b1d86c8f2610e854afed7ab661c4c2554e7004 +++ cert.hh 7e7e906bb36445343533611871cd5d3680cb7f99 @@ -55,9 +55,9 @@ void cert_signable_text(cert const & t,std::string & out); cert_status check_cert(app_state & app, cert const & t); bool priv_key_exists(app_state & app, rsa_keypair_id const & id); -void load_priv_key(app_state & app, - rsa_keypair_id const & id, - base64< arc4 > & priv); +void load_key_pair(app_state & app, + rsa_keypair_id const & id, + keypair & kp); void calculate_cert(app_state & app, cert & t); void make_simple_cert(hexenc const & id, cert_name const & nm, ======================================================================== --- commands.cc 61185ae5cc7cd776c25ca1655199a314bdd40378 +++ commands.cc 5e3307a36823404a734e65224da9966461d8e382 @@ -608,9 +608,15 @@ transaction_guard guard(app.db); if (args.size() == 0) - app.db.get_key_ids("", pubkeys, privkeys); + { + app.db.get_key_ids("", pubkeys); + app.keys.get_key_ids("", privkeys); + } else if (args.size() == 1) - app.db.get_key_ids(idx(args, 0)(), pubkeys, privkeys); + { + app.db.get_key_ids(idx(args, 0)(), pubkeys); + app.keys.get_key_ids(idx(args, 0)(), privkeys); + } else throw usage(name); @@ -636,10 +642,10 @@ for (size_t i = 0; i < privkeys.size(); ++i) { rsa_keypair_id keyid = idx(privkeys, i)(); - base64< arc4 > priv_encoded; + keypair kp; hexenc hash_code; - app.db.get_key(keyid, priv_encoded); - key_hash_code(keyid, priv_encoded, hash_code); + app.keys.get_key_pair(keyid, kp); + key_hash_code(keyid, kp.priv, hash_code); cout << hash_code << " " << keyid << endl; } cout << endl; @@ -808,15 +814,14 @@ rsa_keypair_id ident; internalize_rsa_keypair_id(idx(args, 0), ident); - N(! app.db.key_exists(ident), - F("key '%s' already exists in database") % ident); + N(! (app.db.public_key_exists(ident) || app.keys.key_pair_exists(ident)), + F("key '%s' already exists") % ident); - base64 pub; - base64< arc4 > priv; + keypair kp; P(F("generating key-pair '%s'\n") % ident); - generate_key_pair(app.lua, ident, pub, priv); + generate_key_pair(app.lua, ident, kp); P(F("storing key-pair '%s' in database\n") % ident); - app.db.put_key_pair(ident, pub, priv); + app.keys.put_key_pair(ident, kp); guard.commit(); } @@ -847,8 +852,15 @@ key_deleted = true; } + if (app.keys.key_pair_exists(ident)) + { + P(F("dropping key pair '%s' from key store\n\n") % ident); + app.keys.delete_key(ident); + key_deleted = true; + } + N(key_deleted, - F("public or private key '%s' does not exist in database") % idx(args, 0)()); + F("public or private key '%s' does not exist") % idx(args, 0)()); guard.commit(); } @@ -864,14 +876,14 @@ rsa_keypair_id ident; internalize_rsa_keypair_id(idx(args, 0), ident); - N(app.db.key_exists(ident), - F("key '%s' does not exist in database") % ident); + N(app.keys.key_pair_exists(ident), + F("key '%s' does not exist in the key store") % ident); - base64< arc4 > key; - app.db.get_key(ident, key); - change_key_passphrase(app.lua, ident, key); - app.db.delete_private_key(ident); - app.db.put_key(ident, key); + keypair key; + app.keys.get_key_pair(ident, key); + change_key_passphrase(app.lua, ident, key.priv); + app.keys.delete_key(ident); + app.keys.put_key_pair(ident, key); P(F("passphrase changed\n")); guard.commit(); @@ -910,7 +922,7 @@ encode_base64(val, val_encoded); cert t(ident, name, val_encoded, key); - + packet_db_writer dbw(app); calculate_cert(app, t); dbw.consume_revision_cert(revision(t)); @@ -1807,12 +1819,24 @@ throw usage(name); rsa_keypair_id ident(idx(args, 0)()); - N(app.db.public_key_exists(ident), - F("public key '%s' does not exist in database") % idx(args, 0)()); + bool exists(false); + base64< rsa_pub_key > key; + if (app.db.public_key_exists(ident)) + { + app.db.get_key(ident, key); + exists = true; + } + if (app.keys.key_pair_exists(ident)) + { + keypair kp; + app.keys.get_key_pair(ident, kp); + key = kp.pub; + exists = true; + } + N(exists, + F("public key '%s' does not exist") % idx(args, 0)()); packet_writer pw(cout); - base64< rsa_pub_key > key; - app.db.get_key(ident, key); pw.consume_public_key(ident, key); } @@ -1823,16 +1847,14 @@ throw usage(name); rsa_keypair_id ident(idx(args, 0)()); - N(app.db.private_key_exists(ident) && app.db.private_key_exists(ident), - F("public and private key '%s' do not exist in database") % idx(args, 0)()); + N(app.keys.key_pair_exists(ident), + F("public and private key '%s' do not exist in key store") + % idx(args, 0)()); packet_writer pw(cout); - base64< arc4 > privkey; - base64< rsa_pub_key > pubkey; - app.db.get_key(ident, privkey); - app.db.get_key(ident, pubkey); - pw.consume_public_key(ident, pubkey); - pw.consume_private_key(ident, privkey); + keypair kp; + app.keys.get_key_pair(ident, kp); + pw.consume_key_pair(ident, kp); } @@ -3729,4 +3751,20 @@ app.db.clear_var(k); } +CMD(extract_keys, N_("debug"), N_(""), + N_("copy all private keys in the database into the keystore"), + OPT_NONE) +{ + std::vector privkeys; + app.db.get_private_keys(privkeys); + for (std::vector::const_iterator i = privkeys.begin(); + i != privkeys.end(); ++i) + { + keypair kp; + app.db.get_key(*i, kp.priv); + app.db.get_key(*i, kp.pub); + app.keys.put_key_pair(*i, kp); + } +} + }; // namespace commands ======================================================================== --- database.cc 83c0e4fe4b672da030bc50966c118ac0c2c98017 +++ database.cc a1452325ac8f6aef8804ec53785ecbac5ee4cea6 @@ -1446,11 +1446,9 @@ void database::get_key_ids(string const & pattern, - vector & pubkeys, - vector & privkeys) + vector & pubkeys) { pubkeys.clear(); - privkeys.clear(); results res; if (pattern != "") @@ -1463,17 +1461,6 @@ for (size_t i = 0; i < res.size(); ++i) pubkeys.push_back(res[i][0]); - - if (pattern != "") - fetch(res, one_col, any_rows, - "SELECT id FROM private_keys WHERE id GLOB ?", - pattern.c_str()); - else - fetch(res, one_col, any_rows, - "SELECT id FROM private_keys"); - - for (size_t i = 0; i < res.size(); ++i) - privkeys.push_back(res[i][0]); } void @@ -1488,15 +1475,15 @@ } void -database::get_private_keys(vector & keys) +database::get_public_keys(vector & keys) { - get_keys("private_keys", keys); + get_keys("public_keys", keys); } -void -database::get_public_keys(vector & keys) +void +database::get_private_keys(vector & keys) { - get_keys("public_keys", keys); + get_keys("private_keys", keys); } bool @@ -1525,7 +1512,7 @@ return false; } -bool +bool database::private_key_exists(rsa_keypair_id const & id) { results res; @@ -1538,11 +1525,6 @@ return false; } -bool -database::key_exists(rsa_keypair_id const & id) -{ - return public_key_exists(id) || private_key_exists(id); -} void database::get_pubkey(hexenc const & hash, @@ -1569,17 +1551,6 @@ } void -database::get_key(rsa_keypair_id const & priv_id, - base64< arc4 > & priv_encoded) -{ - results res; - fetch(res, one_col, one_col, - "SELECT keydata FROM private_keys WHERE id = ?", - priv_id().c_str()); - priv_encoded = res[0][0]; -} - -void database::put_key(rsa_keypair_id const & pub_id, base64 const & pub_encoded) { @@ -1592,29 +1563,17 @@ thash().c_str(), pub_id().c_str(), pub_encoded().c_str()); } -void -database::put_key(rsa_keypair_id const & priv_id, - base64< arc4 > const & priv_encoded) +void +database::get_key(rsa_keypair_id const & priv_id, + base64< arc4 > & priv_encoded) { - hexenc thash; - key_hash_code(priv_id, priv_encoded, thash); - E(!private_key_exists(priv_id), - F("another key with name '%s' already exists") % priv_id); - execute("INSERT INTO private_keys VALUES(?, ?, ?)", - thash().c_str(), priv_id().c_str(), priv_encoded().c_str()); + results res; + fetch(res, one_col, one_col, + "SELECT keydata FROM private_keys WHERE id = ?", + priv_id().c_str()); + priv_encoded = res[0][0]; } -void -database::put_key_pair(rsa_keypair_id const & id, - base64 const & pub_encoded, - base64< arc4 > const & priv_encoded) -{ - transaction_guard guard(*this); - put_key(id, pub_encoded); - put_key(id, priv_encoded); - guard.commit(); -} - void database::delete_private_key(rsa_keypair_id const & pub_id) { ======================================================================== --- database.hh 1ac20ac00fe3bb34f5a72c03d3abbfbc57f318b3 +++ database.hh be5bc6fef85bbfd317b0d94d1b6329167a015a28 @@ -318,17 +318,16 @@ // crypto key / cert operations void get_key_ids(std::string const & pattern, - std::vector & pubkeys, - std::vector & privkeys); + std::vector & pubkeys); void get_private_keys(std::vector & privkeys); + void get_public_keys(std::vector & pubkeys); - bool key_exists(rsa_keypair_id const & id); - bool public_key_exists(hexenc const & hash); bool public_key_exists(rsa_keypair_id const & id); bool private_key_exists(rsa_keypair_id const & id); + void get_pubkey(hexenc const & hash, rsa_keypair_id & id, @@ -337,20 +336,13 @@ void get_key(rsa_keypair_id const & id, base64 & pub_encoded); - void get_key(rsa_keypair_id const & id, - base64< arc4 > & priv_encoded); - void put_key(rsa_keypair_id const & id, base64 const & pub_encoded); - - void put_key(rsa_keypair_id const & id, - base64< arc4 > const & priv_encoded); - - void put_key_pair(rsa_keypair_id const & pub_id, - base64 const & pub_encoded, - base64< arc4 > const & priv_encoded); + void get_key(rsa_keypair_id const & id, + base64< arc4 > & priv_encoded); void delete_private_key(rsa_keypair_id const & pub_id); + void delete_public_key(rsa_keypair_id const & pub_id); // note: this section is ridiculous. please do something about it. ======================================================================== --- file_io.cc e39a1c13030c5378ea4cb3aa9621148ce0c8fb7d +++ file_io.cc d57bf07416e3446ccc8235334a0f890da2f12fc4 @@ -315,7 +315,30 @@ dat = tmp2; } +void read_directory(system_path const & path, + std::vector & files, + std::vector & dirs) +{ + files.clear(); + dirs.clear(); + fs::directory_iterator ei; + for(fs::directory_iterator di(path.as_external()); + di != ei; ++di) + { + fs::path entry = *di; + if (!fs::exists(entry) + || di->string() == "." + || di->string() == "..") + continue; + if (fs::is_directory(entry)) + dirs.push_back(utf8(entry.leaf())); + else + files.push_back(utf8(entry.leaf())); + } +} + + // This function can only be called once per run. static void read_data_stdin(data & dat) @@ -348,18 +371,13 @@ static void write_data_impl(any_path const & p, - data const & dat) + data const & dat, + any_path const & tmp) { N(!directory_exists(p), F("file '%s' cannot be overwritten as data; it is a directory") % p); make_dir_for(p); - - // we write, non-atomically, to MT/data.tmp. - // nb: no mucking around with multiple-writer conditions. we're a - // single-user single-threaded program. you get what you paid for. - assert_path_is_directory(bookkeeping_root); - bookkeeping_path tmp = bookkeeping_root / "data.tmp"; { // data.tmp opens @@ -376,6 +394,18 @@ fs::rename(mkdir(tmp), mkdir(p)); } +static void +write_data_impl(any_path const & p, + data const & dat) +{ + // we write, non-atomically, to MT/data.tmp. + // nb: no mucking around with multiple-writer conditions. we're a + // single-user single-threaded program. you get what you paid for. + assert_path_is_directory(bookkeeping_root); + bookkeeping_path tmp = bookkeeping_root / "data.tmp"; + write_data_impl(p, dat, tmp); +} + void write_data(file_path const & path, data const & dat) { @@ -416,6 +446,14 @@ write_data(path, data(tmp2)); } +void +write_data(system_path const & path, + data const & data, + system_path const & tmpdir) +{ + write_data_impl(path, data, tmpdir / "data.tmp"); +} + tree_walker::~tree_walker() {} static void ======================================================================== --- file_io.hh 16f383f0e4eb6fa8454fce1071880f066fc69bdb +++ file_io.hh 26fcbfa41357c4f16c935aaa6227808f35c292c6 @@ -69,6 +69,10 @@ data & dat, lua_hooks & lua); +void read_directory(system_path const & path, + std::vector & files, + std::vector & dirs); + // This function knows that "-" means "stdin". void read_data_for_command_line(utf8 const & path, data & dat); @@ -83,6 +87,14 @@ data const & dat, lua_hooks & lua); +// Version that takes a system_path. To work with the "somewhat atomic" +// goal, it also takes as an argument the place to put the temp file. Whoever +// uses this is responsible to make sure that the tmpdir argument is somewhere +// that the file can be atomically renamed from (same file system) +void write_data(system_path const & path, + data const & data, + system_path const & tmpdir); + class tree_walker { public: ======================================================================== --- key_store.cc +++ key_store.cc 05ee19ff4146a3ef4b9b1751e5dbf39a55f26a50 @@ -0,0 +1,228 @@ +#include + +#include "key_store.hh" +#include "file_io.hh" +#include "packet.hh" +#include "keys.hh" +#include "globish.hh" + +struct keyreader : public packet_consumer +{ + key_store * ks; + + keyreader(key_store * k): ks(k) {} + virtual void consume_file_data(file_id const & ident, + file_data const & dat) + {E(false, F("Extraneous data in key store."));} + virtual void consume_file_delta(file_id const & id_old, + file_id const & id_new, + file_delta const & del) + {E(false, F("Extraneous data in key store."));} + virtual void consume_file_reverse_delta(file_id const & id_new, + file_id const & id_old, + file_delta const & del) + {E(false, F("Extraneous data in key store."));} + + + virtual void consume_manifest_data(manifest_id const & ident, + manifest_data const & dat) + {E(false, F("Extraneous data in key store."));} + virtual void consume_manifest_delta(manifest_id const & id_old, + manifest_id const & id_new, + manifest_delta const & del) + {E(false, F("Extraneous data in key store."));} + virtual void consume_manifest_reverse_delta(manifest_id const & id_new, + manifest_id const & id_old, + manifest_delta const & del) + {E(false, F("Extraneous data in key store."));} + + + virtual void consume_revision_data(revision_id const & ident, + revision_data const & dat) + {E(false, F("Extraneous data in key store."));} + virtual void consume_revision_cert(revision const & t) + {E(false, F("Extraneous data in key store."));} + + + virtual void consume_public_key(rsa_keypair_id const & ident, + base64< rsa_pub_key > const & k) + {E(false, F("Extraneous data in key store."));} + + virtual void consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp) + { + E(!ks->key_pair_exists(ident), + F("Key store has multiple keys with id '%s'.") % ident); + ks->keys.insert(std::make_pair(ident, kp)); + hexenc hash; + key_hash_code(ident, kp.pub, hash); + ks->hashes.insert(std::make_pair(hash, ident)); + L(F("Read key pair '%s' from key store.") % ident); + } +}; + +key_store::key_store(app_state * a): have_read(false), app(a) +{ + key_dir = system_path(get_homedir()) / ".monotone/keys"; +} +void +key_store::set_key_dir(system_path const & kd) +{ + key_dir = kd; +} + +void +key_store::read_key_dir() +{ + std::vector key_files, dirs; + if (directory_exists(key_dir)) + read_directory(key_dir, key_files, dirs); + keyreader kr(this); + for (std::vector::const_iterator i = key_files.begin(); + i != key_files.end(); ++i) + { + data dat; + read_data(key_dir / (*i)(), dat); + std::istringstream is(dat()); + read_packets(is, kr); + } +} + +void +key_store::maybe_read_key_dir() +{ + if (have_read) + return; + have_read = true; + read_key_dir(); +} + +void +key_store::ensure_in_database(rsa_keypair_id const & ident) +{ + maybe_read_key_dir(); + if (app->db.public_key_exists(ident)) + return; + std::map::iterator i = keys.find(ident); + I(i != keys.end()); + app->db.put_key(ident, i->second.pub); +} + +bool +key_store::try_ensure_in_db(hexenc const & hash) +{ + std::map, rsa_keypair_id>::const_iterator i = hashes.find(hash); + if (i == hashes.end()) + return false; + ensure_in_database(i->second); + return true; +} + +void +key_store::get_key_ids(std::string const & pattern, + std::vector & priv) +{ + maybe_read_key_dir(); + priv.clear(); + utf8 inc(pattern); + if (pattern.empty()) + inc = utf8("*"); + globish_matcher gm(inc, utf8("")); + for (std::map::const_iterator + i = keys.begin(); i != keys.end(); ++i) + { + if (gm((i->first)())) + priv.push_back(i->first); + } +} + +void +key_store::get_keys(std::vector & priv) +{ + maybe_read_key_dir(); + priv.clear(); + for (std::map::const_iterator + i = keys.begin(); i != keys.end(); ++i) + { + priv.push_back(i->first); + } +} + +bool +key_store::key_pair_exists(rsa_keypair_id const & ident) +{ + maybe_read_key_dir(); + return keys.find(ident) != keys.end(); +} + +void +key_store::get_key_pair(rsa_keypair_id const & ident, + keypair & kp) +{ + maybe_read_key_dir(); + std::map::const_iterator i = keys.find(ident); + I(i != keys.end()); + kp = i->second; +} + +namespace +{ + // filename is the keypair id, except that some characters can't be put in + // filenames (especially on windows). + void + get_filename(rsa_keypair_id const & ident, std::string & filename) + { + filename = ident(); + for (unsigned int i = 0; i < filename.size(); ++i) + if (std::string("+").find(filename[i]) != std::string::npos) + filename.at(i) = '_'; + } +} + +void +key_store::get_key_file(rsa_keypair_id const & ident, + system_path & file) +{ + std::string leaf; + get_filename(ident, leaf); + file = key_dir / leaf; +} + +void +key_store::write_key(rsa_keypair_id const & ident) +{ + keypair kp; + get_key_pair(ident, kp); + std::ostringstream oss; + packet_writer pw(oss); + pw.consume_key_pair(ident, kp); + data dat(oss.str()); + system_path file; + get_key_file(ident, file); + write_data(file, dat, key_dir); +} + +void +key_store::put_key_pair(rsa_keypair_id const & ident, + keypair const & kp) +{ + maybe_read_key_dir(); + L(F("putting key pair '%s'") % ident); + I(keys.insert(std::make_pair(ident, kp)).second); + hexenc hash; + key_hash_code(ident, kp.pub, hash); + I(hashes.insert(std::make_pair(hash, ident)).second); + write_key(ident); +} + +void +key_store::delete_key(rsa_keypair_id const & ident) +{ + maybe_read_key_dir(); + std::map::iterator i = keys.find(ident); + if (i != keys.end()) + keys.erase(i); + system_path file; + get_key_file(ident, file); + delete_file(file); +} ======================================================================== --- key_store.hh +++ key_store.hh ed83144ff6fdb7f931e8f950b36362bf48b45273 @@ -0,0 +1,50 @@ +#ifndef __KEY_STORE_H__ +#define __KEY_STORE_H__ + +#include + +#include "vocab.hh" +#include "paths.hh" +#include "platform.hh" + +struct app_state; + +struct keyreader; + +class key_store +{ + friend struct keyreader; + system_path key_dir; + bool have_read; + app_state * app; + std::map keys; + std::map, rsa_keypair_id> hashes; + + void get_key_file(rsa_keypair_id const & ident, system_path & file); + void write_key(rsa_keypair_id const & ident); + void read_key_dir(); + void maybe_read_key_dir(); +public: + key_store(app_state * a); + void set_key_dir(system_path const & kd); + + void ensure_in_database(rsa_keypair_id const & ident); + bool try_ensure_in_db(hexenc const & hash); + + void get_key_ids(std::string const & pattern, + std::vector & priv); + + void get_keys(std::vector & priv); + + bool key_pair_exists(rsa_keypair_id const & ident); + + void get_key_pair(rsa_keypair_id const & ident, + keypair & kp); + + void put_key_pair(rsa_keypair_id const & ident, + keypair const & kp); + + void delete_key(rsa_keypair_id const & ident); +}; + +#endif ======================================================================== --- keys.cc cf21506cfc9735f800dccf4d1c3b575fa97bab90 +++ keys.cc 0a144d76856af7fe643c529d9d2536a197fe15cb @@ -146,8 +146,7 @@ void generate_key_pair(lua_hooks & lua, // to hook for phrase rsa_keypair_id const & id, // to prompting user for phrase - base64 & pub_out, - base64< arc4 > & priv_out, + keypair & kp_out, string const unit_test_passphrase) { @@ -179,12 +178,12 @@ raw_pub_key = string(reinterpret_cast(pubkey.begin()), pubkey.size()); // if all that worked, we can return our results to caller - encode_base64(raw_priv_key, priv_out); - encode_base64(raw_pub_key, pub_out); + encode_base64(raw_priv_key, kp_out.priv); + encode_base64(raw_pub_key, kp_out.pub); L(F("generated %d-byte public key\n" "generated %d-byte (encrypted) private key\n") - % pub_out().size() - % priv_out().size()); + % kp_out.pub().size() + % kp_out.priv().size()); } void @@ -505,19 +504,17 @@ app_state & app) { N(priv_key_exists(app, key), - F("no private key '%s' found in database or get_priv_key hook") % key); - N(app.db.public_key_exists(key), - F("no public key '%s' found in database") % key); - base64 pub; - app.db.get_key(key, pub); - base64< arc4 > priv; - load_priv_key(app, key, priv); + F("no key pair '%s' found in key store or get_priv_key hook") % key); +// N(app.db.public_key_exists(key), +// F("no public key '%s' found in database") % key); + keypair kp; + load_key_pair(app, key, kp); if (app.lua.hook_persist_phrase_ok()) { string plaintext("hi maude"); base64 sig; - make_signature(app, key, priv, plaintext, sig); - N(check_signature(app, key, pub, plaintext, sig), + make_signature(app, key, kp.priv, plaintext, sig); + N(check_signature(app, key, kp.pub, plaintext, sig), F("passphrase for '%s' is incorrect") % key); } } @@ -561,9 +558,8 @@ BOOST_CHECKPOINT("generating key pairs"); rsa_keypair_id key("address@hidden"); - base64 pubkey; - base64< arc4 > privkey; - generate_key_pair(app.lua, key, pubkey, privkey, "address@hidden"); + keypair kp; + generate_key_pair(app.lua, key, kp, "address@hidden"); BOOST_CHECKPOINT("signing plaintext"); string plaintext("test string to sign"); ======================================================================== --- keys.hh 16c620ec000b13c0ef656a92a59c18660d4c1aaf +++ keys.hh ec2036295cdf27e6ac7994fb9952360c364221f6 @@ -18,8 +18,7 @@ void generate_key_pair(lua_hooks & lua, // to hook for phrase rsa_keypair_id const & id, // to prompting user for phrase - base64 & pub, - base64< arc4 > & priv, + keypair & kp, // Used for unit tests only: std::string const unit_test_passphrase = std::string()); ======================================================================== --- lua.cc fc053070a0c0a106e6f9a3e5678ef2a427ad1fcd +++ lua.cc 05347870adbee4f4ceb65293c2b9d1ef7e7eb72a @@ -834,8 +834,8 @@ } bool -lua_hooks::hook_get_priv_key(rsa_keypair_id const & k, - base64< arc4 > & priv_key ) +lua_hooks::hook_get_key_pair(rsa_keypair_id const & k, + keypair & kp) { string key; bool ok = Lua(st) @@ -845,7 +845,11 @@ .extract_str(key) .ok(); - priv_key = key; + size_t pos = key.find("#"); + if (pos == std::string::npos) + return false; + kp.pub = key.substr(0, pos); + kp.priv = key.substr(pos+1); return ok; } ======================================================================== --- lua.hh 950e24bc4b2c16b258f00aee0c4923ae93f256df +++ lua.hh a36a8bff73690208d4d879fc868696315881a465 @@ -42,8 +42,8 @@ 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(cert_value const & branchname, rsa_keypair_id & k); - bool lua_hooks::hook_get_priv_key(rsa_keypair_id const & k, - base64< arc4 > & priv_key ); + bool hook_get_key_pair(rsa_keypair_id const & k, + keypair & kp); bool hook_get_passphrase(rsa_keypair_id const & k, std::string & phrase); bool hook_get_author(cert_value const & branchname, std::string & author); bool hook_edit_comment(std::string const & commentary, ======================================================================== --- monotone.cc 21be582ccd322a35a5d1864ee1251e5773054619 +++ monotone.cc a9105108afab139e9cad54cc92e5f5bf672f0594 @@ -91,6 +91,7 @@ {"db", 'd', POPT_ARG_STRING, &argstr, OPT_DB_NAME, gettext_noop("set name of database"), NULL}, {"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, gettext_noop("limit search for working copy to specified root"), NULL}, {"verbose", 0, POPT_ARG_NONE, NULL, OPT_VERBOSE, gettext_noop("verbose completion output"), NULL}, + {"keydir", 0, POPT_ARG_STRING, &argstr, OPT_KEY_DIR, gettext_noop("set location of key store"), NULL}, { NULL, 0, 0, NULL, 0, NULL, NULL } }; @@ -331,6 +332,10 @@ app.set_database(system_path(argstr)); break; + case OPT_KEY_DIR: + app.set_key_dir(system_path(argstr)); + break; + case OPT_TICKER: if (string(argstr) == "dot") ui.set_tick_writer(new tick_write_dot); ======================================================================== --- netsync.cc c4933c5062e859a98b69e9dfa872b45a5be919da +++ netsync.cc f3d10c633250d6a96bc32d11859c6064d243772c @@ -378,7 +378,8 @@ void queue_bye_cmd(); void queue_error_cmd(string const & errmsg); void queue_done_cmd(size_t level, netcmd_item_type type); - void queue_hello_cmd(id const & server, + void queue_hello_cmd(rsa_keypair_id const & key_name, + base64 const & pub_encoded, id const & nonce); void queue_anonymous_cmd(protocol_role role, utf8 const & include_pattern, @@ -650,10 +651,11 @@ void session::set_session_key(rsa_oaep_sha_data const & hmac_key_encrypted) { - base64< arc4 > our_priv; - load_priv_key(app, app.signing_key, our_priv); + keypair our_kp; + load_key_pair(app, app.signing_key, our_kp); string hmac_key; - decrypt_rsa(app.lua, app.signing_key, our_priv, hmac_key_encrypted, hmac_key); + decrypt_rsa(app.lua, app.signing_key, our_kp.priv, + hmac_key_encrypted, hmac_key); set_session_key(hmac_key); } @@ -1453,18 +1455,11 @@ } void -session::queue_hello_cmd(id const & server, +session::queue_hello_cmd(rsa_keypair_id const & key_name, + base64 const & pub_encoded, id const & nonce) { - netcmd cmd; - hexenc server_encoded; - encode_hexenc(server, server_encoded); - - rsa_keypair_id key_name; - base64 pub_encoded; rsa_pub_key pub; - - app.db.get_pubkey(server_encoded, key_name, pub_encoded); decode_base64(pub_encoded, pub); cmd.write_hello_cmd(key_name, pub, nonce); write_netcmd_and_try_flush(cmd); @@ -1834,20 +1829,20 @@ if (app.signing_key() != "") { - // get our public key for its hash identifier - base64 our_pub; + // get our key pair + keypair our_kp; + load_key_pair(app, app.signing_key, our_kp); + + // get the hash identifier for our pubkey hexenc our_key_hash; id our_key_hash_raw; - app.db.get_key(app.signing_key, our_pub); - key_hash_code(app.signing_key, our_pub, our_key_hash); + key_hash_code(app.signing_key, our_kp.pub, our_key_hash); decode_hexenc(our_key_hash, our_key_hash_raw); - // get our private key and make a signature + // make a signature base64 sig; rsa_sha1_signature sig_raw; - base64< arc4 > our_priv; - load_priv_key(app, app.signing_key, our_priv); - make_signature(app, app.signing_key, our_priv, nonce(), sig); + make_signature(app, app.signing_key, our_kp.priv, nonce(), sig); decode_base64(sig, sig_raw); // make a new nonce of our own and send off the 'auth' @@ -1967,9 +1962,14 @@ if (!app.db.public_key_exists(their_key_hash)) { - W(F("remote public key hash '%s' is unknown\n") % their_key_hash); - this->saved_nonce = id(""); - return false; + // if it's not in the db, it still could be in the keystore if we + // have the private key that goes with it + if (!app.keys.try_ensure_in_db(their_key_hash)) + { + W(F("remote public key hash '%s' is unknown\n") % their_key_hash); + this->saved_nonce = id(""); + return false; + } } // get their public key @@ -3229,13 +3229,9 @@ void session::begin_service() { - base64 pub_encoded; - app.db.get_key(app.signing_key, pub_encoded); - hexenc keyhash; - id keyhash_raw; - key_hash_code(app.signing_key, pub_encoded, keyhash); - decode_hexenc(keyhash, keyhash_raw); - queue_hello_cmd(keyhash_raw(), mk_nonce()); + keypair kp; + app.keys.get_key_pair(app.signing_key, kp); + queue_hello_cmd(app.signing_key, kp.pub, mk_nonce()); } void @@ -3277,6 +3273,8 @@ W(F("input buffer for peer %s is overfull after netcmd dispatch\n") % peer_id); guard.commit(); maybe_say_goodbye(); + if (!ret) + P(F("failed to process '%s' packet") % cmd.get_cmd_code()); return ret; } catch (bad_decode & bd) ======================================================================== --- options.hh e3f8114ba090e819215b0b8b370181b273bf16cc +++ options.hh ac7bfa54b40a56f68dcdd40c83f994613aa7ab9f @@ -43,3 +43,4 @@ #define OPT_EXTERNAL_DIFF_ARGS 34 #define OPT_LCA 35 #define OPT_EXECUTE 36 +#define OPT_KEY_DIR 37 ======================================================================== --- packet.cc 407e15cf1c485e09665a372588be2f99491a17d7 +++ packet.cc f4ca19df46ef590bb112762f222325ae2ddb98be @@ -305,18 +305,18 @@ }; class -delayed_private_key_packet +delayed_keypair_packet : public delayed_packet { rsa_keypair_id id; - base64< arc4 > key; + keypair kp; public: - delayed_private_key_packet(rsa_keypair_id const & id, - base64< arc4 > key) - : id(id), key(key) + delayed_keypair_packet(rsa_keypair_id const & id, + keypair const & kp) + : id(id), kp(kp) {} virtual void apply_delayed_packet(packet_db_writer & pw); - virtual ~delayed_private_key_packet(); + virtual ~delayed_keypair_packet(); }; void @@ -426,13 +426,13 @@ } void -delayed_private_key_packet::apply_delayed_packet(packet_db_writer & pw) +delayed_keypair_packet::apply_delayed_packet(packet_db_writer & pw) { L(F("writing delayed private key %s\n") % id()); - pw.consume_private_key(id, key); + pw.consume_key_pair(id, kp); } -delayed_private_key_packet::~delayed_private_key_packet() +delayed_keypair_packet::~delayed_keypair_packet() { // keys don't have dependencies I(all_prerequisites_satisfied()); @@ -461,10 +461,10 @@ } void -packet_consumer::set_on_privkey_written(boost::function1 +packet_consumer::set_on_keypair_written(boost::function1 const & x) { - on_privkey_written=x; + on_keypair_written=x; } @@ -992,22 +992,22 @@ } void -packet_db_writer::consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k) +packet_db_writer::consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp) { transaction_guard guard(pimpl->app.db); if (! pimpl->take_keys) { - W(F("skipping prohibited private key %s\n") % ident); + W(F("skipping prohibited key pair %s\n") % ident); return; } - if (! pimpl->app.db.private_key_exists(ident)) + if (! pimpl->app.keys.key_pair_exists(ident)) { - pimpl->app.db.put_key(ident, k); - if(on_privkey_written) on_privkey_written(ident); + pimpl->app.keys.put_key_pair(ident, kp); + if(on_keypair_written) on_keypair_written(ident); } else - L(F("skipping existing private key %s\n") % ident); + L(F("skipping existing key pair %s\n") % ident); ++(pimpl->count); guard.commit(); } @@ -1085,11 +1085,11 @@ } void -packet_db_valve::set_on_privkey_written(boost::function1 +packet_db_valve::set_on_keypair_written(boost::function1 const & x) { - on_privkey_written=x; - pimpl->writer.set_on_privkey_written(x); + on_keypair_written=x; + pimpl->writer.set_on_keypair_written(x); } void @@ -1159,10 +1159,10 @@ } void -packet_db_valve::consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k) +packet_db_valve::consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp) { - DOIT(delayed_private_key_packet(ident, k)); + DOIT(delayed_keypair_packet(ident, kp)); } #undef DOIT @@ -1277,11 +1277,11 @@ } void -packet_writer::consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k) +packet_writer::consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp) { - ost << "[privkey " << ident() << "]" << endl - << trim_ws(k()) << endl + ost << "[keypair " << ident() << "]" << endl + << trim_ws(kp.pub()) <<"#\n" < const & res) const { - if (res.size() != 17) + if (res.size() != 4) throw oops("matched impossible packet with " + lexical_cast(res.size()) + " matching parts: " + string(res[0].first, res[0].second)); - - if (res[1].matched) + I(res[1].matched); + I(res[2].matched); + I(res[3].matched); + std::string type(res[1].first, res[1].second); + std::string args(res[2].first, res[2].second); + std::string body(res[3].first, res[3].second); + if (regex_match(type, regex("[mfr]data"))) { - L(F("read data packet\n")); - I(res[2].matched); - I(res[3].matched); - string head(res[1].first, res[1].second); - string ident(res[2].first, res[2].second); - base64 > body_packed(trim_ws(string(res[3].first, res[3].second))); - data body; - unpack(body_packed, body); - if (head == "rdata") - cons.consume_revision_data(revision_id(hexenc(ident)), - revision_data(body)); - else if (head == "mdata") - cons.consume_manifest_data(manifest_id(hexenc(ident)), - manifest_data(body)); - else if (head == "fdata") - cons.consume_file_data(file_id(hexenc(ident)), - file_data(body)); + L(F("read data packet")); + require(regex_match(args, regex(ident))); + require(regex_match(body, regex(base))); + base64 > body_packed(trim_ws(body)); + data contents; + unpack(body_packed, contents); + if (type == "rdata") + cons.consume_revision_data(revision_id(hexenc(args)), + revision_data(contents)); + else if (type == "mdata") + cons.consume_manifest_data(manifest_id(hexenc(args)), + manifest_data(contents)); + else if (type == "fdata") + cons.consume_file_data(file_id(hexenc(args)), + file_data(contents)); else - throw oops("matched impossible data packet with head '" + head + "'"); + throw oops("matched impossible data packet with head '" + type + "'"); } - else if (res[4].matched) + else if (regex_match(type, regex("[mf]r?delta"))) { - L(F("read delta packet\n")); - I(res[5].matched); - I(res[6].matched); - I(res[7].matched); - string head(res[4].first, res[4].second); - string src_id(res[5].first, res[5].second); - string dst_id(res[6].first, res[6].second); - base64 > body_packed(trim_ws(string(res[7].first, res[7].second))); - delta body; - unpack(body_packed, body); - if (head == "mdelta") + L(F("read delta packet")); + match_results matches; + require(regex_match(args, matches, regex(ident + sp + ident))); + string src_id(matches[1].first, matches[1].second); + string dst_id(matches[2].first, matches[2].second); + require(regex_match(body, regex(base))); + base64 > body_packed(trim_ws(body)); + delta contents; + unpack(body_packed, contents); + if (type == "mdelta") cons.consume_manifest_delta(manifest_id(hexenc(src_id)), manifest_id(hexenc(dst_id)), - manifest_delta(body)); - else if (head == "fdelta") + manifest_delta(contents)); + else if (type == "fdelta") cons.consume_file_delta(file_id(hexenc(src_id)), file_id(hexenc(dst_id)), - file_delta(body)); - else if (head == "mrdelta") + file_delta(contents)); + else if (type == "mrdelta") cons.consume_manifest_reverse_delta(manifest_id(hexenc(src_id)), manifest_id(hexenc(dst_id)), - manifest_delta(body)); - else if (head == "frdelta") + manifest_delta(contents)); + else if (type == "frdelta") cons.consume_file_reverse_delta(file_id(hexenc(src_id)), file_id(hexenc(dst_id)), - file_delta(body)); + file_delta(contents)); else - throw oops("matched impossible delta packet with head '" + head + "'"); + throw oops("matched impossible delta packet with head '" + + type + "'"); } - else if (res[8].matched) + else if (type == "rcert") { - L(F("read cert packet\n")); - I(res[9].matched); - I(res[10].matched); - I(res[11].matched); - I(res[12].matched); - I(res[13].matched); - string head(res[8].first, res[8].second); - string ident(res[9].first, res[9].second); - string certname(res[10].first, res[10].second); - string key(res[11].first, res[11].second); - string val(res[12].first, res[12].second); - string body(trim_ws(string(res[13].first, res[13].second))); + L(F("read cert packet")); + match_results matches; + require(regex_match(args, matches, regex(ident + sp + certname + + key + sp + base))); + string certid(matches[1].first, matches[1].second); + string name(matches[2].first, matches[2].second); + string keyid(matches[3].first, matches[3].second); + string val(matches[4].first, matches[4].second); + string contents(trim_ws(body)); // canonicalize the base64 encodings to permit searches - cert t = cert(hexenc(ident), - cert_name(certname), + cert t = cert(hexenc(certid), + cert_name(name), base64(canonical_base64(val)), - rsa_keypair_id(key), - base64(canonical_base64(body))); - if (head == "rcert") - cons.consume_revision_cert(revision(t)); - else - throw oops("matched impossible cert packet with head '" + head + "'"); + rsa_keypair_id(keyid), + base64(canonical_base64(contents))); + cons.consume_revision_cert(revision(t)); } - else if (res[14].matched) + else if (type == "pubkey") { - L(F("read key data packet\n")); - I(res[15].matched); - I(res[16].matched); - string head(res[14].first, res[14].second); - string ident(res[15].first, res[15].second); - string body(trim_ws(string(res[16].first, res[16].second))); - if (head == "pubkey") - cons.consume_public_key(rsa_keypair_id(ident), - base64(body)); - else if (head == "privkey") - cons.consume_private_key(rsa_keypair_id(ident), - base64< arc4 >(body)); - else - throw oops("matched impossible key data packet with head '" + head + "'"); + L(F("read pubkey data packet")); + require(regex_match(args, regex(key))); + require(regex_match(body, regex(base))); + string contents(trim_ws(body)); + cons.consume_public_key(rsa_keypair_id(args), + base64(contents)); } + else if (type == "keypair") + { + L(F("read keypair data packet")); + require(regex_match(args, regex(key))); + match_results matches; + require(regex_match(body, matches, regex(base + "#" + base))); + string pub_dat(trim_ws(string(matches[1].first, matches[1].second))); + string priv_dat(trim_ws(string(matches[2].first, matches[2].second))); + cons.consume_key_pair(rsa_keypair_id(args), keypair(pub_dat, priv_dat)); + } else - return true; + { + W(F("unknown packet type: '%s'") % type); + return true; + } ++count; return true; } }; static size_t -extract_packets(string const & s, packet_consumer & cons) -{ - string const ident("([[:xdigit:]]{40})"); - string const sp("[[:space:]]+"); - string const bra("\\["); - string const ket("\\]"); - string const certhead("(rcert)"); - string const datahead("([mfr]data)"); - string const deltahead("([mf]r?delta)"); - string const keyhead("(pubkey|privkey)"); - string const key("(address@hidden)"); - string const certname("([-a-zA-Z0-9]+)"); - string const base64("([a-zA-Z0-9+/=[:space:]]+)"); - string const end("\\[end\\]"); - string const data = bra + datahead + sp + ident + ket + base64 + end; - string const delta = bra + deltahead + sp + ident + sp + ident + ket + base64 + end; - string const cert = bra - + certhead + sp + ident + sp + certname + sp + key + sp + base64 - + ket - + base64 + end; - string const keydata = bra + keyhead + sp + key + ket + base64 + end; - string const biggie = (data + "|" + delta + "|" + cert + "|" + keydata); - regex expr(biggie); +extract_packets(string const & s, packet_consumer & cons, bool last) +{ + std::string r(s); + { + // since we don't have privkey packets anymore, translate a + // pubkey packet immediately followed by a matching privkey + // packet into a keypair packet (which is what privkey packets + // have been replaced by) + string const key("(address@hidden)"); + string const base64("([a-zA-Z0-9+/=[:space:]]+)"); + string const pubkey("\\[pubkey[[:space:]]+"+ key + "\\]" + base64 + + "\\[end\\]"); + string const privkey("\\[privkey \\1\\]" + base64 + "\\[end\\]"); + string const pubkey_privkey = pubkey + "[[:space:]]*" + privkey; + string const keypair_fmt("[keypair $1]$2#$3[end]"); + r = regex_replace(s, regex(pubkey_privkey), keypair_fmt); + bool pub = regex_match(s, regex(pubkey + ".*")); + bool two = regex_match(s, regex(".+\\[end\\].+\\[end\\]")); + if (!last && pub && !two) + return 0; + } + + string const head("\\[([a-z]+)[[:space:]]+([^\\[\\]]+)\\]"); + string const body("([^\\[\\]]+)"); + string const tail("\\[end\\]"); + string const whole = head + body + tail; + regex expr(whole); size_t count = 0; - regex_grep(feed_packet_consumer(count, cons), s, expr, match_default); + regex_grep(feed_packet_consumer(count, cons), r, expr, match_default); return count; } @@ -1453,13 +1474,16 @@ { endpos += end.size(); string tmp = accum.substr(0, endpos); - count += extract_packets(tmp, cons); - if (endpos < accum.size() - 1) - accum = accum.substr(endpos+1); - else - accum.clear(); + size_t num = extract_packets(tmp, cons, false); + count += num; + if (num) + if (endpos < accum.size() - 1) + accum = accum.substr(endpos+1); + else + accum.clear(); } } + count += extract_packets(accum, cons, true); return count; } @@ -1538,7 +1562,7 @@ encode_base64(arc4 (rsa_priv_key("this is not a real rsa key either!")), pik); - pw.consume_private_key(rsa_keypair_id("address@hidden"), pik); + pw.consume_key_pair(rsa_keypair_id("address@hidden"), puk, pik); } ======================================================================== --- packet.hh ec7178c2332473305c0aa7d00c727e338fc7810d +++ packet.hh aff613af14460ebbedebdbc0e95c3dfc136f5c02 @@ -40,16 +40,16 @@ boost::function1 on_revision_written; boost::function1 on_cert_written; boost::function1 on_pubkey_written; - boost::function1 on_privkey_written; + boost::function1 on_keypair_written; public: - + virtual void set_on_revision_written(boost::function1 const & x); virtual void set_on_cert_written(boost::function1 const & x); virtual void set_on_pubkey_written(boost::function1 const & x); - virtual void set_on_privkey_written(boost::function1 + virtual void set_on_keypair_written(boost::function1 const & x); virtual ~packet_consumer() {} @@ -80,8 +80,8 @@ virtual void consume_public_key(rsa_keypair_id const & ident, base64< rsa_pub_key > const & k) = 0; - virtual void consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k) = 0; + virtual void consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp) = 0; }; // this writer writes packets into a stream @@ -115,8 +115,8 @@ virtual void consume_public_key(rsa_keypair_id const & ident, base64< rsa_pub_key > const & k); - virtual void consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k); + virtual void consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp); }; // this writer injects packets it receives to the database. @@ -155,8 +155,8 @@ virtual void consume_public_key(rsa_keypair_id const & ident, base64< rsa_pub_key > const & k); - virtual void consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k); + virtual void consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp); }; // this writer is just like packet_db_writer, except that none of your calls @@ -178,7 +178,7 @@ const & x); virtual void set_on_pubkey_written(boost::function1 const & x); - virtual void set_on_privkey_written(boost::function1 + virtual void set_on_keypair_written(boost::function1 const & x); virtual void consume_file_data(file_id const & ident, file_data const & dat); @@ -204,8 +204,8 @@ virtual void consume_public_key(rsa_keypair_id const & ident, base64< rsa_pub_key > const & k); - virtual void consume_private_key(rsa_keypair_id const & ident, - base64< arc4 > const & k); + virtual void consume_key_pair(rsa_keypair_id const & ident, + keypair const & kp); virtual void open_valve(); }; ======================================================================== --- tests/t_cross.at 3c97886280aa50bb3dcef375373f9207175cc927 +++ tests/t_cross.at d4a0e27dcd1f8ad7859485a1785ed3e3ff7c0272 @@ -70,8 +70,8 @@ end ]) -m4_define([BOB], [monotone --rcfile=test_hooks.lua --rcfile=bob.lua --nostd --norc --db=test.db --key=bob]) -m4_define([ALICE], [monotone --rcfile=test_hooks.lua --rcfile=alice.lua --nostd --norc --db=test.db --key=alice]) +m4_define([BOB], [monotone --rcfile=test_hooks.lua --rcfile=bob.lua --nostd --norc --db=test.db --key=bob --keydir=keys]) +m4_define([ALICE], [monotone --rcfile=test_hooks.lua --rcfile=alice.lua --nostd --norc --db=test.db --key=alice --keydir=keys]) AT_CHECK((echo "bob"; echo "bob") | BOB genkey bob, [], [ignore], [ignore]) AT_CHECK((echo "alice"; echo "alice") | ALICE genkey alice, [], [ignore], [ignore]) ======================================================================== --- tests/t_lua_privkey.at e85cec14dbef01453f2307c787d8144502bfba77 +++ tests/t_lua_privkey.at 4ab69dc88079acf06ee05abf7368abcab84019a9 @@ -1,10 +1,10 @@ AT_SETUP([reading private keys from lua hooks]) MONOTONE_SETUP AT_CHECK(MONOTONE privkey address@hidden > privkey, [], [ignore], [ignore]) address@hidden -m4_define([RCMONOTONE], [monotone --rcfile=test_key_hooks.lua --nostd --norc --db=$_ROOT_DIR/test.db address@hidden) +m4_define([RCMONOTONE], [monotone --rcfile=test_key_hooks.lua --nostd --norc --db=$_ROOT_DIR/test.db address@hidden --keydir=$_ROOT_DIR/keys]) # check it works originally @@ -35,6 +35,7 @@ # check that it fails if the keys differ AT_CHECK(RCMONOTONE db execute "delete from private_keys", [], [ignore], [ignore]) AT_CHECK(RCMONOTONE db execute "delete from public_keys", [], [ignore], [ignore]) +AT_CHECK(rm -r keys/) AT_CHECK((echo $KEY; echo $KEY) | RCMONOTONE genkey $KEY, [], [ignore], [ignore]) AT_CHECK(RCMONOTONE comment $SOMEREV aintworking, [1], [ignore], [ignore]) ======================================================================== --- tests/t_netsync_absorbs.at 192d6c27f26a2553f8a0044c95832ae611861c49 +++ tests/t_netsync_absorbs.at 754c3f6173c7f3a22380904f8dbc20c4174447c6 @@ -13,7 +13,10 @@ UtfQv7Fh2d/euba9cbtNPaNjWu7RsnVOo4iEQhb3czeObhE0n+hJ3/dGj9d1IClbCwIBEQ== @<:@end@:>@ ]) -AT_DATA(foo_private, [@<:@privkey address@hidden@:>@ +AT_DATA(foo_private, [@<:@keypair address@hidden@:>@ +MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDXdNcDvz8ARAxEw7ESs2y60RgT9HdpoPHA +AXvhjWtojgoW8t7RLp+62RUm+c9H2tWFAVMBRtLzd9kFl58f+muDAiQVEJo4rgPy2jjrHO6Z +UtfQv7Fh2d/euba9cbtNPaNjWu7RsnVOo4iEQhb3czeObhE0n+hJ3/dGj9d1IClbCwIBEQ==# gWGUHfbvsqKRQZ7inK1NQUO1cAuNcKENJNvoxhBtQKv6iIZp23ZBSrpBGuKoSnQSPXresXLD tYc4Ztecez0hBTjGsTmUm1EQLGf2dTJ4dG2nBJGbI2R6QvX9/KJbF1dIc9DCoJGB3XTpVg4R kMc9IFKM5tzwpNYRx+vpjf5GTamIyj1O5qmQmg7VFWx8uC3hzgW/FnaP9Qh2lnjiaEVDNzjA @@ -29,7 +32,6 @@ @<:@end@:>@ ]) -AT_CHECK(MONOTONE read var_key; + +struct keypair +{ + base64 pub; + base64< arc4 > priv; + keypair() + {} + keypair(base64 const & a, + base64< arc4 > const & b) + : pub(a), priv(b) + {} +}; + // fs::path is our "generic" safe path type, pointing potentially anywhere // in the filesystem. if you want to *define* or work with any of these you // need to include boost/filesystem/path.hpp.