# # # patch "README.encapsulation" # from [ae11f2bb346c489e458fcdd39b5f7fabba05dcde] # to [7775f0fa4adab68eaf623276d3c883126503d401] # # patch "automate.cc" # from [06a214e81fbc38094531d00c8a27b2af832d855a] # to [7a0b4b83bd698f3c37cc19747cd0516006339f08] # # patch "cert.cc" # from [b0908ee81de56cff78f0854d43c65637a8b74b7e] # to [7b5c4be73e3946f7ae25bf707a30c2c7cf039af7] # # patch "cert.hh" # from [2700aa9422c6d9783cb252df855a39cb39bba03a] # to [71a5ac357a0a437c5556a69297c0116ef41917a8] # # patch "cmd_db.cc" # from [1c6087ad01b4fab7333b671f080c830a82e8f9ce] # to [6e0eed17a107e27235d68204bc4dde35b3f78c5e] # # patch "cmd_key_cert.cc" # from [35113296a43d1362967b899619a2d7f95f67dc98] # to [eba5cb5fad09136c4a23e56a823f3134de5dea4c] # # patch "cmd_merging.cc" # from [e3b9043678878bbb036f24d4f8fab59a877ed4ae] # to [933ab606f898c8f056b3fa86815de0dc1977fb8c] # # patch "cmd_netsync.cc" # from [5d55946eef1344e13d0c3c6827a07f865d5c762c] # to [27562e25ad1c82527a6e54392f555e0cb6763a2d] # # patch "cmd_othervcs.cc" # from [61aa5daaa67e128b1e3cdf094bb2553a588e77b5] # to [26ce46520e69789d28d93a8c733f26ace5e59592] # # patch "cmd_ws_commit.cc" # from [580f51c980235a9d6d05ad46399cb69fcf452d05] # to [a849392a7e25e92eacfd1785c8576d5252fd344b] # # patch "database.cc" # from [fc8b895df262f7c072e4da2020e9e79c8dfffe17] # to [a43b661056bacfe298863382b8dc145285aea772] # # patch "database.hh" # from [2d6bd5939c406972bfc6744ae6ca073ab415bed4] # to [e0a41892c27ca9c23da4ddb55462e102d314e2fd] # # patch "database_check.cc" # from [4a77f59692f47b501a73ede9369b8a5923c161d7] # to [927ecbc5cb205246a6cd959005031b122928d20c] # # patch "key_store.hh" # from [43bb15d9678fd04e7ecea6357019bf3c57322345] # to [78b1f281a7dcd30fbd3404b2b954e0470a362c85] # # patch "keys.cc" # from [bd54a676a32aba4cc8777d28564e96df33774398] # to [e7e47cab5f6efeae027fa0eefdddd64438c9a074] # # patch "keys.hh" # from [aa40aff6d0dd20cacd009c0fa02d2e326d3bebf6] # to [2247bc16bb59873ced93ccc907e10d4615af6861] # # patch "netsync.cc" # from [2840a4879df54163b34f37248c0d01d28c24ed74] # to [4d388b36d00bcfa1aba5ffcb1f767f6737488673] # # patch "options_list.hh" # from [1f1eb3ce7a6a72870d0a42ef7f79b58eef621127] # to [5ee94e644545cd8550638c47113655d8022d78c8] # # patch "project.cc" # from [1f6c91873685bf50ce73f6236aad710a8fb6951c] # to [6262745ddc861ff5aa139ec8c4ef25b300db84a0] # # patch "project.hh" # from [995a7865a30fbda9d237b87d221b0a9f503399f5] # to [4fce04f721322c557064635b86cfaf13fd54fc00] # # patch "rcs_import.cc" # from [b292beb9bd9d2b19ec34efb7ee4b19486cd2b92e] # to [352e42220856fef882d4125ca9df95c1896ceffb] # # patch "rcs_import.hh" # from [b864e2f752da32d66ef1f2b083b63fddb8d6bb84] # to [975c217ebf5a3b8b73ebc64290f12b8050ce05f3] # # patch "revision.cc" # from [08ced70b3682935ee831353d3953d7a9d264eec7] # to [725c74dec3553f8e4a5f5d341b7101518fb83aba] # # patch "revision.hh" # from [567afc600236952217f44e1af404e718c6d688ee] # to [8a83775b59c469630040469f6a8ad0c5711f28f9] # ============================================================ --- README.encapsulation ae11f2bb346c489e458fcdd39b5f7fabba05dcde +++ README.encapsulation 7775f0fa4adab68eaf623276d3c883126503d401 @@ -1,12 +1,9 @@ database.cc: database.cc: - __app is a member variable of class database, used by: + __app is a member variable of class database, used by accessor + hacks: - accessor hacks: - __app->keys - get_key_store() - - __app->lua ... + __app->lua hook_get_manifest_cert_trust() hook_get_revision_cert_trust() hook_get_author() ============================================================ --- automate.cc 06a214e81fbc38094531d00c8a27b2af832d855a +++ automate.cc 7a0b4b83bd698f3c37cc19747cd0516006339f08 @@ -2089,7 +2089,7 @@ CMD_AUTOMATE(cert, N_("REVISION-ID NAME N(db.revision_exists(rid), F("no such revision '%s'") % rid); make_simple_cert(rid.inner(), cert_name(idx(args, 1)()), - cert_value(idx(args, 2)()), db, c); + cert_value(idx(args, 2)()), db, app.keys, c); revision rc(c); db.put_revision_cert(rc); guard.commit(); ============================================================ --- cert.cc b0908ee81de56cff78f0854d43c65637a8b74b7e +++ cert.cc 7b5c4be73e3946f7ae25bf707a30c2c7cf039af7 @@ -361,91 +361,46 @@ cert_hash_code(cert const & t, hexenc temp_keys; - bool persist_ok = (!temp_keys.empty()) || keys.hook_persist_phrase_ok(); - - if (persist_ok && temp_keys.find(id) != temp_keys.end()) - { - kp = temp_keys[id]; - } - else - { - N(keys.key_pair_exists(id), - F("no key pair '%s' found in key store '%s'") - % id % keys.get_key_dir()); - keys.get_key_pair(id, kp); - if (persist_ok) - temp_keys.insert(make_pair(id, kp)); - } + N(keys.key_pair_exists(id), + F("no key pair '%s' found in key store '%s'") + % id % keys.get_key_dir()); + keys.get_key_pair(id, kp); } -void -calculate_cert(database & db, cert & t) +static void +calculate_cert(key_store & keys, database & db, cert & t) { string signed_text; keypair kp; cert_signable_text(t, signed_text); - load_key_pair(db.get_key_store(), t.key, kp); + load_key_pair(keys, t.key, kp); db.put_key(t.key, kp.pub); - make_signature(db.get_key_store(), t.key, kp.priv, signed_text, t.sig); + make_signature(keys, db, t.key, kp.priv, signed_text, t.sig); } cert_status check_cert(database & db, cert const & t) { - base64< rsa_pub_key > pub; - - static map > pubkeys; - bool persist_ok = (!pubkeys.empty()) || db.get_key_store().hook_persist_phrase_ok(); - - if (persist_ok - && pubkeys.find(t.key) != pubkeys.end()) - { - pub = pubkeys[t.key]; - } - else - { - if (!db.public_key_exists(t.key)) - return cert_unknown; - db.get_key(t.key, pub); - if (persist_ok) - pubkeys.insert(make_pair(t.key, pub)); - } - string signed_text; cert_signable_text(t, signed_text); - if (check_signature(db.get_key_store(), t.key, pub, signed_text, t.sig)) - return cert_ok; - else - return cert_bad; + return db.check_signature(t.key, signed_text, t.sig); } - // "special certs" void -get_user_key(rsa_keypair_id & key, database & db) +get_user_key(rsa_keypair_id & key, key_store & keys, database & db) { - key_store & keys = db.get_key_store(); - if (keys.has_opt_signing_key()) key = keys.get_opt_signing_key(); else if (keys.hook_get_current_branch_key(key)) @@ -522,14 +477,15 @@ make_simple_cert(hexenc const & id, cert_name const & nm, cert_value const & cv, database & db, + key_store & keys, cert & c) { rsa_keypair_id key; - get_user_key(key, db); + get_user_key(key, keys, db); base64 encoded_val; encode_base64(cv, encoded_val); cert t(id, nm, encoded_val, key); - calculate_cert(db, t); + calculate_cert(keys, db, t); c = t; } @@ -537,10 +493,11 @@ put_simple_revision_cert(revision_id con put_simple_revision_cert(revision_id const & id, cert_name const & nm, cert_value const & val, - database & db) + database & db, + key_store & keys) { cert t; - make_simple_cert(id.inner(), nm, val, db, t); + make_simple_cert(id.inner(), nm, val, db, keys, t); revision cc(t); db.put_revision_cert(cc); } @@ -548,18 +505,21 @@ cert_revision_in_branch(revision_id cons void cert_revision_in_branch(revision_id const & rev, branch_name const & branch, - database & db) + database & db, + key_store & keys) { - put_simple_revision_cert(rev, branch_cert_name, cert_value(branch()), db); + put_simple_revision_cert(rev, branch_cert_name, cert_value(branch()), + db, keys); } void cert_revision_suspended_in_branch(revision_id const & rev, - branch_name const & branch, - database & db) + branch_name const & branch, + database & db, + key_store & keys) { put_simple_revision_cert (rev, suspend_cert_name, cert_value(branch()), - db); + db, keys); } @@ -568,64 +528,68 @@ cert_revision_date_time(revision_id cons void cert_revision_date_time(revision_id const & m, date_t const & t, - database & db) + database & db, + key_store & keys) { cert_value val = cert_value(t.as_iso_8601_extended()); - put_simple_revision_cert(m, date_cert_name, val, db); + put_simple_revision_cert(m, date_cert_name, val, db, keys); } void cert_revision_author(revision_id const & m, string const & author, - database & db) + database & db, + key_store & keys) { - put_simple_revision_cert(m, author_cert_name, cert_value(author), db); + put_simple_revision_cert(m, author_cert_name, cert_value(author), db, keys); } void cert_revision_author_default(revision_id const & m, - database & db) + database & db, key_store & keys) { string author; rsa_keypair_id key; - get_user_key(key, db); + get_user_key(key, keys, db); if (!db.hook_get_author(key, author)) { author = key(); } - cert_revision_author(m, author, db); + cert_revision_author(m, author, db, keys); } void cert_revision_tag(revision_id const & m, string const & tagname, - database & db) + database & db, key_store & keys) { - put_simple_revision_cert(m, tag_cert_name, cert_value(tagname), db); + put_simple_revision_cert(m, tag_cert_name, cert_value(tagname), db, keys); } void cert_revision_changelog(revision_id const & m, utf8 const & log, - database & db) + database & db, key_store & keys) { - put_simple_revision_cert(m, changelog_cert_name, cert_value(log()), db); + put_simple_revision_cert(m, changelog_cert_name, cert_value(log()), + db, keys); } void cert_revision_comment(revision_id const & m, utf8 const & comment, - database & db) + database & db, key_store & keys) { - put_simple_revision_cert(m, comment_cert_name, cert_value(comment()), db); + put_simple_revision_cert(m, comment_cert_name, cert_value(comment()), + db, keys); } void cert_revision_testresult(revision_id const & r, string const & results, - database & db) + database & db, key_store & keys) { bool passed = false; if (lowercase(results) == "true" || @@ -644,7 +608,7 @@ cert_revision_testresult(revision_id con "'pass/fail'"); put_simple_revision_cert(r, testresult_cert_name, - cert_value(lexical_cast(passed)), db); + cert_value(lexical_cast(passed)), db, keys); } // Local Variables: ============================================================ --- cert.hh 2700aa9422c6d9783cb252df855a39cb39bba03a +++ cert.hh 71a5ac357a0a437c5556a69297c0116ef41917a8 @@ -65,7 +65,6 @@ cert_status check_cert(database & db, ce void cert_signable_text(cert const & t,std::string & out); cert_status check_cert(database & db, cert const & t); -bool priv_key_exists(key_store & keys, rsa_keypair_id const & id); void load_key_pair(key_store & keys, rsa_keypair_id const & id, keypair & kp); @@ -76,12 +75,14 @@ void make_simple_cert(hexenc const & cert_name const & nm, cert_value const & cv, database & db, + key_store & keys, cert & c); void put_simple_revision_cert(revision_id const & id, cert_name const & nm, cert_value const & val, - database & db); + database & db, + key_store & keys); void erase_bogus_certs(std::vector< revision > & certs, database & db); @@ -96,7 +97,7 @@ cert_revision_in_branch(revision_id cons void cert_revision_in_branch(revision_id const & ctx, branch_name const & branchname, - database & db); + database & db, key_store & keys); // We also define some common cert types, to help establish useful @@ -105,7 +106,7 @@ void // N()'s out if there is no unique key for us to use void -get_user_key(rsa_keypair_id & key, database & db); +get_user_key(rsa_keypair_id & key, key_store & keys, database & db); void guess_branch(revision_id const & id, database & db, project_t & project, @@ -124,41 +125,41 @@ cert_revision_suspended_in_branch(revisi void cert_revision_suspended_in_branch(revision_id const & ctx, branch_name const & branchname, - database & db); + database & db, key_store & keys); void cert_revision_date_time(revision_id const & m, date_t const & t, - database & db); + database & db, key_store & keys); void cert_revision_author(revision_id const & m, std::string const & author, - database & db); + database & db, key_store & keys); void cert_revision_author_default(revision_id const & m, - database & db); + database & db, key_store & keys); void cert_revision_tag(revision_id const & m, std::string const & tagname, - database & db); + database & db, key_store & keys); void cert_revision_changelog(revision_id const & m, utf8 const & changelog, - database & db); + database & db, key_store & keys); void cert_revision_comment(revision_id const & m, utf8 const & comment, - database & db); + database & db, key_store & keys); void cert_revision_testresult(revision_id const & m, std::string const & results, - database & db); + database & db, key_store & keys); // Local Variables: ============================================================ --- cmd_db.cc 1c6087ad01b4fab7333b671f080c830a82e8f9ce +++ cmd_db.cc 6e0eed17a107e27235d68204bc4dde35b3f78c5e @@ -227,10 +227,10 @@ CMD(db_changesetify, "changesetify", "", // early short-circuit to avoid failure after lots of work rsa_keypair_id key; - get_user_key(key, app.db); - require_password(key, app.keys); + get_user_key(key, app.keys, app.db); + require_password(key, app.keys, app.db); - build_changesets_from_manifest_ancestry(app.db, set()); + build_changesets_from_manifest_ancestry(app.db, app.keys, set()); } CMD(db_rosterify, "rosterify", "", CMD_REF(db), "", @@ -246,10 +246,10 @@ CMD(db_rosterify, "rosterify", "", CMD_R // early short-circuit to avoid failure after lots of work rsa_keypair_id key; - get_user_key(key, app.db); - require_password(key, app.keys); + get_user_key(key, app.keys, app.db); + require_password(key, app.keys, app.db); - build_roster_style_revs_from_manifest_style_revs(app.db, + build_roster_style_revs_from_manifest_style_revs(app.db, app.keys, app.opts.attrs_to_drop); } ============================================================ --- cmd_key_cert.cc 35113296a43d1362967b899619a2d7f95f67dc98 +++ cmd_key_cert.cc eba5cb5fad09136c4a23e56a823f3134de5dea4c @@ -28,6 +28,7 @@ using std::ofstream; using std::set; using std::string; using std::ofstream; +using boost::shared_ptr; using Botan::Pipe; using Botan::RSA_PrivateKey; @@ -134,8 +135,8 @@ CMD(ssh_agent_export, "ssh_agent_export" rsa_keypair_id id; keypair key; - get_user_key(id, app.db); - N(priv_key_exists(app.keys, id), F("the key you specified cannot be found")); + get_user_key(id, app.keys, app.db); + N(app.keys.key_pair_exists(id), F("the key you specified cannot be found")); app.keys.get_key_pair(id, key); shared_ptr priv = get_private_key(app.keys, id, key.priv); utf8 new_phrase; @@ -174,8 +175,8 @@ CMD(ssh_agent_add, "ssh_agent_add", "", rsa_keypair_id id; keypair key; - get_user_key(id, app.db); - N(priv_key_exists(app.keys, id), F("the key you specified cannot be found")); + get_user_key(id, app.keys, app.db); + N(app.keys.key_pair_exists(id), F("the key you specified cannot be found")); app.keys.get_key_pair(id, key); shared_ptr priv = get_private_key(app.keys, id, key.priv); app.agent.add_identity(*priv, id()); @@ -199,7 +200,7 @@ CMD(cert, "cert", "", CMD_REF(key_and_ce internalize_cert_name(idx(args, 1), cname); rsa_keypair_id key; - get_user_key(key, app.db); + get_user_key(key, app.keys, app.db); cert_value val; if (args.size() == 3) @@ -211,7 +212,7 @@ CMD(cert, "cert", "", CMD_REF(key_and_ce val = cert_value(dat()); } - app.get_project().put_cert(rid, cname, val); + app.get_project().put_cert(app.keys, rid, cname, val); guard.commit(); } @@ -277,7 +278,7 @@ CMD(tag, "tag", "", CMD_REF(review), N_( revision_id r; complete(app, idx(args, 0)(), r); - cert_revision_tag(r, idx(args, 1)(), app.db); + cert_revision_tag(r, idx(args, 1)(), app.db, app.keys); } @@ -292,7 +293,7 @@ CMD(testresult, "testresult", "", CMD_RE revision_id r; complete(app, idx(args, 0)(), r); - cert_revision_testresult(r, idx(args, 1)(), app.db); + cert_revision_testresult(r, idx(args, 1)(), app.db, app.keys); } @@ -308,7 +309,7 @@ CMD(approve, "approve", "", CMD_REF(revi complete(app, idx(args, 0)(), r); guess_branch(r, app.db, app.get_project()); N(app.opts.branchname() != "", F("need --branch argument for approval")); - app.get_project().put_revision_in_branch(r, app.opts.branchname); + app.get_project().put_revision_in_branch(app.keys, r, app.opts.branchname); } CMD(suspend, "suspend", "", CMD_REF(review), N_("REVISION"), @@ -323,7 +324,7 @@ CMD(suspend, "suspend", "", CMD_REF(revi complete(app, idx(args, 0)(), r); guess_branch(r, app.db, app.get_project()); N(app.opts.branchname() != "", F("need --branch argument to suspend")); - app.get_project().suspend_revision_in_branch(r, app.opts.branchname); + app.get_project().suspend_revision_in_branch(app.keys, r, app.opts.branchname); } CMD(comment, "comment", "", CMD_REF(review), N_("REVISION [COMMENT]"), @@ -350,7 +351,7 @@ CMD(comment, "comment", "", CMD_REF(revi revision_id r; complete(app, idx(args, 0)(), r); - cert_revision_comment(r, comment, app.db); + cert_revision_comment(r, comment, app.db, app.keys); } // Local Variables: ============================================================ --- cmd_merging.cc e3b9043678878bbb036f24d4f8fab59a877ed4ae +++ cmd_merging.cc 933ab606f898c8f056b3fa86815de0dc1977fb8c @@ -348,14 +348,14 @@ merge_two(revision_id const & left, revi { // early short-circuit to avoid failure after lots of work rsa_keypair_id key; - get_user_key(key, app.db); + get_user_key(key, app.keys, app.db); } revision_id merged; transaction_guard guard(app.db); interactive_merge_and_store(left, right, merged, app.db, app.lua); - app.get_project().put_standard_certs_from_options(merged, + app.get_project().put_standard_certs_from_options(app.keys, merged, branch, utf8(log.str())); @@ -550,7 +550,7 @@ CMD(merge_into_dir, "merge_into_dir", "" P(F("no merge necessary; putting %s in branch '%s'") % (*src_i) % idx(args, 1)()); transaction_guard guard(app.db); - app.get_project().put_revision_in_branch(*src_i, + app.get_project().put_revision_in_branch(app.keys, *src_i, branch_name(idx(args, 1)())); guard.commit(); } @@ -610,7 +610,7 @@ CMD(merge_into_dir, "merge_into_dir", "" { rsa_keypair_id key; - get_user_key(key, app.db); + get_user_key(key, app.keys, app.db); } resolve_merge_conflicts(left_roster, right_roster, result, dba, app.lua); @@ -636,7 +636,7 @@ CMD(merge_into_dir, "merge_into_dir", "" % idx(args, 0) % (*src_i) % idx(args, 1) % (*dst_i)).str()); - app.get_project().put_standard_certs_from_options(merged, + app.get_project().put_standard_certs_from_options(app.keys, merged, branch_name(idx(args, 1)()), log_message); ============================================================ --- cmd_netsync.cc 5d55946eef1344e13d0c3c6827a07f865d5c762c +++ cmd_netsync.cc 27562e25ad1c82527a6e54392f555e0cb6763a2d @@ -22,6 +22,8 @@ using std::vector; using std::string; using std::vector; +using boost::shared_ptr; + static const var_key default_server_key(var_domain("database"), var_name("default-server")); static const var_key default_include_pattern_key(var_domain("database"), @@ -78,7 +80,7 @@ find_key(utf8 const & addr, { if (needed) { - get_user_key(key, app.db); + get_user_key(key, app.keys, app.db); } } app.opts.signing_key = key; @@ -443,7 +445,7 @@ CMD_NO_WORKSPACE(serve, "serve", "", CMD N(app.lua.hook_persist_phrase_ok(), F("need permission to store persistent passphrase (see hook persist_phrase_ok())")); - require_password(app.opts.signing_key, app.keys); + require_password(app.opts.signing_key, app.keys, app.db); } else if (!app.opts.bind_stdio) W(F("The --no-transport-auth option is usually only used in combination with --stdio")); ============================================================ --- cmd_othervcs.cc 61aa5daaa67e128b1e3cdf094bb2553a588e77b5 +++ cmd_othervcs.cc 26ce46520e69789d28d93a8c733f26ace5e59592 @@ -52,10 +52,10 @@ CMD(cvs_import, "cvs_import", "", CMD_RE // the password (if necessary) up front rather than after some arbitrary // amount of work rsa_keypair_id key; - get_user_key(key, app.db); - require_password(key, app.keys); + get_user_key(key, app.keys, app.db); + require_password(key, app.keys, app.db); - import_cvs_repo(cvsroot, app.db, app.get_project(), app.opts.branchname); + import_cvs_repo(cvsroot, app.db, app.keys, app.get_project(), app.opts.branchname); } ============================================================ --- cmd_ws_commit.cc 580f51c980235a9d6d05ad46399cb69fcf452d05 +++ cmd_ws_commit.cc a849392a7e25e92eacfd1785c8576d5252fd344b @@ -374,7 +374,7 @@ CMD(disapprove, "disapprove", "", CMD_RE calculate_ident(rdat, inv_id); app.db.put_revision(inv_id, rdat); - app.get_project().put_standard_certs_from_options(inv_id, + app.get_project().put_standard_certs_from_options(app.keys, inv_id, app.opts.branchname, log_message); guard.commit(); @@ -1055,7 +1055,7 @@ CMD(commit, "commit", "ci", CMD_REF(work { // fail early if there isn't a key rsa_keypair_id key; - get_user_key(key, app.db); + get_user_key(key, app.keys, app.db); } app.make_branch_sticky(); @@ -1235,7 +1235,8 @@ CMD(commit, "commit", "ci", CMD_REF(work app.db.put_revision(restricted_rev_id, rdat); } - app.get_project().put_standard_certs_from_options(restricted_rev_id, + app.get_project().put_standard_certs_from_options(app.keys, + restricted_rev_id, app.opts.branchname, log_message); guard.commit(); ============================================================ --- database.cc fc8b895df262f7c072e4da2020e9e79c8dfffe17 +++ database.cc a43b661056bacfe298863382b8dc145285aea772 @@ -51,6 +51,11 @@ #include "outdated_indicator.hh" #include "lru_writeback_cache.hh" +#include "botan/botan.h" +#include "botan/rsa.h" +#include "botan/keypair.h" +#include "botan/pem.h" + // defined in schema.c, generated from schema.sql: extern char const schema_constant[]; @@ -75,6 +80,11 @@ using boost::lexical_cast; using boost::shared_ptr; using boost::lexical_cast; +using Botan::PK_Verifier; +using Botan::SecureVector; +using Botan::X509_PublicKey; +using Botan::RSA_PublicKey; + int const one_row = 1; int const one_col = 1; int const any_rows = -1; @@ -161,6 +171,11 @@ namespace typedef hashmap::hash_map > parent_id_map; typedef hashmap::hash_map height_map; + typedef hashmap::hash_map, + shared_ptr > + > verifier_cache; + } // anonymous namespace class database_impl @@ -322,6 +337,9 @@ class database_impl // void get_keys(string const & table, vector & keys); + // cache of verifiers for public keys + verifier_cache verifiers; + // // --== Certs ==-- // @@ -2614,15 +2632,24 @@ database::get_key(rsa_keypair_id const & void database::get_key(rsa_keypair_id const & pub_id, - base64 & pub_encoded) + rsa_pub_key & pub) { results res; imp->fetch(res, one_col, one_row, query("SELECT keydata FROM public_keys WHERE id = ?") % text(pub_id())); - encode_base64(rsa_pub_key(res[0][0]), pub_encoded); + pub = rsa_pub_key(res[0][0]); } +void +database::get_key(rsa_keypair_id const & pub_id, + base64 & pub_encoded) +{ + rsa_pub_key pub; + get_key(pub_id, pub); + encode_base64(pub, pub_encoded); +} + bool database::put_key(rsa_keypair_id const & pub_id, base64 const & pub_encoded) @@ -2660,6 +2687,64 @@ database::delete_public_key(rsa_keypair_ % text(pub_id())); } +cert_status +database::check_signature(rsa_keypair_id const & id, + string const & alleged_text, + base64 const & signature) +{ + shared_ptr verifier; + + verifier_cache::const_iterator i = imp->verifiers.find(id); + if (i != imp->verifiers.end()) + verifier = i->second.first; + + else + { + rsa_pub_key pub; + SecureVector pub_block; + + if (!public_key_exists(id)) + return cert_unknown; + + get_key(id, pub); + pub_block.set(reinterpret_cast(pub().data()), + pub().size()); + + L(FL("building verifier for %d-byte pub key") % pub_block.size()); + shared_ptr x509_key(Botan::X509::load_key(pub_block)); + shared_ptr pub_key + = boost::shared_dynamic_cast(x509_key); + + E(pub_key, + F("Failed to get RSA verifying key for %s") % id); + + verifier.reset(get_pk_verifier(*pub_key, "EMSA3(SHA-1)")); + + /* XXX This is ugly. We need to keep the key around + * as long as the verifier is around, but the shared_ptr will go + * away after we leave this scope. Hence we store a pair of + * so they both exist. */ + imp->verifiers.insert(make_pair(id, make_pair(verifier, pub_key))); + } + + // examine signature + rsa_sha1_signature sig_decoded; + decode_base64(signature, sig_decoded); + + // check the text+sig against the key + L(FL("checking %d-byte (%d decoded) signature") % + signature().size() % sig_decoded().size()); + + if (verifier->verify_message( + reinterpret_cast(alleged_text.data()), + alleged_text.size(), + reinterpret_cast(sig_decoded().data()), + sig_decoded().size())) + return cert_ok; + else + return cert_bad; +} + // cert management bool @@ -3604,13 +3689,6 @@ database::hook_accept_testresult_change( return __app->lua.hook_accept_testresult_change(old_results, new_results); } - -key_store & -database::get_key_store() -{ - return __app->keys; -} - utf8 const & database::get_opt_author() { ============================================================ --- database.hh 2d6bd5939c406972bfc6744ae6ca073ab415bed4 +++ database.hh e0a41892c27ca9c23da4ddb55462e102d314e2fd @@ -17,9 +17,9 @@ #include "vocab.hh" #include "roster.hh" #include "graph.hh" +#include "cert.hh" class app_state; -struct cert; struct date_t; struct globish; class key_store; @@ -245,6 +245,7 @@ public: rsa_keypair_id & ident, base64 & pub_encoded); + void get_key(rsa_keypair_id const & ident, rsa_pub_key & pub); void get_key(rsa_keypair_id const & ident, base64 & pub_encoded); @@ -253,6 +254,10 @@ public: void delete_public_key(rsa_keypair_id const & pub_id); + cert_status check_signature(rsa_keypair_id const & id, + std::string const & alleged_text, + base64 const & signature); + // // --== Certs ==-- // @@ -442,8 +447,6 @@ public: bool const get_opt_ignore_suspend_certs(); void set_opt_branchname(branch_name const & branchname); - key_store & get_key_store(); - private: database_impl *imp; app_state * __app; ============================================================ --- database_check.cc 4a77f59692f47b501a73ede9369b8a5923c161d7 +++ database_check.cc 927ecbc5cb205246a6cd959005031b122928d20c @@ -455,8 +455,6 @@ check_certs(database & db, map & checked_keys, size_t & total_certs) { - key_store & keys = db.get_key_store(); - vector< revision > certs; db.get_revision_certs(certs); @@ -476,9 +474,9 @@ check_certs(database & db, { string signed_text; cert_signable_text(i->inner(), signed_text); - checked.good_sig = check_signature(keys, i->inner().key, - checked_keys[i->inner().key].pub_encoded, - signed_text, i->inner().sig); + checked.good_sig + = (db.check_signature(i->inner().key, + signed_text, i->inner().sig) == cert_ok); } checked_keys[i->inner().key].sigs++; ============================================================ --- key_store.hh 43bb15d9678fd04e7ecea6357019bf3c57322345 +++ key_store.hh 78b1f281a7dcd30fbd3404b2b954e0470a362c85 @@ -73,9 +73,6 @@ public: std::map, boost::shared_ptr > > signers; - std::map, - boost::shared_ptr > > verifiers; // FIXME: quick hack to make these hooks and options available via // the key_store context ============================================================ --- keys.cc bd54a676a32aba4cc8777d28564e96df33774398 +++ keys.cc e7e47cab5f6efeae027fa0eefdddd64438c9a074 @@ -35,6 +35,7 @@ #include "cert.hh" #include "charset.hh" #include "ssh_agent.hh" +#include "database.hh" using std::cout; using std::make_pair; @@ -52,7 +53,6 @@ using Botan::PK_Signer; using Botan::PK_Decryptor; using Botan::PK_Encryptor; using Botan::PK_Signer; -using Botan::PK_Verifier; using Botan::Pipe; using Botan::RSA_PrivateKey; using Botan::RSA_PublicKey; @@ -361,25 +361,23 @@ void } void -make_signature(key_store & keys, // to hook for phrase - rsa_keypair_id const & id, // to prompting user for phrase +make_signature(key_store & keys, + database & db, + rsa_keypair_id const & id, // to prompt user for phrase base64< rsa_priv_key > const & priv, string const & tosign, base64 & signature) { const string & opt_ssh_sign = keys.get_opt_ssh_sign(); - E(!opt_ssh_sign.empty(), - F("--ssh-sign requires a value ['yes', 'no', 'only', or 'check']")); - E(opt_ssh_sign == "yes" - || opt_ssh_sign == "no" - || opt_ssh_sign == "check" - || opt_ssh_sign == "only", - F("--ssh-sign must be set to 'yes', 'no', 'only', or 'check'")); - keypair key; keys.get_key_pair(id, key); + I(key.priv == priv); + // If the database doesn't have this public key, add it now. + if (!db.public_key_exists(id)) + db.put_key(id, key.pub); + string sig_string; ssh_agent & agent = keys.get_agent(); //sign with ssh-agent (if connected) @@ -496,66 +494,11 @@ make_signature(key_store & keys, L(FL("make_signature: produced %d-byte signature") % sig_string.size()); encode_base64(rsa_sha1_signature(sig_string), signature); - E(check_signature(keys, id, key.pub, tosign, signature), - F("make_signature: signature is not valid")); + cert_status s = db.check_signature(id, tosign, signature); + I(s != cert_unknown); + E(s == cert_ok, F("make_signature: signature is not valid")); } -bool -check_signature(key_store & keys, - rsa_keypair_id const & id, - base64 const & pub_encoded, - string const & alleged_text, - base64 const & signature) -{ - // examine pubkey - - bool persist_phrase = (!keys.verifiers.empty()) || keys.hook_persist_phrase_ok(); - - shared_ptr verifier; - shared_ptr pub_key; - if (persist_phrase - && keys.verifiers.find(id) != keys.verifiers.end()) - verifier = keys.verifiers[id].first; - - else - { - rsa_pub_key pub; - decode_base64(pub_encoded, pub); - SecureVector pub_block; - pub_block.set(reinterpret_cast(pub().data()), pub().size()); - - L(FL("building verifier for %d-byte pub key") % pub_block.size()); - shared_ptr x509_key = - shared_ptr(Botan::X509::load_key(pub_block)); - pub_key = shared_dynamic_cast(x509_key); - if (!pub_key) - throw informative_failure("Failed to get RSA verifying key"); - - verifier = shared_ptr(get_pk_verifier(*pub_key, "EMSA3(SHA-1)")); - - /* XXX This is ugly. We need to keep the key around - * as long as the verifier is around, but the shared_ptr will go - * away after we leave this scope. Hence we store a pair of - * so they both exist. */ - if (persist_phrase) - keys.verifiers.insert(make_pair(id, make_pair(verifier, pub_key))); - } - - // examine signature - rsa_sha1_signature sig_decoded; - decode_base64(signature, sig_decoded); - - // check the text+sig against the key - L(FL("checking %d-byte (%d decoded) signature") % - signature().size() % sig_decoded().size()); - - bool valid_sig = verifier->verify_message( - reinterpret_cast(alleged_text.data()), alleged_text.size(), - reinterpret_cast(sig_decoded().data()), sig_decoded().size()); - - return valid_sig; -} - void encrypt_rsa(key_store & keys, rsa_keypair_id const & id, base64 & pub_encoded, @@ -669,20 +612,16 @@ require_password(rsa_keypair_id const & void require_password(rsa_keypair_id const & key, - key_store & keys) + key_store & keys, + database & db) { - N(priv_key_exists(keys, key), - F("no key pair '%s' found in key store '%s'") - % key % keys.get_key_dir()); keypair kp; load_key_pair(keys, key, kp); if (keys.hook_persist_phrase_ok()) { string plaintext("hi maude"); base64 sig; - make_signature(keys, key, kp.priv, plaintext, sig); - N(check_signature(keys, key, kp.pub, plaintext, sig), - F("passphrase for '%s' is incorrect") % key); + make_signature(keys, db, key, kp.priv, plaintext, sig); } } ============================================================ --- keys.hh aa40aff6d0dd20cacd009c0fa02d2e326d3bebf6 +++ keys.hh 2247bc16bb59873ced93ccc907e10d4615af6861 @@ -11,13 +11,11 @@ // PURPOSE. #include "vocab.hh" -#include "botan/rsa.h" #include -using Botan::RSA_PrivateKey; -using boost::shared_ptr; - class key_store; +class database; +namespace Botan { class RSA_PrivateKey; } // keys.{hh,cc} does all the "delicate" crypto (meaning: that which needs // to read passphrases and manipulate raw, decrypted private keys). it @@ -40,8 +38,9 @@ void migrate_private_key(key_store & key base64< old_arc4_rsa_priv_key > const & old_priv, keypair & kp); -void make_signature(key_store & keys, // to hook for phrase - rsa_keypair_id const & id, // to prompting user for phrase +void make_signature(key_store & keys, + database & db, + rsa_keypair_id const & id, base64< rsa_priv_key > const & priv, std::string const & tosign, base64 & signature); @@ -53,7 +52,7 @@ void require_password(rsa_keypair_id con base64 const & signature); void require_password(rsa_keypair_id const & id, - key_store & keys); + key_store & keys, database & db); void encrypt_rsa(key_store & keys, rsa_keypair_id const & id, @@ -75,7 +74,7 @@ get_passphrase(key_store & keys, bool force_from_user = false, bool generating_key = false); -shared_ptr +boost::shared_ptr get_private_key(key_store & keys, rsa_keypair_id const & id, base64< rsa_priv_key > const & priv, @@ -103,12 +102,6 @@ bool keys_match(rsa_keypair_id const & i base64 const & key1, rsa_keypair_id const & id2, base64 const & key2); -/* Doesn't work -bool keys_match(rsa_keypair_id const & id1, - base64< rsa_priv_key > const & key1, - rsa_keypair_id const & id2, - base64< rsa_priv_key > const & key2); -*/ // Local Variables: // mode: C++ ============================================================ --- netsync.cc 2840a4879df54163b34f37248c0d01d28c24ed74 +++ netsync.cc 4d388b36d00bcfa1aba5ffcb1f767f6737488673 @@ -1383,7 +1383,7 @@ session::process_hello_cmd(rsa_keypair_i // make a signature base64 sig; rsa_sha1_signature sig_raw; - make_signature(keys, signing_key, our_kp.priv, nonce(), sig); + make_signature(keys, db, signing_key, our_kp.priv, nonce(), sig); decode_base64(sig, sig_raw); // make a new nonce of our own and send off the 'auth' @@ -1644,7 +1644,7 @@ session::process_auth_cmd(protocol_role // Check the signature. base64 sig; encode_base64(rsa_sha1_signature(signature), sig); - if (check_signature(keys, their_id, their_key, nonce1(), sig)) + if (db.check_signature(their_id, nonce1(), sig) == cert_ok) { // Get our private key and sign back. L(FL("client signature OK, accepting authentication")); ============================================================ --- options_list.hh 1f1eb3ce7a6a72870d0a42ef7f79b58eef621127 +++ options_list.hh 5ee94e644545cd8550638c47113655d8022d78c8 @@ -229,9 +229,23 @@ GOPT(ssh_sign, "ssh-sign", std::string, #endif GOPT(ssh_sign, "ssh-sign", std::string, "yes", - gettext_noop("sign with ssh-agent, 'yes' to sign with ssh if key found, 'no' to force monotone to sign, 'check' to sign with both and compare")) + gettext_noop("controls use of ssh-agent. valid arguments are: " + "'yes' to use ssh-agent to make signatures if possible, " + "'no' to force use of monotone's internal code, " + "'only' to force use of ssh-agent, " + "'check' to sign with both and compare")) #ifdef option_bodies { + if (arg.empty()) + throw bad_arg_internal(F("--ssh-sign requires a value " + "['yes', 'no', 'only', or 'check']").str()); + if (arg != "yes" + && arg != "no" + && arg != "check" + && arg != "only") // XXX what does "only" do? not documented + throw bad_arg_internal(F("--ssh-sign must be set to 'yes', 'no', " + "'only', or 'check'").str()); + ssh_sign = arg; } #endif ============================================================ --- project.cc 1f6c91873685bf50ce73f6236aad710a8fb6951c +++ project.cc 6262745ddc861ff5aa139ec8c4ef25b300db84a0 @@ -178,10 +178,11 @@ void } void -project_t::put_revision_in_branch(revision_id const & id, +project_t::put_revision_in_branch(key_store & keys, + revision_id const & id, branch_name const & branch) { - cert_revision_in_branch(id, branch, db); + cert_revision_in_branch(id, branch, db, keys); } bool @@ -208,10 +209,11 @@ void } void -project_t::suspend_revision_in_branch(revision_id const & id, - branch_name const & branch) +project_t::suspend_revision_in_branch(key_store & keys, + revision_id const & id, + branch_name const & branch) { - cert_revision_suspended_in_branch(id, branch, db); + cert_revision_suspended_in_branch(id, branch, db, keys); } @@ -308,46 +310,50 @@ void } void -project_t::put_tag(revision_id const & id, +project_t::put_tag(key_store & keys, + revision_id const & id, string const & name) { - cert_revision_tag(id, name, db); + cert_revision_tag(id, name, db, keys); } void -project_t::put_standard_certs(revision_id const & id, +project_t::put_standard_certs(key_store & keys, + revision_id const & id, branch_name const & branch, utf8 const & changelog, date_t const & time, utf8 const & author) { - cert_revision_in_branch(id, branch, db); - cert_revision_changelog(id, changelog, db); - cert_revision_date_time(id, time, db); + cert_revision_in_branch(id, branch, db, keys); + cert_revision_changelog(id, changelog, db, keys); + cert_revision_date_time(id, time, db, keys); if (!author().empty()) - cert_revision_author(id, author(), db); + cert_revision_author(id, author(), db, keys); else - cert_revision_author_default(id, db); + cert_revision_author_default(id, db, keys); } void -project_t::put_standard_certs_from_options(revision_id const & id, +project_t::put_standard_certs_from_options(key_store & keys, + revision_id const & id, branch_name const & branch, utf8 const & changelog) { - put_standard_certs(id, + put_standard_certs(keys, id, branch, changelog, db.get_opt_date_or_cur_date(), db.get_opt_author()); } void -project_t::put_cert(revision_id const & id, +project_t::put_cert(key_store & keys, + revision_id const & id, cert_name const & name, cert_value const & value) { - put_simple_revision_cert(id, name, value, db); + put_simple_revision_cert(id, name, value, db, keys); } ============================================================ --- project.hh 995a7865a30fbda9d237b87d221b0a9f503399f5 +++ project.hh 4fce04f721322c557064635b86cfaf13fd54fc00 @@ -12,6 +12,7 @@ class database; #include "vocab.hh" class database; +class key_store; class tag_t { @@ -42,15 +43,17 @@ public: std::multimap *inverse_graph_cache_ptr = NULL); outdated_indicator get_tags(std::set & tags); - void put_tag(revision_id const & id, std::string const & name); + void put_tag(key_store & keys, revision_id const & id, std::string const & name); bool revision_is_in_branch(revision_id const & id, branch_name const & branch); - void put_revision_in_branch(revision_id const & id, + void put_revision_in_branch(key_store & keys, + revision_id const & id, branch_name const & branch); bool revision_is_suspended_in_branch(revision_id const & id, branch_name const & branch); - void suspend_revision_in_branch(revision_id const & id, - branch_name const & branch); + void suspend_revision_in_branch(key_store & keys, + revision_id const & id, + branch_name const & branch); outdated_indicator get_revision_cert_hashes(revision_id const & rid, std::vector > & hashes); @@ -64,16 +67,19 @@ public: outdated_indicator get_branch_certs(branch_name const & branch, std::vector > & certs); - void put_standard_certs(revision_id const & id, + void put_standard_certs(key_store & keys, + revision_id const & id, branch_name const & branch, utf8 const & changelog, date_t const & time, utf8 const & author); - void put_standard_certs_from_options(revision_id const & id, + void put_standard_certs_from_options(key_store & keys, + revision_id const & id, branch_name const & branch, utf8 const & changelog); - void put_cert(revision_id const & id, + void put_cert(key_store & keys, + revision_id const & id, cert_name const & name, cert_value const & value); }; ============================================================ --- rcs_import.cc b292beb9bd9d2b19ec34efb7ee4b19486cd2b92e +++ rcs_import.cc 352e42220856fef882d4125ca9df95c1896ceffb @@ -1009,6 +1009,7 @@ cluster_consumer { cvs_history & cvs; database & db; + key_store & keys; project_t & project; string const & branchname; @@ -1039,6 +1040,7 @@ cluster_consumer cluster_consumer(cvs_history & cvs, database & db, + key_store & keys, project_t & project, string const & branchname, cvs_branch const & branch, @@ -1070,13 +1072,14 @@ import_branch(cvs_history & cvs, void import_branch(cvs_history & cvs, database & db, + key_store & keys, project_t & project, string const & branchname, shared_ptr const & branch, ticker & n_revs) { cluster_set clusters; - cluster_consumer cons(cvs, db, project, branchname, *branch, n_revs); + cluster_consumer cons(cvs, db, keys, project, branchname, *branch, n_revs); unsigned long commits_remaining = branch->lineage.size(); // step 1: sort the lineage @@ -1202,6 +1205,7 @@ import_cvs_repo(system_path const & cvsr void import_cvs_repo(system_path const & cvsroot, database & db, + key_store & keys, project_t & project, branch_name const & branchname) @@ -1240,7 +1244,7 @@ import_cvs_repo(system_path const & cvsr string branchname = i->first; shared_ptr branch = i->second; L(FL("branch %s has %d entries") % branchname % branch->lineage.size()); - import_branch(cvs, db, project, branchname, branch, n_revs); + import_branch(cvs, db, keys, project, branchname, branch, n_revs); // free up some memory cvs.branches.erase(branchname); @@ -1250,7 +1254,7 @@ import_cvs_repo(system_path const & cvsr { transaction_guard guard(db); L(FL("trunk has %d entries") % cvs.trunk->lineage.size()); - import_branch(cvs, db, project, cvs.base_branch, cvs.trunk, n_revs); + import_branch(cvs, db, keys, project, cvs.base_branch, cvs.trunk, n_revs); guard.commit(); } @@ -1263,7 +1267,7 @@ import_cvs_repo(system_path const & cvsr { string tag = cvs.tag_interner.lookup(i->first); ui.set_tick_trailer("marking tag " + tag); - project.put_tag(i->second.second, tag); + project.put_tag(keys, i->second.second, tag); ++n_tags; } guard.commit(); @@ -1272,12 +1276,14 @@ cluster_consumer::cluster_consumer(cvs_h cluster_consumer::cluster_consumer(cvs_history & cvs, database & db, + key_store & keys, project_t & project, string const & branchname, cvs_branch const & branch, ticker & n_revs) : cvs(cvs), db(db), + keys(keys), project(project), branchname(branchname), branch(branch), @@ -1367,7 +1373,7 @@ cluster_consumer::store_auxiliary_certs( } } - project.put_standard_certs(p.rid, + project.put_standard_certs(keys, p.rid, branch_name(branchname), utf8(cvs.changelog_interner.lookup(p.changelog)), date_t::from_unix_epoch(p.time), ============================================================ --- rcs_import.hh b864e2f752da32d66ef1f2b083b63fddb8d6bb84 +++ rcs_import.hh 975c217ebf5a3b8b73ebc64290f12b8050ce05f3 @@ -12,11 +12,12 @@ class database; class system_path; class database; +class key_store; class project_t; class branch_name; void test_parse_rcs_file(system_path const & filename, database & db); -void import_cvs_repo(system_path const & cvsroot, database & db, +void import_cvs_repo(system_path const & cvsroot, database & db, key_store & keys, project_t & project, branch_name const & branchname); // Local Variables: ============================================================ --- revision.cc 08ced70b3682935ee831353d3953d7a9d264eec7 +++ revision.cc 725c74dec3553f8e4a5f5d341b7101518fb83aba @@ -847,9 +847,10 @@ struct anc_graph struct anc_graph { - anc_graph(bool existing, database & db) : + anc_graph(bool existing, database & db, key_store & keys) : existing_graph(existing), db(db), + keys(keys), max_node(0), n_nodes("nodes", "n", 1), n_certs_in("certs in", "c", 1), @@ -859,6 +860,7 @@ struct anc_graph bool existing_graph; database & db; + key_store & keys; u64 max_node; ticker n_nodes; @@ -941,7 +943,7 @@ void anc_graph::write_certs() cert_value val(j->second.second); cert new_cert; - make_simple_cert(rev.inner(), name, val, db, new_cert); + make_simple_cert(rev.inner(), name, val, db, keys, new_cert); revision rcert(new_cert); if (db.put_revision_cert(rcert)) ++n_certs_out; @@ -1639,10 +1641,10 @@ void } void -build_roster_style_revs_from_manifest_style_revs(database & db, +build_roster_style_revs_from_manifest_style_revs(database & db, key_store & keys, set const & attrs_to_drop) { - anc_graph graph(true, db); + anc_graph graph(true, db, keys); P(F("converting existing revision graph to new roster-style revisions")); multimap existing_graph; @@ -1684,10 +1686,10 @@ void void -build_changesets_from_manifest_ancestry(database & db, +build_changesets_from_manifest_ancestry(database & db, key_store & keys, set const & attrs_to_drop) { - anc_graph graph(false, db); + anc_graph graph(false, db, keys); P(F("rebuilding revision graph from manifest certs")); ============================================================ --- revision.hh 567afc600236952217f44e1af404e718c6d688ee +++ revision.hh 8a83775b59c469630040469f6a8ad0c5711f28f9 @@ -219,11 +219,11 @@ void commands::command_id const & cmd_name); void -build_changesets_from_manifest_ancestry(database & db, +build_changesets_from_manifest_ancestry(database & db, key_store & keys, std::set const & attrs_to_drop); void -build_roster_style_revs_from_manifest_style_revs(database & db, +build_roster_style_revs_from_manifest_style_revs(database & db, key_store & keys, std::set const & attrs_to_drop); void