# # # patch "Makefile.am" # from [fbd8a4f969f93fad2158db91ca84782b0665f7f8] # to [fc3c81973ef80f0a3d78a5017e25100f3ea479b9] # # patch "cmd_packet.cc" # from [d42befd7ed6120f7dd26606e09fefe07ed4b4f02] # to [34e89ffed5af38b68772de5dd856a11aa31233e7] # # patch "key_store.cc" # from [37017048204ae6741ee9b3e48b6065ede644a09a] # to [5c905fddc9d6f1f00210f24eaebe64cd6798acf2] # # patch "key_store.hh" # from [726c6a2b1295878948dfb7884969fa3b44d9f602] # to [5fad394e325c17fc777df8c842cfca3277b27e2d] # # patch "keys.cc" # from [1243cf1e9c5532e2944d6c679d2a5763e861bbad] # to [3e7731523632d889743797586fe41c3f4a3d293c] # # patch "keys.hh" # from [571830e357b1b545ac02b85ffdee5c4eadda3cf6] # to [20a82226fc92c1b34d830031939a6686cd4a54de] # # patch "lua-testsuite.lua" # from [b3c5868689f9c7ba3c3c85478eb10b59f1ceeb6c] # to [d2db92ea969f6b5a4a7cd2d6a0dc6c528b41bbfa] # # patch "packet.cc" # from [80cc020335599c6250e070a8a7553bbded3c240e] # to [ef7079554bc2b0a4be3e0fec17b820b18b9d1762] # # patch "packet.hh" # from [355a185960331d31e5493d7f6a285e8385377d1d] # to [c20147b757445b462c674190add9606835573603] # # patch "schema_migration.cc" # from [1206b6e5c43f5485004ad487a77407524a0747e4] # to [5e346221c35a8797dd6255b834ec06f6bfa70006] # # patch "tests/read_and_convert_old_privkey_packet/__driver__.lua" # from [4da19713ba2c6c334cc39d6f186e3bbe23d0506f] # to [e5916e38ae5227feb76d9a8814e2ec104a5d263f] # ============================================================ --- Makefile.am fbd8a4f969f93fad2158db91ca84782b0665f7f8 +++ Makefile.am fc3c81973ef80f0a3d78a5017e25100f3ea479b9 @@ -303,7 +303,7 @@ UNIT_TEST_SOURCES = \ # these files contain unit tests UNIT_TEST_SOURCES = \ basic_io.cc charset.cc commands.cc crypto_tests.cc cset.cc \ - dates.cc diff_patch.cc globish.cc graph.cc keys.cc netcmd.cc \ + dates.cc diff_patch.cc globish.cc graph.cc netcmd.cc \ netxx_pipe.cc numeric_vocab.cc option.cc outdated_indicator.cc \ packet.cc paths.cc refiner.cc restrictions.cc rev_height.cc \ revision.cc roster.cc roster_merge.cc simplestring_xform.cc \ @@ -313,7 +313,7 @@ UNIT_TEST_SRC_SUPPORT = \ # these files do not contain unit tests, but are required for unit testing # and must be recompiled for that purpose UNIT_TEST_SRC_SUPPORT = \ - lua_hooks.cc roster_delta.cc + roster_delta.cc # these files do not contain unit tests; they are required for unit # testing, but can be used "as is" from the main build. (many of @@ -324,9 +324,9 @@ UNIT_TEST_OBJ_SUPPORT = \ mtn-epoch.$(OBJEXT) mtn-file_io.$(OBJEXT) mtn-hmac.$(OBJEXT) \ mtn-inodeprint.$(OBJEXT) mtn-key_store.$(OBJEXT) \ mtn-lcs.$(OBJEXT) mtn-legacy.$(OBJEXT) mtn-lua.$(OBJEXT) \ - mtn-merkle_tree.$(OBJEXT) mtn-mt_version.$(OBJEXT) \ - mtn-mtn-sanity.$(OBJEXT) mtn-options.$(OBJEXT) \ - mtn-package_full_revision.$(OBJEXT) \ + mtn-lua_hooks.$(OBJEXT) mtn-merkle_tree.$(OBJEXT) \ + mtn-mt_version.$(OBJEXT) mtn-mtn-sanity.$(OBJEXT) \ + mtn-options.$(OBJEXT) mtn-package_full_revision.$(OBJEXT) \ mtn-package_revision.$(OBJEXT) mtn-project.$(OBJEXT) \ mtn-randomizer.$(OBJEXT) mtn-sanity.$(OBJEXT) \ mtn-schema.$(OBJEXT) mtn-schema_migration.$(OBJEXT) \ ============================================================ --- cmd_packet.cc d42befd7ed6120f7dd26606e09fefe07ed4b4f02 +++ cmd_packet.cc 34e89ffed5af38b68772de5dd856a11aa31233e7 @@ -123,10 +123,15 @@ namespace virtual void consume_key_pair(rsa_keypair_id const & ident, keypair const & kp) { - transaction_guard guard(app.db); app.keys.put_key_pair(ident, kp); - guard.commit(); } + + virtual void consume_old_private_key(rsa_keypair_id const & ident, + base64 const & k) + { + base64 dummy; + app.keys.migrate_old_key_pair(ident, k, dummy); + } }; } @@ -140,7 +145,7 @@ CMD(read, "read", "", CMD_REF(packet_io) size_t count = 0; if (args.empty()) { - count += read_packets(cin, dbw, app.keys); + count += read_packets(cin, dbw); N(count != 0, F("no packets found on stdin")); } else @@ -151,7 +156,7 @@ CMD(read, "read", "", CMD_REF(packet_io) data dat; read_data(system_path(*i), dat); istringstream ss(dat()); - count += read_packets(ss, dbw, app.keys); + count += read_packets(ss, dbw); } N(count != 0, FP("no packets found in given file", "no packets found in given files", ============================================================ --- key_store.cc 37017048204ae6741ee9b3e48b6065ede644a09a +++ key_store.cc 5c905fddc9d6f1f00210f24eaebe64cd6798acf2 @@ -29,7 +29,9 @@ using Botan::X509_PublicKey; using Botan::RSA_PublicKey; using Botan::SecureVector; using Botan::X509_PublicKey; +using Botan::PKCS8_PrivateKey; using Botan::PK_Signer; +using Botan::Pipe; class key_store_state { @@ -46,7 +48,9 @@ class key_store_state pair, shared_ptr > > signers; - key_store_state(app_state &); + key_store_state(app_state & app) + : have_read(false), app(app) + {} public: // just like put_key_pair except that the key is _not_ written to disk. @@ -61,9 +65,10 @@ namespace { struct keyreader : public packet_consumer { - key_store_state & ks; + key_store & ks; + key_store_state & kss; - keyreader(key_store_state & k): ks(k) {} + keyreader(key_store & ks, key_store_state & kss): ks(ks), kss(kss) {} virtual void consume_file_data(file_id const & ident, file_data const & dat) {E(false, F("Extraneous data in key store."));} @@ -88,16 +93,24 @@ namespace { L(FL("reading key pair '%s' from key store") % ident); - E(ks.put_key_pair_memory(ident, kp), + E(kss.put_key_pair_memory(ident, kp), F("Key store has multiple keys with id '%s'.") % ident); L(FL("successfully read key pair '%s' from key store") % ident); } - }; -} -key_store_state::key_store_state(app_state & a) : have_read(false), app(a) -{ + // for backward compatibility + virtual void consume_old_private_key(rsa_keypair_id const & ident, + base64 const & k) + { + W(F("converting old-format private key '%s'") % ident); + + base64 dummy; + ks.migrate_old_key_pair(ident, k, dummy); + + L(FL("successfully read key pair '%s' from key store") % ident); + } + }; } key_store::key_store(app_state & a) @@ -137,7 +150,7 @@ key_store::read_key_dir() return; } - keyreader kr(*s); + keyreader kr(*this, *s); for (vector::const_iterator i = key_files.begin(); i != key_files.end(); ++i) { @@ -145,7 +158,7 @@ key_store::read_key_dir() data dat; read_data(s->key_dir / *i, dat); istringstream is(dat()); - read_packets(is, kr, *this); + read_packets(is, kr); } } @@ -354,7 +367,8 @@ key_store::make_signature(database & db, if (!pub_key) throw informative_failure("Failed to get monotone RSA public key"); - agent.sign_data(*pub_key, tosign, sig_string); + + agent.sign_data(*pub_key, tosign, sig_string); } if (sig_string.length() <= 0) L(FL("make_signature: monotone and ssh-agent keys do not match, will" @@ -430,6 +444,93 @@ key_store::make_signature(database & db, } // +// Migration from old databases +// + +void +key_store::migrate_old_key_pair(rsa_keypair_id const & id, + base64 const & old_priv, + base64 const & pub) +{ + keypair kp; + SecureVector arc4_key; + utf8 phrase; + shared_ptr pkcs8_key; + shared_ptr priv_key; + + // See whether a lua hook will tell us the passphrase. + string lua_phrase; + if (s->app.lua.hook_get_passphrase(id, lua_phrase)) + phrase = utf8(lua_phrase); + else + get_passphrase(phrase, id, false, false); + + int cycles = 1; + for (;;) + try + { + arc4_key.set(reinterpret_cast(phrase().data()), + phrase().size()); + + Pipe arc4_decryptor(new Botan::Base64_Decoder, + get_cipher("ARC4", arc4_key, Botan::DECRYPTION)); + arc4_decryptor.process_msg(old_priv()); + + // This is necessary because PKCS8::load_key() cannot currently + // recognize an unencrypted, raw-BER blob as such, but gets it + // right if it's PEM-coded. + SecureVector arc4_decrypt(arc4_decryptor.read_all()); + Pipe p; + p.process_msg(Botan::PEM_Code::encode(arc4_decrypt, "PRIVATE KEY")); + + pkcs8_key.reset(Botan::PKCS8::load_key(p)); + break; + } + catch (Botan::Exception & e) + { + L(FL("migrate_old_key_pair: failure %d to load old private key: %s") + % cycles % e.what()); + + E(cycles <= 3, + F("failed to decrypt old private RSA key, " + "probably incorrect passphrase")); + + get_passphrase(phrase, id, false, false); + cycles++; + continue; + } + + priv_key = shared_dynamic_cast(pkcs8_key); + I(priv_key); + + // now we can write out the new key + Pipe p; + p.start_msg(); + Botan::PKCS8::encrypt_key(*priv_key, p, phrase(), + "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", + Botan::RAW_BER); + rsa_priv_key raw_priv = rsa_priv_key(p.read_all_as_string()); + encode_base64(raw_priv, kp.priv); + + // also the public key (which is derivable from the private key; asking + // Botan for the X.509 encoding of the private key implies that we want + // it to derive and produce the public key) + Pipe p2; + p2.start_msg(); + Botan::X509::encode(*priv_key, p2, Botan::RAW_BER); + rsa_pub_key raw_pub = rsa_pub_key(p2.read_all_as_string()); + encode_base64(raw_pub, kp.pub); + + // if the database had a public key entry for this key, make sure it + // matches what we derived from the private key entry, but don't abort the + // whole migration if it doesn't. + if (!pub().empty() && !keys_match(id, pub, id, kp.pub)) + W(F("public and private keys for %s don't match") % id); + + put_key_pair(id, kp); +} + +// // Hooks into the application configuration // ============================================================ --- key_store.hh 726c6a2b1295878948dfb7884969fa3b44d9f602 +++ key_store.hh 5fad394e325c17fc777df8c842cfca3277b27e2d @@ -54,7 +54,13 @@ public: void make_signature(database & db, rsa_keypair_id const & id, std::string const & tosign, base64 & signature); - + + // Migration from old databases + + void migrate_old_key_pair(rsa_keypair_id const & id, + base64 const & old_priv, + base64 const & pub); + // FIXME: quick hack to make these hooks and options available via // the key_store context bool hook_get_passphrase(rsa_keypair_id const & k, std::string & phrase); ============================================================ --- keys.cc 1243cf1e9c5532e2944d6c679d2a5763e861bbad +++ keys.cc 3e7731523632d889743797586fe41c3f4a3d293c @@ -63,16 +63,6 @@ using Botan::X509_PublicKey; // there will probably forever be bugs in this file. it's very // hard to get right, portably and securely. sorry about that. -static void -do_arc4(SecureVector & sym_key, - SecureVector & payload) -{ - L(FL("running arc4 process on %d bytes of data") % payload.size()); - Pipe enc(get_cipher("ARC4", sym_key, Botan::ENCRYPTION)); - enc.process_msg(payload); - payload = enc.read_all(); -} - // "raw" passphrase prompter; unaware of passphrase caching or the laziness // hook. KEYID is used only in prompts. CONFIRM_PHRASE causes the user to // be prompted to type the same thing twice, and will loop if they don't @@ -367,76 +357,7 @@ get_private_key(key_store & keys, I(false); } -// converts an oldstyle arc4 encrypted key into a newstyle pkcs#8 encoded -// key. the public key is also included void -migrate_private_key(key_store & keys, - rsa_keypair_id const & id, - base64< old_arc4_rsa_priv_key > const & old_priv, - keypair & new_kp) -{ - old_arc4_rsa_priv_key decoded_key; - SecureVector decrypted_key; - utf8 phrase; - - bool force = false; - - // need to decrypt the old key - shared_ptr priv_key; - L(FL("base64-decoding %d-byte old private key") % old_priv().size()); - decode_base64(old_priv, decoded_key); - for (int i = 0; i < 3; ++i) - { - decrypted_key.set(reinterpret_cast(decoded_key().data()), - decoded_key().size()); - get_passphrase(keys, phrase, id, force); - SecureVector sym_key; - sym_key.set(reinterpret_cast(phrase().data()), phrase().size()); - do_arc4(sym_key, decrypted_key); - - L(FL("building signer from %d-byte decrypted private key") % decrypted_key.size()); - - shared_ptr pkcs8_key; - try - { - Pipe p; - p.process_msg(Botan::PEM_Code::encode(decrypted_key, "PRIVATE KEY")); - pkcs8_key = shared_ptr(Botan::PKCS8::load_key(p)); - } - catch (...) - { - if (i >= 2) - throw informative_failure("failed to decrypt old private RSA key, " - "probably incorrect passphrase"); - // don't use the cache bad one next time - force = true; - continue; - } - - priv_key = shared_dynamic_cast(pkcs8_key); - if (!priv_key) - throw informative_failure("Failed to get old RSA key"); - } - - I(priv_key); - - // now we can write out the new key - Pipe p; - p.start_msg(); - Botan::PKCS8::encrypt_key(*priv_key, p, phrase(), - "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", Botan::RAW_BER); - rsa_priv_key raw_priv = rsa_priv_key(p.read_all_as_string()); - encode_base64(raw_priv, new_kp.priv); - - // also the public portion - Pipe p2; - p2.start_msg(); - Botan::X509::encode(*priv_key, p2, Botan::RAW_BER); - rsa_pub_key raw_pub = rsa_pub_key(p2.read_all_as_string()); - encode_base64(raw_pub, new_kp.pub); -} - -void change_key_passphrase(key_store & keys, rsa_keypair_id const & id, base64< rsa_priv_key > & encoded_key) @@ -542,34 +463,6 @@ keys_match(rsa_keypair_id const & id1, return hash1 == hash2; } -#ifdef BUILD_UNIT_TESTS -#include "unit_tests.hh" - -// This is not much of a unit test, but there is no point in strengthening -// it, because the only thing we still use arc4 for is migrating *really* -// old in-database private keys out to pkcs#8 files in the keystore. -UNIT_TEST(key, arc4) -{ - static Botan::byte const pt[] = "new fascist tidiness regime in place"; - static Botan::byte const phr[] = "still spring water"; - - SecureVector phrase(phr, sizeof phr - 1); - SecureVector orig(pt, sizeof pt - 1); - SecureVector data(orig); - - UNIT_TEST_CHECKPOINT("encrypting data"); - do_arc4(phrase, data); - - UNIT_TEST_CHECK(data != orig); - - UNIT_TEST_CHECKPOINT("decrypting data"); - do_arc4(phrase, data); - - UNIT_TEST_CHECK(data == orig); -} - -#endif // BUILD_UNIT_TESTS - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- keys.hh 571830e357b1b545ac02b85ffdee5c4eadda3cf6 +++ keys.hh 20a82226fc92c1b34d830031939a6686cd4a54de @@ -55,11 +55,6 @@ void change_key_passphrase(key_store & k rsa_keypair_id const & id, // to prompting user for phrase base64< rsa_priv_key > & encoded_key); -void migrate_private_key(key_store & keys, - rsa_keypair_id const & id, - base64< old_arc4_rsa_priv_key > const & old_priv, - keypair & kp); - bool check_signature(key_store & keys, rsa_keypair_id const & id, base64 const & pub, ============================================================ --- lua-testsuite.lua b3c5868689f9c7ba3c3c85478eb10b59f1ceeb6c +++ lua-testsuite.lua d2db92ea969f6b5a4a7cd2d6a0dc6c528b41bbfa @@ -38,6 +38,12 @@ end "address@hidden", unpack(arg)) end +function nokey_mtn(...) + return raw_mtn("--rcfile", test.root .. "/test_hooks.lua", -- "--nostd", + "--db=" .. test.root .. "/test.db", + "--keydir", test.root .. "/keys", unpack(arg)) +end + function minhooks_mtn(...) return raw_mtn("--db=" .. test.root .. "/test.db", "--keydir", test.root .. "/keys", ============================================================ --- packet.cc 80cc020335599c6250e070a8a7553bbded3c240e +++ packet.cc ef7079554bc2b0a4be3e0fec17b820b18b9d1762 @@ -10,7 +10,6 @@ #include "base.hh" #include -#include "app_state.hh" #include "cset.hh" #include "constants.hh" #include "packet.hh" @@ -18,8 +17,6 @@ #include "sanity.hh" #include "transforms.hh" #include "simplestring_xform.hh" -#include "keys.hh" -#include "key_store.hh" #include "cert.hh" using std::istream; @@ -100,190 +97,193 @@ packet_writer::consume_key_pair(rsa_keyp << "[end]\n"; } +void +packet_writer::consume_old_private_key(rsa_keypair_id const & ident, + base64 const & k) +{ + ost << "[privkey " << ident() << "]\n" + << trim_ws(k()) << '\n' + << "[end]\n"; +} -// -- remainder just deals with the regexes for reading packets off streams -// -// Note: If these change, the character sets in constants.cc may need to -// change too. -struct -feed_packet_consumer +// --- reading packets from streams --- +namespace { - key_store & keys; - size_t & count; - packet_consumer & cons; - feed_packet_consumer(size_t & count, packet_consumer & c, key_store & keys) - : keys(keys), count(count), cons(c) - {} - void validate_id(string const & id) const + struct + feed_packet_consumer { - E(id.size() == constants::idlen - && id.find_first_not_of(constants::legal_id_bytes) == string::npos, - F("malformed packet: invalid identifier")); - } - void validate_base64(string const & s) const - { - E(s.size() > 0 - && s.find_first_not_of(constants::legal_base64_bytes) == string::npos, - F("malformed packet: invalid base64 block")); - } - void validate_arg_base64(string const & s) const - { - E(s.find_first_not_of(constants::legal_base64_bytes) == string::npos, - F("malformed packet: invalid base64 block")); - } - void validate_key(string const & k) const - { - E(k.size() > 0 - && k.find_first_not_of(constants::legal_key_name_bytes) == string::npos, - F("malformed packet: invalid key name")); - } - void validate_certname(string const & cn) const - { - E(cn.size() > 0 - && cn.find_first_not_of(constants::legal_cert_name_bytes) == string::npos, - F("malformed packet: invalid cert name")); - } - void validate_no_more_args(istringstream & iss) const - { - string next; - iss >> next; - E(next.size() == 0, - F("malformed packet: too many arguments in header")); - } + size_t & count; + packet_consumer & cons; + feed_packet_consumer(size_t & count, packet_consumer & c) + : count(count), cons(c) + {} + void validate_id(string const & id) const + { + E(id.size() == constants::idlen + && id.find_first_not_of(constants::legal_id_bytes) == string::npos, + F("malformed packet: invalid identifier")); + } + void validate_base64(string const & s) const + { + E(s.size() > 0 + && s.find_first_not_of(constants::legal_base64_bytes) == string::npos, + F("malformed packet: invalid base64 block")); + } + void validate_arg_base64(string const & s) const + { + E(s.find_first_not_of(constants::legal_base64_bytes) == string::npos, + F("malformed packet: invalid base64 block")); + } + void validate_key(string const & k) const + { + E(k.size() > 0 + && k.find_first_not_of(constants::legal_key_name_bytes) == string::npos, + F("malformed packet: invalid key name")); + } + void validate_certname(string const & cn) const + { + E(cn.size() > 0 + && cn.find_first_not_of(constants::legal_cert_name_bytes) == string::npos, + F("malformed packet: invalid cert name")); + } + void validate_no_more_args(istringstream & iss) const + { + string next; + iss >> next; + E(next.size() == 0, + F("malformed packet: too many arguments in header")); + } - void data_packet(string const & args, string const & body, - bool is_revision) const - { - L(FL("read %s data packet") % (is_revision ? "revision" : "file")); - validate_id(args); - validate_base64(body); + void data_packet(string const & args, string const & body, + bool is_revision) const + { + L(FL("read %s data packet") % (is_revision ? "revision" : "file")); + validate_id(args); + validate_base64(body); - data contents; - unpack(base64 >(body), contents); - if (is_revision) - cons.consume_revision_data(revision_id(hexenc(args)), - revision_data(contents)); - else - cons.consume_file_data(file_id(hexenc(args)), - file_data(contents)); - } + data contents; + unpack(base64 >(body), contents); + if (is_revision) + cons.consume_revision_data(revision_id(hexenc(args)), + revision_data(contents)); + else + cons.consume_file_data(file_id(hexenc(args)), + file_data(contents)); + } - void fdelta_packet(string const & args, string const & body) const - { - L(FL("read delta packet")); - istringstream iss(args); - string src_id; iss >> src_id; validate_id(src_id); - string dst_id; iss >> dst_id; validate_id(dst_id); - validate_no_more_args(iss); - validate_base64(body); + void fdelta_packet(string const & args, string const & body) const + { + L(FL("read delta packet")); + istringstream iss(args); + string src_id; iss >> src_id; validate_id(src_id); + string dst_id; iss >> dst_id; validate_id(dst_id); + validate_no_more_args(iss); + validate_base64(body); - delta contents; - unpack(base64 >(body), contents); - cons.consume_file_delta(file_id(hexenc(src_id)), - file_id(hexenc(dst_id)), - file_delta(contents)); - } - static void read_rest(istream& in, string& dest) - { + delta contents; + unpack(base64 >(body), contents); + cons.consume_file_delta(file_id(hexenc(src_id)), + file_id(hexenc(dst_id)), + file_delta(contents)); + } + static void read_rest(istream& in, string& dest) + { - while( true ) + while( true ) + { + string t; + in >> t; + if( t.size() == 0 ) break; + dest += t; + } + } + void rcert_packet(string const & args, string const & body) const { - string t; - in >> t; - if( t.size() == 0 ) break; - dest += t; + L(FL("read cert packet")); + istringstream iss(args); + string certid; iss >> certid; validate_id(certid); + string name; iss >> name; validate_certname(name); + string keyid; iss >> keyid; validate_key(keyid); + string val; + read_rest(iss,val); validate_arg_base64(val); + validate_base64(body); + // canonicalize the base64 encodings to permit searches + cert t = cert(hexenc(certid), + cert_name(name), + base64(canonical_base64(val)), + rsa_keypair_id(keyid), + base64(canonical_base64(body))); + cons.consume_revision_cert(revision(t)); } - } - void rcert_packet(string const & args, string const & body) const - { - L(FL("read cert packet")); - istringstream iss(args); - string certid; iss >> certid; validate_id(certid); - string name; iss >> name; validate_certname(name); - string keyid; iss >> keyid; validate_key(keyid); - string val; - read_rest(iss,val); validate_arg_base64(val); - validate_base64(body); - // canonicalize the base64 encodings to permit searches - cert t = cert(hexenc(certid), - cert_name(name), - base64(canonical_base64(val)), - rsa_keypair_id(keyid), - base64(canonical_base64(body))); - cons.consume_revision_cert(revision(t)); - } - void pubkey_packet(string const & args, string const & body) const - { - L(FL("read pubkey packet")); - validate_key(args); - validate_base64(body); + void pubkey_packet(string const & args, string const & body) const + { + L(FL("read pubkey packet")); + validate_key(args); + validate_base64(body); - cons.consume_public_key(rsa_keypair_id(args), - base64(body)); - } + cons.consume_public_key(rsa_keypair_id(args), + base64(body)); + } - void keypair_packet(string const & args, string const & body) const - { - L(FL("read keypair packet")); - string::size_type hashpos = body.find('#'); - string pub(body, 0, hashpos); - string priv(body, hashpos+1); + void keypair_packet(string const & args, string const & body) const + { + L(FL("read keypair packet")); + string::size_type hashpos = body.find('#'); + string pub(body, 0, hashpos); + string priv(body, hashpos+1); - validate_key(args); - validate_base64(pub); - validate_base64(priv); - cons.consume_key_pair(rsa_keypair_id(args), - keypair(base64(pub), - base64(priv))); - } + validate_key(args); + validate_base64(pub); + validate_base64(priv); + cons.consume_key_pair(rsa_keypair_id(args), + keypair(base64(pub), + base64(priv))); + } - void privkey_packet(string const & args, string const & body) const - { - L(FL("read privkey packet")); - validate_key(args); - validate_base64(body); - keypair kp; - migrate_private_key(keys, - rsa_keypair_id(args), - base64(body), - kp); - cons.consume_key_pair(rsa_keypair_id(args), kp); - } + void privkey_packet(string const & args, string const & body) const + { + L(FL("read privkey packet")); + validate_key(args); + validate_base64(body); + cons.consume_old_private_key(rsa_keypair_id(args), + base64(body)); + } - void operator()(string const & type, - string const & args, - string const & body) const - { - if (type == "rdata") - data_packet(args, body, true); - else if (type == "fdata") - data_packet(args, body, false); - else if (type == "fdelta") - fdelta_packet(args, body); - else if (type == "rcert") - rcert_packet(args, body); - else if (type == "pubkey") - pubkey_packet(args, body); - else if (type == "keypair") - keypair_packet(args, body); - else if (type == "privkey") - privkey_packet(args, body); - else - { - W(F("unknown packet type: '%s'") % type); - return; - } - ++count; - } -}; + void operator()(string const & type, + string const & args, + string const & body) const + { + if (type == "rdata") + data_packet(args, body, true); + else if (type == "fdata") + data_packet(args, body, false); + else if (type == "fdelta") + fdelta_packet(args, body); + else if (type == "rcert") + rcert_packet(args, body); + else if (type == "pubkey") + pubkey_packet(args, body); + else if (type == "keypair") + keypair_packet(args, body); + else if (type == "privkey") + privkey_packet(args, body); + else + { + W(F("unknown packet type: '%s'") % type); + return; + } + ++count; + } + }; +} // anonymous namespace static size_t -extract_packets(string const & s, packet_consumer & cons, key_store & keys) +extract_packets(string const & s, packet_consumer & cons) { size_t count = 0; - feed_packet_consumer feeder(count, cons, keys); + feed_packet_consumer feeder(count, cons); string::const_iterator p, tbeg, tend, abeg, aend, bbeg, bend; @@ -354,7 +354,7 @@ size_t } size_t -read_packets(istream & in, packet_consumer & cons, key_store & keys) +read_packets(istream & in, packet_consumer & cons) { string accum, tmp; size_t count = 0; @@ -371,7 +371,7 @@ read_packets(istream & in, packet_consum { endpos += end.size(); string tmp = accum.substr(0, endpos); - count += extract_packets(tmp, cons, keys); + count += extract_packets(tmp, cons); if (endpos < accum.size() - 1) accum = accum.substr(endpos+1); else @@ -392,9 +392,8 @@ UNIT_TEST(packet, validators) { ostringstream oss; packet_writer pw(oss); - app_state app; size_t count; - feed_packet_consumer f(count, pw, app.keys); + feed_packet_consumer f(count, pw); #define N_THROW(expr) UNIT_TEST_CHECK_NOT_THROW(expr, informative_failure) #define Y_THROW(expr) UNIT_TEST_CHECK_THROW(expr, informative_failure) @@ -510,24 +509,23 @@ UNIT_TEST(packet, roundabout) // a keypair packet encode_base64(rsa_priv_key("this is not a real rsa key either!"), kp.priv); + pw.consume_key_pair(rsa_keypair_id("address@hidden"), kp); - pw.consume_key_pair(rsa_keypair_id("address@hidden"), kp); + // an old privkey packet + base64 oldpriv; + encode_base64(old_arc4_rsa_priv_key("and neither is this!"), oldpriv); + pw.consume_old_private_key(rsa_keypair_id("address@hidden"), oldpriv); tmp = oss.str(); } - // read_packets needs this to convert privkeys to keypairs. - // This doesn't test privkey packets (theres a tests/ test for that), - // so we don't actually use the app_state for anything. So a default one - // is ok. - app_state aaa; for (int i = 0; i < 10; ++i) { // now spin around sending and receiving this a few times ostringstream oss; packet_writer pw(oss); istringstream iss(tmp); - read_packets(iss, pw, aaa.keys); + read_packets(iss, pw); UNIT_TEST_CHECK(oss.str() == tmp); tmp = oss.str(); } ============================================================ --- packet.hh 355a185960331d31e5493d7f6a285e8385377d1d +++ packet.hh c20147b757445b462c674190add9606835573603 @@ -12,7 +12,6 @@ #include "vocab.hh" -class key_store; struct cert; // the idea here is that monotone can produce and consume "packet streams", @@ -53,6 +52,8 @@ public: base64< rsa_pub_key > const & k) = 0; virtual void consume_key_pair(rsa_keypair_id const & ident, keypair const & kp) = 0; + virtual void consume_old_private_key(rsa_keypair_id const & ident, + base64< old_arc4_rsa_priv_key > const & k) = 0; }; // this writer writes packets into a stream @@ -76,9 +77,11 @@ struct packet_writer : public packet_con base64< rsa_pub_key > const & k); virtual void consume_key_pair(rsa_keypair_id const & ident, keypair const & kp); + virtual void consume_old_private_key(rsa_keypair_id const & ident, + base64< old_arc4_rsa_priv_key > const & k); }; -size_t read_packets(std::istream & in, packet_consumer & cons, key_store & keys); +size_t read_packets(std::istream & in, packet_consumer & cons); // Local Variables: // mode: C++ ============================================================ --- schema_migration.cc 1206b6e5c43f5485004ad487a77407524a0747e4 +++ schema_migration.cc 5e346221c35a8797dd6255b834ec06f6bfa70006 @@ -16,7 +16,6 @@ #include "sanity.hh" #include "schema_migration.hh" #include "key_store.hh" -#include "keys.hh" #include "transforms.hh" #include "ui.hh" @@ -498,22 +497,15 @@ migrate_to_external_privkeys(sqlite3 * d while (stmt.step()) { rsa_keypair_id ident(stmt.column_string(0)); - base64< old_arc4_rsa_priv_key > old_priv(stmt.column_string(1)); + base64 old_priv(stmt.column_string(1)); + base64 pub; - keypair kp; - migrate_private_key(keys, ident, old_priv, kp); - MM(kp.pub); - if (stmt.column_nonnull(2)) - { - base64< rsa_pub_key > pub(stmt.column_string(2)); - MM(pub); - N(keys_match(ident, pub, ident, kp.pub), - F("public and private keys for %s don't match") % ident); - } + pub = base64(stmt.column_string(2)); + P(F("moving key '%s' from database to %s") % ident % keys.get_key_dir()); - keys.put_key_pair(ident, kp); + keys.migrate_old_key_pair(ident, old_priv, pub); } } ============================================================ --- tests/read_and_convert_old_privkey_packet/__driver__.lua 4da19713ba2c6c334cc39d6f186e3bbe23d0506f +++ tests/read_and_convert_old_privkey_packet/__driver__.lua e5916e38ae5227feb76d9a8814e2ec104a5d263f @@ -1,18 +1,46 @@ mtn_setup() mtn_setup() -- this is an old privkey generated with 0.23 +-- mtn read should accept it and convert it to the new format check(get("old_privkey", "pkt")) - check(mtn("read", "pkt"), 0, false, true) check(qgrep("read 1 packet", "stderr")) +check(qgrep("keypair", "keys/address@hidden")) +check(not qgrep("privkey", "keys/address@hidden")) + check(mtn("ls", "keys"), 0, true) check(qgrep("address@hidden", "stdout")) addfile("foo", "foo") --- check that we can use the key we just read --- if it imported wrong, it'll fail by not accepting the passphrase +-- if we put the old privkey in the keydir, it should get +-- auto-converted the first time anything tries to read it +check(get("old_privkey", "keys/address@hidden")) +check(mtn("ls", "keys"), 0, true, true, "address@hidden") +check(qgrep("address@hidden", "stdout")) +check(qgrep("converting old-format", "stderr")) +check(qgrep("keypair", "keys/address@hidden")) +check(not qgrep("privkey", "keys/address@hidden")) + + +-- check that we can use the converted key to commit with + +-- 1) without a --key= switch, it should give us the multiple +-- available keys message and error out +remove("_MTN/options") +check(nokey_mtn("ci", "-bfoo", "-mbar"), 1, nil, true) +check(qgrep("multiple private keys", "stderr")) + +-- 2) address@hidden should work + +remove("_MTN/options") +check(nokey_mtn("ci", "-bfoo", "-mbar", "address@hidden"), 0, nil, true) +check(qgrep("committed revision", "stderr")) + +-- 3) that should have actually signed the certs with that key +check(mtn("ls", "certs", "h:foo"), 0, true, false) +check(qgrep("Key *: address@hidden", "stdout")) +check(not qgrep("Key *: address@hidden", "stdout")) -check(mtn("ci", "-bfoo", "-mbar"), 0, false, false, string.rep("address@hidden", 2))