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