monotone

monotone Mtn Source Tree

Root/key_store.cc

1#include "base.hh"
2#include <sstream>
3
4#include "key_store.hh"
5#include "file_io.hh"
6#include "packet.hh"
7#include "database.hh"
8#include "keys.hh"
9#include "globish.hh"
10#include "app_state.hh"
11#include "transforms.hh"
12#include "constants.hh"
13#include "ssh_agent.hh"
14#include "safe_map.hh"
15
16#include "botan/botan.h"
17#include "botan/rsa.h"
18#include "botan/keypair.h"
19#include "botan/pem.h"
20#include "botan_pipe_cache.hh"
21
22using std::make_pair;
23using std::istringstream;
24using std::map;
25using std::ostringstream;
26using std::pair;
27using std::string;
28using std::vector;
29
30using boost::scoped_ptr;
31using boost::shared_ptr;
32using boost::shared_dynamic_cast;
33
34using Botan::RSA_PrivateKey;
35using Botan::RSA_PublicKey;
36using Botan::SecureVector;
37using Botan::X509_PublicKey;
38using Botan::PKCS8_PrivateKey;
39using Botan::PK_Decryptor;
40using Botan::PK_Signer;
41using Botan::Pipe;
42
43struct key_store_state
44{
45 system_path const key_dir;
46 string const ssh_sign_mode;
47 bool have_read;
48 lua_hooks & lua;
49 map<rsa_keypair_id, keypair> keys;
50 map<id, rsa_keypair_id> hashes;
51
52 // These are used to cache keys and signers (if the hook allows).
53 map<rsa_keypair_id, shared_ptr<RSA_PrivateKey> > privkey_cache;
54 map<rsa_keypair_id, shared_ptr<PK_Signer> > signer_cache;
55
56 // Initialized when first required.
57 scoped_ptr<ssh_agent> agent;
58
59 key_store_state(app_state & app)
60 : key_dir(app.opts.key_dir), ssh_sign_mode(app.opts.ssh_sign),
61 have_read(false), lua(app.lua)
62 {
63 N(app.opts.key_dir_given
64 || app.opts.conf_dir_given
65 || !app.opts.no_default_confdir,
66 F("No available keystore found"));
67 }
68
69 // internal methods
70 void get_key_file(rsa_keypair_id const & ident, system_path & file);
71 void write_key(rsa_keypair_id const & ident, keypair const & kp);
72 void maybe_read_key_dir();
73 shared_ptr<RSA_PrivateKey> decrypt_private_key(rsa_keypair_id const & id,
74 bool force_from_user = false);
75
76 // just like put_key_pair except that the key is _not_ written to disk.
77 // for internal use in reading keys back from disk.
78 bool put_key_pair_memory(rsa_keypair_id const & ident,
79 keypair const & kp);
80
81 // wrapper around accesses to agent, initializes as needed
82 ssh_agent & get_agent()
83 {
84 if (!agent)
85 agent.reset(new ssh_agent);
86 return *agent;
87 }
88
89 // duplicates of key_store interfaces for use by key_store_state methods
90 // and the keyreader.
91 bool maybe_get_key_pair(rsa_keypair_id const & ident,
92 keypair & kp);
93 bool put_key_pair(rsa_keypair_id const & ident,
94 keypair const & kp);
95 void migrate_old_key_pair(rsa_keypair_id const & id,
96 old_arc4_rsa_priv_key const & old_priv,
97 rsa_pub_key const & pub);
98};
99
100namespace
101{
102 struct keyreader : public packet_consumer
103 {
104 key_store_state & kss;
105
106 keyreader(key_store_state & kss): kss(kss) {}
107 virtual void consume_file_data(file_id const & ident,
108 file_data const & dat)
109 {E(false, F("Extraneous data in key store."));}
110 virtual void consume_file_delta(file_id const & id_old,
111 file_id const & id_new,
112 file_delta const & del)
113 {E(false, F("Extraneous data in key store."));}
114
115 virtual void consume_revision_data(revision_id const & ident,
116 revision_data const & dat)
117 {E(false, F("Extraneous data in key store."));}
118 virtual void consume_revision_cert(revision<cert> const & t)
119 {E(false, F("Extraneous data in key store."));}
120
121
122 virtual void consume_public_key(rsa_keypair_id const & ident,
123 rsa_pub_key const & k)
124 {E(false, F("Extraneous data in key store."));}
125
126 virtual void consume_key_pair(rsa_keypair_id const & ident,
127 keypair const & kp)
128 {
129 L(FL("reading key pair '%s' from key store") % ident);
130
131 E(kss.put_key_pair_memory(ident, kp),
132 F("Key store has multiple keys with id '%s'.") % ident);
133
134 L(FL("successfully read key pair '%s' from key store") % ident);
135 }
136
137 // for backward compatibility
138 virtual void consume_old_private_key(rsa_keypair_id const & ident,
139 old_arc4_rsa_priv_key const & k)
140 {
141 W(F("converting old-format private key '%s'") % ident);
142
143 rsa_pub_key dummy;
144 kss.migrate_old_key_pair(ident, k, dummy);
145
146 L(FL("successfully read key pair '%s' from key store") % ident);
147 }
148 };
149}
150
151key_store::key_store(app_state & a)
152 : s(new key_store_state(a))
153{}
154
155key_store::~key_store()
156{}
157
158system_path const &
159key_store::get_key_dir()
160{
161 return s->key_dir;
162}
163
164void
165key_store_state::maybe_read_key_dir()
166{
167 if (have_read)
168 return;
169 have_read = true;
170
171 vector<path_component> key_files, dirs;
172 if (directory_exists(key_dir))
173 {
174 L(FL("reading key dir '%s'") % key_dir);
175 read_directory(key_dir, key_files, dirs);
176 }
177 else
178 {
179 L(FL("key dir '%s' does not exist") % key_dir);
180 return;
181 }
182
183 keyreader kr(*this);
184 for (vector<path_component>::const_iterator i = key_files.begin();
185 i != key_files.end(); ++i)
186 {
187 L(FL("reading keys from file '%s'") % (*i));
188 data dat;
189 read_data(key_dir / *i, dat);
190 istringstream is(dat());
191 read_packets(is, kr);
192 }
193}
194
195void
196key_store::get_key_ids(globish const & pattern,
197 vector<rsa_keypair_id> & priv)
198{
199 s->maybe_read_key_dir();
200 priv.clear();
201 for (map<rsa_keypair_id, keypair>::const_iterator
202 i = s->keys.begin(); i != s->keys.end(); ++i)
203 if (pattern.matches((i->first)()))
204 priv.push_back(i->first);
205}
206
207void
208key_store::get_key_ids(vector<rsa_keypair_id> & priv)
209{
210 s->maybe_read_key_dir();
211 priv.clear();
212 for (map<rsa_keypair_id, keypair>::const_iterator
213 i = s->keys.begin(); i != s->keys.end(); ++i)
214 priv.push_back(i->first);
215}
216
217bool
218key_store::key_pair_exists(rsa_keypair_id const & ident)
219{
220 s->maybe_read_key_dir();
221 return s->keys.find(ident) != s->keys.end();
222}
223
224bool
225key_store_state::maybe_get_key_pair(rsa_keypair_id const & ident,
226 keypair & kp)
227{
228 maybe_read_key_dir();
229 map<rsa_keypair_id, keypair>::const_iterator i = keys.find(ident);
230 if (i == keys.end())
231 return false;
232 kp = i->second;
233 return true;
234}
235
236bool
237key_store::maybe_get_key_pair(rsa_keypair_id const & ident,
238 keypair & kp)
239{
240 return s->maybe_get_key_pair(ident, kp);
241}
242
243void
244key_store::get_key_pair(rsa_keypair_id const & ident,
245 keypair & kp)
246{
247 bool found = maybe_get_key_pair(ident, kp);
248 I(found);
249}
250
251bool
252key_store::maybe_get_key_pair(id const & hash,
253 rsa_keypair_id & keyid,
254 keypair & kp)
255{
256 s->maybe_read_key_dir();
257 map<id, rsa_keypair_id>::const_iterator hi = s->hashes.find(hash);
258 if (hi == s->hashes.end())
259 return false;
260
261 map<rsa_keypair_id, keypair>::const_iterator ki = s->keys.find(hi->second);
262 if (ki == s->keys.end())
263 return false;
264 keyid = hi->second;
265 kp = ki->second;
266 return true;
267}
268
269void
270key_store_state::get_key_file(rsa_keypair_id const & ident,
271 system_path & file)
272{
273 // filename is the keypair id, except that some characters can't be put in
274 // filenames (especially on windows).
275 string leaf = ident();
276 for (unsigned int i = 0; i < leaf.size(); ++i)
277 if (leaf.at(i) == '+')
278 leaf.at(i) = '_';
279
280 file = key_dir / path_component(leaf);
281}
282
283void
284key_store_state::write_key(rsa_keypair_id const & ident,
285 keypair const & kp)
286{
287 ostringstream oss;
288 packet_writer pw(oss);
289 pw.consume_key_pair(ident, kp);
290 data dat(oss.str());
291
292 system_path file;
293 get_key_file(ident, file);
294
295 // Make sure the private key is not readable by anyone other than the user.
296 L(FL("writing key '%s' to file '%s' in dir '%s'") % ident % file % key_dir);
297 write_data_userprivate(file, dat, key_dir);
298}
299
300bool
301key_store_state::put_key_pair(rsa_keypair_id const & ident,
302 keypair const & kp)
303{
304 maybe_read_key_dir();
305 bool newkey = put_key_pair_memory(ident, kp);
306 if (newkey)
307 write_key(ident, kp);
308 return newkey;
309}
310
311bool
312key_store::put_key_pair(rsa_keypair_id const & ident,
313 keypair const & kp)
314{
315 return s->put_key_pair(ident, kp);
316}
317
318bool
319key_store_state::put_key_pair_memory(rsa_keypair_id const & ident,
320 keypair const & kp)
321{
322 L(FL("putting key pair '%s'") % ident);
323 pair<map<rsa_keypair_id, keypair>::iterator, bool> res;
324 res = keys.insert(make_pair(ident, kp));
325 if (res.second)
326 {
327 id hash;
328 key_hash_code(ident, kp.pub, hash);
329 I(hashes.insert(make_pair(hash, ident)).second);
330 return true;
331 }
332 else
333 {
334 E(keys_match(ident, res.first->second.pub, ident, kp.pub),
335 F("Cannot store key '%s': a different key by that name exists.")
336 % ident);
337 L(FL("skipping existing key pair %s") % ident);
338 return false;
339 }
340}
341
342void
343key_store::delete_key(rsa_keypair_id const & ident)
344{
345 s->maybe_read_key_dir();
346 map<rsa_keypair_id, keypair>::iterator i = s->keys.find(ident);
347 if (i != s->keys.end())
348 {
349 id hash;
350 key_hash_code(ident, i->second.pub, hash);
351 map<id, rsa_keypair_id>::iterator j = s->hashes.find(hash);
352 I(j != s->hashes.end());
353 s->hashes.erase(j);
354 s->keys.erase(i);
355 s->signer_cache.erase(ident);
356 s->privkey_cache.erase(ident);
357 }
358 system_path file;
359 s->get_key_file(ident, file);
360 delete_file(file);
361}
362
363//
364// Crypto operations
365//
366
367shared_ptr<RSA_PrivateKey>
368key_store_state::decrypt_private_key(rsa_keypair_id const & id,
369 bool force_from_user)
370{
371 // See if we have this key in the decrypted key cache.
372 map<rsa_keypair_id, shared_ptr<RSA_PrivateKey> >::const_iterator
373 cpk = privkey_cache.find(id);
374 if (cpk != privkey_cache.end())
375 return cpk->second;
376
377 keypair kp;
378 N(maybe_get_key_pair(id, kp),
379 F("no key pair '%s' found in key store '%s'") % id % key_dir);
380
381 L(FL("%d-byte private key") % kp.priv().size());
382
383 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
384 try // with empty passphrase
385 {
386 Botan::DataSource_Memory ds(kp.priv());
387 pkcs8_key.reset(Botan::PKCS8::load_key(ds, ""));
388 }
389 catch (Botan::Exception & e)
390 {
391 L(FL("failed to load key with no passphrase: %s") % e.what());
392
393 utf8 phrase;
394 string lua_phrase;
395 // See whether a lua hook will tell us the passphrase.
396 if (!force_from_user && lua.hook_get_passphrase(id, lua_phrase))
397 phrase = utf8(lua_phrase);
398 else
399 get_passphrase(phrase, id, false, false);
400
401 int cycles = 1;
402 for (;;)
403 try
404 {
405 Botan::DataSource_Memory ds(kp.priv());
406 pkcs8_key.reset(Botan::PKCS8::load_key(ds, phrase()));
407 break;
408 }
409 catch (Botan::Exception & e)
410 {
411 L(FL("decrypt_private_key: failure %d to load encrypted key: %s")
412 % cycles % e.what());
413 E(cycles <= 3,
414 F("failed to decrypt old private RSA key, "
415 "probably incorrect passphrase"));
416
417 get_passphrase(phrase, id, false, false);
418 cycles++;
419 continue;
420 }
421 }
422
423 I(pkcs8_key);
424
425 shared_ptr<RSA_PrivateKey> priv_key;
426 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
427 E(priv_key,
428 F("failed to extract RSA private key from PKCS#8 keypair"));
429
430 // Cache the decrypted key if we're allowed.
431 if (lua.hook_persist_phrase_ok())
432 safe_insert(privkey_cache, make_pair(id, priv_key));
433
434 return priv_key;
435}
436
437void
438key_store::cache_decrypted_key(const rsa_keypair_id & id)
439{
440 signing_key = id;
441 keypair key;
442 get_key_pair(id, key);
443 if (s->get_agent().has_key(key))
444 {
445 L(FL("ssh-agent has key '%s' loaded, skipping internal cache") % id);
446 return;
447 }
448
449 if (s->lua.hook_persist_phrase_ok())
450 s->decrypt_private_key(id);
451}
452
453void
454key_store::create_key_pair(database & db,
455 rsa_keypair_id const & id,
456 utf8 const * maybe_passphrase,
457 id * maybe_pubhash,
458 id * maybe_privhash)
459{
460 conditional_transaction_guard guard(db);
461
462 bool exists = key_pair_exists(id);
463 if (db.database_specified())
464 {
465 guard.acquire();
466 exists = exists || db.public_key_exists(id);
467 }
468 N(!exists, F("key '%s' already exists") % id);
469
470 utf8 prompted_passphrase;
471 if (!maybe_passphrase)
472 {
473 get_passphrase(prompted_passphrase, id, true, true);
474 maybe_passphrase = &prompted_passphrase;
475 }
476
477 // okay, now we can create the key
478 P(F("generating key-pair '%s'") % id);
479 RSA_PrivateKey priv(constants::keylen);
480
481 // serialize and maybe encrypt the private key
482 keypair kp;
483 SecureVector<Botan::byte> pubkey, privkey;
484
485 unfiltered_pipe->start_msg();
486 if ((*maybe_passphrase)().length())
487 Botan::PKCS8::encrypt_key(priv, *unfiltered_pipe,
488 (*maybe_passphrase)(),
489 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
490 Botan::RAW_BER);
491 else
492 Botan::PKCS8::encode(priv, *unfiltered_pipe);
493 unfiltered_pipe->end_msg();
494 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE));
495
496 // serialize the public key
497 unfiltered_pipe->start_msg();
498 Botan::X509::encode(priv, *unfiltered_pipe, Botan::RAW_BER);
499 unfiltered_pipe->end_msg();
500 kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE));
501
502 // convert to storage format
503 L(FL("generated %d-byte public key\n"
504 "generated %d-byte (encrypted) private key\n")
505 % kp.pub().size()
506 % kp.priv().size());
507
508 // and save it.
509 P(F("storing key-pair '%s' in %s/") % id % get_key_dir());
510 put_key_pair(id, kp);
511
512 if (db.database_specified())
513 {
514 P(F("storing public key '%s' in %s") % id % db.get_filename());
515 db.put_key(id, kp.pub);
516 guard.commit();
517 }
518
519 if (maybe_pubhash)
520 key_hash_code(id, kp.pub, *maybe_pubhash);
521 if (maybe_privhash)
522 key_hash_code(id, kp.priv, *maybe_privhash);
523}
524
525void
526key_store::change_key_passphrase(rsa_keypair_id const & id)
527{
528 keypair kp;
529 load_key_pair(*this, id, kp);
530 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id, true);
531
532 utf8 new_phrase;
533 get_passphrase(new_phrase, id, true, false);
534
535 unfiltered_pipe->start_msg();
536 Botan::PKCS8::encrypt_key(*priv, *unfiltered_pipe, new_phrase(),
537 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
538 Botan::RAW_BER);
539 unfiltered_pipe->end_msg();
540 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE));
541
542 delete_key(id);
543 put_key_pair(id, kp);
544}
545
546void
547key_store::decrypt_rsa(rsa_keypair_id const & id,
548 rsa_oaep_sha_data const & ciphertext,
549 string & plaintext)
550{
551 keypair kp;
552 load_key_pair(*this, id, kp);
553 shared_ptr<RSA_PrivateKey> priv_key = s->decrypt_private_key(id);
554
555 shared_ptr<PK_Decryptor>
556 decryptor(get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
557
558 SecureVector<Botan::byte> plain = decryptor->decrypt(
559 reinterpret_cast<Botan::byte const *>(ciphertext().data()),
560 ciphertext().size());
561 plaintext = string(reinterpret_cast<char const*>(plain.begin()),
562 plain.size());
563}
564
565void
566key_store::make_signature(database & db,
567 rsa_keypair_id const & id,
568 string const & tosign,
569 rsa_sha1_signature & signature)
570{
571 keypair key;
572 get_key_pair(id, key);
573
574 // If the database doesn't have this public key, add it now.
575 if (!db.public_key_exists(id))
576 db.put_key(id, key.pub);
577
578 string sig_string;
579 ssh_agent & agent = s->get_agent();
580
581 //sign with ssh-agent (if connected)
582 N(agent.connected() || s->ssh_sign_mode != "only",
583 F("You have chosen to sign only with ssh-agent but ssh-agent"
584 " does not seem to be running."));
585 if (s->ssh_sign_mode == "yes"
586 || s->ssh_sign_mode == "check"
587 || s->ssh_sign_mode == "only")
588 {
589 if (agent.connected()) {
590 //grab the monotone public key as an RSA_PublicKey
591 SecureVector<Botan::byte> pub_block;
592 pub_block.set(reinterpret_cast<Botan::byte const *>(key.pub().data()),
593 key.pub().size());
594 L(FL("make_signature: building %d-byte pub key") % pub_block.size());
595 shared_ptr<X509_PublicKey> x509_key =
596 shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
597 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
598
599 if (!pub_key)
600 throw informative_failure("Failed to get monotone RSA public key");
601
602 agent.sign_data(*pub_key, tosign, sig_string);
603 }
604 if (sig_string.length() <= 0)
605 L(FL("make_signature: monotone and ssh-agent keys do not match, will"
606 " use monotone signing"));
607 }
608
609 string ssh_sig = sig_string;
610
611 N(ssh_sig.length() > 0 || s->ssh_sign_mode != "only",
612 F("You don't seem to have your monotone key imported "));
613
614 if (ssh_sig.length() <= 0
615 || s->ssh_sign_mode == "check"
616 || s->ssh_sign_mode == "no")
617 {
618 SecureVector<Botan::byte> sig;
619
620 // we permit the user to relax security here, by caching a decrypted key
621 // (if they permit it) through the life of a program run. this helps when
622 // you're making a half-dozen certs during a commit or merge or
623 // something.
624
625 bool persist_phrase = (!s->signer_cache.empty()
626 || s->lua.hook_persist_phrase_ok());
627
628 shared_ptr<PK_Signer> signer;
629 shared_ptr<RSA_PrivateKey> priv_key;
630 if (persist_phrase && s->signer_cache.find(id) != s->signer_cache.end())
631 signer = s->signer_cache[id];
632
633 else
634 {
635 priv_key = s->decrypt_private_key(id);
636 if (agent.connected()
637 && s->ssh_sign_mode != "only"
638 && s->ssh_sign_mode != "no") {
639 L(FL("make_signature: adding private key (%s) to ssh-agent") % id());
640 agent.add_identity(*priv_key, id());
641 }
642 signer = shared_ptr<PK_Signer>(get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
643
644 /* If persist_phrase is true, the RSA_PrivateKey object is
645 cached in s->active_keys and will survive as long as the
646 PK_Signer object does. */
647 if (persist_phrase)
648 s->signer_cache.insert(make_pair(id, signer));
649 }
650
651 sig = signer->sign_message(reinterpret_cast<Botan::byte const *>(tosign.data()), tosign.size());
652 sig_string = string(reinterpret_cast<char const*>(sig.begin()), sig.size());
653 }
654
655 if (s->ssh_sign_mode == "check" && ssh_sig.length() > 0)
656 {
657 E(ssh_sig == sig_string,
658 F("make_signature: ssh signature (%i) != monotone signature (%i)\n"
659 "ssh signature : %s\n"
660 "monotone signature: %s")
661 % ssh_sig.length()
662 % sig_string.length()
663 % encode_hexenc(ssh_sig)
664 % encode_hexenc(sig_string));
665 L(FL("make_signature: signatures from ssh-agent and monotone"
666 " are the same"));
667 }
668
669 L(FL("make_signature: produced %d-byte signature") % sig_string.size());
670 signature = rsa_sha1_signature(sig_string);
671
672 cert_status s = db.check_signature(id, tosign, signature);
673 I(s != cert_unknown);
674 E(s == cert_ok, F("make_signature: signature is not valid"));
675}
676
677//
678// Interoperation with ssh-agent (see also above)
679//
680
681void
682key_store::add_key_to_agent(rsa_keypair_id const & id)
683{
684 ssh_agent & agent = s->get_agent();
685 N(agent.connected(),
686 F("no ssh-agent is available, cannot add key '%s'") % id);
687
688 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id);
689 agent.add_identity(*priv, id());
690}
691
692void
693key_store::export_key_for_agent(rsa_keypair_id const & id,
694 std::ostream & os)
695{
696 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id);
697 utf8 new_phrase;
698 get_passphrase(new_phrase, id, true, false);
699
700 // This pipe cannot sensibly be recycled.
701 Pipe p(new Botan::DataSink_Stream(os));
702 p.start_msg();
703 if (new_phrase().length())
704 Botan::PKCS8::encrypt_key(*priv,
705 p,
706 new_phrase(),
707 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)");
708 else
709 Botan::PKCS8::encode(*priv, p);
710 p.end_msg();
711}
712
713
714//
715// Migration from old databases
716//
717
718void
719key_store_state::migrate_old_key_pair
720 (rsa_keypair_id const & id,
721 old_arc4_rsa_priv_key const & old_priv,
722 rsa_pub_key const & pub)
723{
724 keypair kp;
725 SecureVector<Botan::byte> arc4_key;
726 utf8 phrase;
727 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
728 shared_ptr<RSA_PrivateKey> priv_key;
729
730 // See whether a lua hook will tell us the passphrase.
731 string lua_phrase;
732 if (lua.hook_get_passphrase(id, lua_phrase))
733 phrase = utf8(lua_phrase);
734 else
735 get_passphrase(phrase, id, false, false);
736
737 int cycles = 1;
738 for (;;)
739 try
740 {
741 arc4_key.set(reinterpret_cast<Botan::byte const *>(phrase().data()),
742 phrase().size());
743
744 Pipe arc4_decryptor(get_cipher("ARC4", arc4_key, Botan::DECRYPTION));
745 arc4_decryptor.process_msg(old_priv());
746
747 // This is necessary because PKCS8::load_key() cannot currently
748 // recognize an unencrypted, raw-BER blob as such, but gets it
749 // right if it's PEM-coded.
750 SecureVector<Botan::byte> arc4_decrypt(arc4_decryptor.read_all());
751 Botan::DataSource_Memory ds(Botan::PEM_Code::encode(arc4_decrypt,
752 "PRIVATE KEY"));
753 pkcs8_key.reset(Botan::PKCS8::load_key(ds));
754 break;
755 }
756 catch (Botan::Exception & e)
757 {
758 L(FL("migrate_old_key_pair: failure %d to load old private key: %s")
759 % cycles % e.what());
760
761 E(cycles <= 3,
762 F("failed to decrypt old private RSA key, "
763 "probably incorrect passphrase"));
764
765 get_passphrase(phrase, id, false, false);
766 cycles++;
767 continue;
768 }
769
770 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
771 I(priv_key);
772
773 // now we can write out the new key
774 unfiltered_pipe->start_msg();
775 Botan::PKCS8::encrypt_key(*priv_key, *unfiltered_pipe, phrase(),
776 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
777 Botan::RAW_BER);
778 unfiltered_pipe->end_msg();
779 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE));
780
781 // also the public key (which is derivable from the private key; asking
782 // Botan for the X.509 encoding of the private key implies that we want
783 // it to derive and produce the public key)
784 unfiltered_pipe->start_msg();
785 Botan::X509::encode(*priv_key, *unfiltered_pipe, Botan::RAW_BER);
786 unfiltered_pipe->end_msg();
787 kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE));
788
789 // if the database had a public key entry for this key, make sure it
790 // matches what we derived from the private key entry, but don't abort the
791 // whole migration if it doesn't.
792 if (!pub().empty() && !keys_match(id, pub, id, kp.pub))
793 W(F("public and private keys for %s don't match") % id);
794
795 put_key_pair(id, kp);
796}
797
798void
799key_store::migrate_old_key_pair
800 (rsa_keypair_id const & id,
801 old_arc4_rsa_priv_key const & old_priv,
802 rsa_pub_key const & pub)
803{
804 s->migrate_old_key_pair(id, old_priv, pub);
805}
806
807// Local Variables:
808// mode: C++
809// fill-column: 76
810// c-file-style: "gnu"
811// indent-tabs-mode: nil
812// End:
813// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status