# # # patch "automate.cc" # from [57c0c432f47e33c999104acdaa1bd38c65a1fae0] # to [e3e32861dd1d2b804f9c9e17b2493e5ff8687e88] # # patch "cmd_key_cert.cc" # from [a5afd0a653db10653e01d802e2e66d19d087ebed] # to [94ec51ced8680fd359478be5be38ac1813636881] # # patch "database.cc" # from [0797db2c719528424be8c3ddfccc52fe256d28ce] # to [23fbe46aed38d2500801aa3bda745464b63f58de] # # patch "database.hh" # from [28fe3324ba174366b2ee92bb70256f2083de0a57] # to [dbf1f978a8f1f65bfc11328cf2684ff99fe327c8] # # patch "key_store.cc" # from [5c905fddc9d6f1f00210f24eaebe64cd6798acf2] # to [b061169c0ab5dfb2e3eb88f525883d8ea70009b4] # # patch "key_store.hh" # from [5fad394e325c17fc777df8c842cfca3277b27e2d] # to [6711a8d0a768cf22b124daf00511b8d656f2cb71] # # patch "keys.cc" # from [3e7731523632d889743797586fe41c3f4a3d293c] # to [91c80e8e4bbc58a60638b01474724ac3b9ea07f2] # # patch "keys.hh" # from [20a82226fc92c1b34d830031939a6686cd4a54de] # to [3c50c4270dfa507b114173189075bf9af2423f59] # # patch "tests/automate_genkey/__driver__.lua" # from [04c4a76116eb2a18077d9c5633e429d141db11b1] # to [144117cd43682ed1c2aa1c717b101c81a49d4bce] # # patch "tests/automate_keys/__driver__.lua" # from [8d88368298c3af5b2c7e938d7731cbc045549867] # to [42ad71325d1df9437aa6623996367bbeaa28fe93] # ============================================================ --- automate.cc 57c0c432f47e33c999104acdaa1bd38c65a1fae0 +++ automate.cc e3e32861dd1d2b804f9c9e17b2493e5ff8687e88 @@ -1746,28 +1746,14 @@ CMD_AUTOMATE(genkey, N_("KEYID PASSPHRAS utf8 passphrase = idx(args, 1); - key_store & keys = app.keys; - bool exists = keys.key_pair_exists(ident); - if (db.database_specified()) - { - transaction_guard guard(db); - exists = exists || db.public_key_exists(ident); - guard.commit(); - } + hexenc pubhash, privhash; + app.keys.create_key_pair(app.db, ident, &passphrase, &pubhash, &privhash); - N(!exists, F("key '%s' already exists") % ident); - - keypair kp; - generate_key_pair(kp, passphrase); - keys.put_key_pair(ident, kp); - basic_io::printer prt; basic_io::stanza stz; - hexenc privhash, pubhash; vector publocs, privlocs; - key_hash_code(ident, kp.pub, pubhash); - key_hash_code(ident, kp.priv, privhash); - + if (app.db.database_specified()) + publocs.push_back("database"); publocs.push_back("keystore"); privlocs.push_back("keystore"); ============================================================ --- cmd_key_cert.cc a5afd0a653db10653e01d802e2e66d19d087ebed +++ cmd_key_cert.cc 94ec51ced8680fd359478be5be38ac1813636881 @@ -43,22 +43,8 @@ CMD(genkey, "genkey", "", CMD_REF(key_an rsa_keypair_id ident; internalize_rsa_keypair_id(idx(args, 0), ident); - bool exists = app.keys.key_pair_exists(ident); - if (app.db.database_specified()) - exists = exists || app.db.public_key_exists(ident); - N(!exists, F("key '%s' already exists") % ident); - - utf8 phrase; - get_passphrase(phrase, ident, true, true); - - keypair kp; - P(F("generating key-pair '%s'") % ident); - generate_key_pair(kp, phrase); - - P(F("storing key-pair '%s' in %s/") - % ident % app.keys.get_key_dir()); - app.keys.put_key_pair(ident, kp); + app.keys.create_key_pair(app.db, ident); } CMD(dropkey, "dropkey", "", CMD_REF(key_and_cert), N_("KEYID"), ============================================================ --- database.cc 0797db2c719528424be8c3ddfccc52fe256d28ce +++ database.cc 23fbe46aed38d2500801aa3bda745464b63f58de @@ -233,7 +233,7 @@ class database_impl void begin_transaction(bool exclusive); void commit_transaction(); void rollback_transaction(); - friend class transaction_guard; + friend class conditional_transaction_guard; struct roster_writeback_manager { @@ -3682,21 +3682,10 @@ database::hook_get_revision_cert_trust(s // transaction guards -transaction_guard::transaction_guard(database & d, bool exclusive, - size_t checkpoint_batch_size, - size_t checkpoint_batch_bytes) - : imp(d.imp), - checkpoint_batch_size(checkpoint_batch_size), - checkpoint_batch_bytes(checkpoint_batch_bytes), - checkpointed_calls(0), - checkpointed_bytes(0), - committed(false), exclusive(exclusive) +conditional_transaction_guard::~conditional_transaction_guard() { - imp->begin_transaction(exclusive); -} - -transaction_guard::~transaction_guard() -{ + if (!acquired) + return; if (committed) imp->commit_transaction(); else @@ -3704,8 +3693,17 @@ void } void -transaction_guard::do_checkpoint() +conditional_transaction_guard::acquire() { + I(!acquired); + acquired = true; + imp->begin_transaction(exclusive); +} + +void +conditional_transaction_guard::do_checkpoint() +{ + I(acquired); imp->commit_transaction(); imp->begin_transaction(exclusive); checkpointed_calls = 0; @@ -3713,9 +3711,10 @@ void } void -transaction_guard::maybe_checkpoint(size_t nbytes) +conditional_transaction_guard::maybe_checkpoint(size_t nbytes) { - checkpointed_calls += 1; + I(acquired); + checkpointed_calls += 1; checkpointed_bytes += nbytes; if (checkpointed_calls >= checkpoint_batch_size || checkpointed_bytes >= checkpoint_batch_bytes) @@ -3723,11 +3722,14 @@ void } void -transaction_guard::commit() +conditional_transaction_guard::commit() { + I(acquired); committed = true; } + + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- database.hh 28fe3324ba174366b2ee92bb70256f2083de0a57 +++ database.hh dbf1f978a8f1f65bfc11328cf2684ff99fe327c8 @@ -26,7 +26,7 @@ struct revision_t; class outdated_indicator; class rev_height; struct revision_t; -class transaction_guard; +class conditional_transaction_guard; // this file defines a public, typed interface to the database. // the database class encapsulates all knowledge about sqlite, @@ -93,7 +93,7 @@ private: // --== Transactions ==-- // private: - friend class transaction_guard; + friend class conditional_transaction_guard; // // --== Write-buffering -- tied into transaction ==-- @@ -536,8 +536,18 @@ inline marking_map const & parent_markin // full commits at high frequency is too high. The solution for these // platforms is to run inside a longer-lived transaction (session-length), // and checkpoint at higher granularity (every megabyte or so). +// +// A conditional transaction guard is just like a transaction guard, +// except that it doesn't begin the transaction until you call acquire(). +// If you don't call acquire(), you must not call commit(), do_checkpoint(), +// or maybe_checkpoint() either. +// +// Implementation note: Making transaction_guard inherit from +// conditional_transaction_guard means we can reuse all the latter's methods +// and just call acquire() in transaction_guard's constructor. If we did it +// the other way around they would wind up being totally unrelated classes. -class transaction_guard +class conditional_transaction_guard { database_impl * imp; size_t const checkpoint_batch_size; @@ -545,17 +555,40 @@ class transaction_guard size_t checkpointed_calls; size_t checkpointed_bytes; bool committed; + bool acquired; bool const exclusive; public: - transaction_guard(database & d, bool exclusive=true, - size_t checkpoint_batch_size=1000, - size_t checkpoint_batch_bytes=0xfffff); - ~transaction_guard(); + conditional_transaction_guard(database & d, bool exclusive=true, + size_t checkpoint_batch_size=1000, + size_t checkpoint_batch_bytes=0xfffff) + : imp(d.imp), + checkpoint_batch_size(checkpoint_batch_size), + checkpoint_batch_bytes(checkpoint_batch_bytes), + checkpointed_calls(0), + checkpointed_bytes(0), + committed(false), acquired(false), exclusive(exclusive) + {} + + ~conditional_transaction_guard(); + void acquire(); + void commit(); void do_checkpoint(); void maybe_checkpoint(size_t nbytes); - void commit(); }; +class transaction_guard : public conditional_transaction_guard +{ +public: + transaction_guard(database & d, bool exclusive=true, + size_t checkpoint_batch_size=1000, + size_t checkpoint_batch_bytes=0xfffff) + : conditional_transaction_guard(d, exclusive, checkpoint_batch_size, + checkpoint_batch_bytes) + { + acquire(); + } +}; + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- key_store.cc 5c905fddc9d6f1f00210f24eaebe64cd6798acf2 +++ key_store.cc b061169c0ab5dfb2e3eb88f525883d8ea70009b4 @@ -8,6 +8,7 @@ #include "globish.hh" #include "app_state.hh" #include "transforms.hh" +#include "constants.hh" #include "botan/botan.h" #include "botan/rsa.h" @@ -328,6 +329,79 @@ void // void +key_store::create_key_pair(database & db, + rsa_keypair_id const & id, + utf8 const * maybe_passphrase, + hexenc * maybe_pubhash, + hexenc * maybe_privhash) +{ + conditional_transaction_guard guard(db); + + bool exists = key_pair_exists(id); + if (db.database_specified()) + { + guard.acquire(); + exists = exists || db.public_key_exists(id); + } + N(!exists, F("key '%s' already exists") % id); + + utf8 prompted_passphrase; + if (!maybe_passphrase) + { + get_passphrase(prompted_passphrase, id, true, true); + maybe_passphrase = &prompted_passphrase; + } + + // okay, now we can create the key + P(F("generating key-pair '%s'") % id); + RSA_PrivateKey priv(constants::keylen); + + // serialize and maybe encrypt the private key + SecureVector pubkey, privkey; + Pipe p; + p.start_msg(); + if ((*maybe_passphrase)().length()) + Botan::PKCS8::encrypt_key(priv, p, + (*maybe_passphrase)(), + "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", + Botan::RAW_BER); + else + Botan::PKCS8::encode(priv, p); + rsa_priv_key raw_priv_key(p.read_all_as_string()); + + // serialize the public key + Pipe p2; + p2.start_msg(); + Botan::X509::encode(priv, p2, Botan::RAW_BER); + rsa_pub_key raw_pub_key(p2.read_all_as_string()); + + // convert to storage format + keypair kp; + encode_base64(raw_priv_key, kp.priv); + encode_base64(raw_pub_key, kp.pub); + L(FL("generated %d-byte public key\n" + "generated %d-byte (encrypted) private key\n") + % kp.pub().size() + % kp.priv().size()); + + // and save it. + P(F("storing key-pair '%s' in %s/") % id % get_key_dir()); + put_key_pair(id, kp); + + if (db.database_specified()) + { + P(F("storing public key '%s' in %s") % id % db.get_filename()); + db.put_key(id, kp.pub); + guard.commit(); + } + + if (maybe_pubhash) + key_hash_code(id, kp.pub, *maybe_pubhash); + if (maybe_privhash) + key_hash_code(id, kp.priv, *maybe_privhash); +} + +void key_store::make_signature(database & db, rsa_keypair_id const & id, string const & tosign, ============================================================ --- key_store.hh 5fad394e325c17fc777df8c842cfca3277b27e2d +++ key_store.hh 6711a8d0a768cf22b124daf00511b8d656f2cb71 @@ -30,6 +30,8 @@ public: void set_key_dir(system_path const & kd); system_path const & get_key_dir(); + // Basic key I/O + void get_key_ids(std::vector & priv); void get_key_ids(globish const & pattern, std::vector & priv); @@ -51,6 +53,11 @@ public: // Crypto operations + void create_key_pair(database & db, rsa_keypair_id const & id, + utf8 const * maybe_passphrase = NULL, + hexenc * maybe_pubhash = NULL, + hexenc * maybe_privhash = NULL); + void make_signature(database & db, rsa_keypair_id const & id, std::string const & tosign, base64 & signature); ============================================================ --- keys.cc 3e7731523632d889743797586fe41c3f4a3d293c +++ keys.cc 91c80e8e4bbc58a60638b01474724ac3b9ea07f2 @@ -257,45 +257,6 @@ cache_user_key(options const & opts, lua get_user_key(key, opts, lua, keys, db); } -void -generate_key_pair(keypair & kp_out, - utf8 const phrase) -{ - SecureVector pubkey, privkey; - rsa_pub_key raw_pub_key; - rsa_priv_key raw_priv_key; - - // generate private key (and encrypt it) - RSA_PrivateKey priv(constants::keylen); - - Pipe p; - p.start_msg(); - if (phrase().length()) { - Botan::PKCS8::encrypt_key(priv, - p, - phrase(), - "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", - Botan::RAW_BER); - } else { - Botan::PKCS8::encode(priv, p); - } - raw_priv_key = rsa_priv_key(p.read_all_as_string()); - - // generate public key - Pipe p2; - p2.start_msg(); - Botan::X509::encode(priv, p2, Botan::RAW_BER); - raw_pub_key = rsa_pub_key(p2.read_all_as_string()); - - // if all that worked, we can return our results to caller - encode_base64(raw_priv_key, kp_out.priv); - encode_base64(raw_pub_key, kp_out.pub); - L(FL("generated %d-byte public key\n" - "generated %d-byte (encrypted) private key\n") - % kp_out.pub().size() - % kp_out.priv().size()); -} - // ask for passphrase then decrypt a private key. shared_ptr get_private_key(key_store & keys, ============================================================ --- keys.hh 20a82226fc92c1b34d830031939a6686cd4a54de +++ keys.hh 3c50c4270dfa507b114173189075bf9af2423f59 @@ -44,13 +44,6 @@ void load_key_pair(key_store & keys, rsa_keypair_id const & id, keypair & kp); -void generate_key_pair(key_store & keys, // to hook for phrase - rsa_keypair_id const & id, // to prompting user for phrase - keypair & kp_out); - -void generate_key_pair(keypair & kp_out, - utf8 const phrase); - void change_key_passphrase(key_store & keys, // to hook for phrase rsa_keypair_id const & id, // to prompting user for phrase base64< rsa_priv_key > & encoded_key); ============================================================ --- tests/automate_genkey/__driver__.lua 04c4a76116eb2a18077d9c5633e429d141db11b1 +++ tests/automate_genkey/__driver__.lua 144117cd43682ed1c2aa1c717b101c81a49d4bce @@ -40,7 +40,7 @@ end if string.find(line.name, "private") then locs[key].priv = true end if string.find(line.name, "public") then locs[key].pub = true end end -check(locs["address@hidden"].db == false) +check(locs["address@hidden"].db == true) check(locs["address@hidden"].ks == true) check(locs["address@hidden"].priv == true) check(locs["address@hidden"].pub == true) ============================================================ --- tests/automate_keys/__driver__.lua 8d88368298c3af5b2c7e938d7731cbc045549867 +++ tests/automate_keys/__driver__.lua 42ad71325d1df9437aa6623996367bbeaa28fe93 @@ -1,14 +1,16 @@ check(mtn("ci", "-m", "foobar"), 0, fals mtn_setup() addfile("testfile", "foo bar") check(mtn("ci", "-m", "foobar"), 0, false, false) -check(mtn("genkey", "address@hidden"), 0, false, false, string.rep("address@hidden", 2)) -check(mtn("genkey", "address@hidden"), 0, false, false, string.rep("address@hidden", 2)) -check(mtn("pubkey", "address@hidden"), 0, true) -rename("stdout", "baz") -check(mtn("dropkey", "address@hidden"), 0, false, false) -check(mtn("read"), 0, false, false, {"baz"}) +remove("_MTN/options") +check(nodb_mtn("genkey", "address@hidden"), + 0, false, false, string.rep("address@hidden", 2)) +remove("_MTN/options") +check(mtn("genkey", "address@hidden"), + 0, false, false, string.rep("address@hidden", 2)) +remove("_MTN/options") +check(nodb_mtn("dropkey", "address@hidden"), 0, false, false) -- we now have address@hidden in the keystore, address@hidden in both keystore -- and database, and address@hidden in only the database