monotone

monotone Mtn Source Tree

Root/src/key_store.cc

1// Copyright (C) 2005 Timothy Brownawell <tbrownaw@gmail.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <sstream>
12
13#include <botan/botan.h>
14#include <botan/rsa.h>
15#include <botan/pem.h>
16#include <botan/look_pk.h>
17
18#include "char_classifiers.hh"
19#include "key_store.hh"
20#include "file_io.hh"
21#include "packet.hh"
22#include "project.hh"
23#include "database.hh"
24#include "keys.hh"
25#include "globish.hh"
26#include "app_state.hh"
27#include "transforms.hh"
28#include "constants.hh"
29#include "ssh_agent.hh"
30#include "safe_map.hh"
31#include "charset.hh"
32#include "ui.hh"
33#include "lazy_rng.hh"
34#include "botan_pipe_cache.hh"
35
36using std::make_pair;
37using std::istringstream;
38using std::map;
39using std::ostringstream;
40using std::pair;
41using std::string;
42using std::vector;
43
44using boost::scoped_ptr;
45using boost::shared_ptr;
46using boost::shared_dynamic_cast;
47
48using Botan::RSA_PrivateKey;
49using Botan::RSA_PublicKey;
50using Botan::SecureVector;
51using Botan::X509_PublicKey;
52using Botan::PKCS8_PrivateKey;
53using Botan::PK_Decryptor;
54using Botan::PK_Signer;
55using Botan::Pipe;
56using Botan::get_pk_decryptor;
57using Botan::get_cipher;
58
59
60typedef pair<key_name, keypair> key_info;
61typedef pair<key_id, key_info> full_key_info;
62typedef map<key_id, key_info> key_map;
63
64struct key_store_state
65{
66 system_path const key_dir;
67 string const ssh_sign_mode;
68 bool non_interactive;
69 bool have_read;
70 lua_hooks & lua;
71 key_map keys;
72
73 // These are used to cache keys and signers (if the hook allows).
74 map<key_id, shared_ptr<RSA_PrivateKey> > privkey_cache;
75 map<key_id, shared_ptr<PK_Signer> > signer_cache;
76
77 // Initialized when first required.
78 scoped_ptr<ssh_agent> agent;
79
80 key_store_state(app_state & app)
81 : key_dir(app.opts.key_dir), ssh_sign_mode(app.opts.ssh_sign),
82 non_interactive(app.opts.non_interactive),
83 have_read(false), lua(app.lua)
84 {
85 E(app.opts.key_dir_given
86 || app.opts.key_dir != system_path(get_default_keydir(), origin::user)
87 || app.opts.conf_dir_given
88 || !app.opts.no_default_confdir,
89 origin::user,
90 F("no available keystore found"));
91 }
92
93 // internal methods
94 void get_key_file(key_id const & ident, key_name const & name,
95 system_path & file);
96 void get_old_key_file(key_name const & name, system_path & file);
97 void write_key(full_key_info const & info);
98 void maybe_read_key_dir();
99 shared_ptr<RSA_PrivateKey> decrypt_private_key(key_id const & id,
100 bool force_from_user = false);
101
102 // just like put_key_pair except that the key is _not_ written to disk.
103 // for internal use in reading keys back from disk.
104 bool put_key_pair_memory(full_key_info const & info);
105
106 // wrapper around accesses to agent, initializes as needed
107 ssh_agent & get_agent()
108 {
109 if (!agent)
110 agent.reset(new ssh_agent);
111 return *agent;
112 }
113
114 // duplicates of key_store interfaces for use by key_store_state methods
115 // and the keyreader.
116 bool maybe_get_key_pair(key_id const & ident,
117 key_name & name,
118 keypair & kp);
119 bool put_key_pair(full_key_info const & info);
120 void migrate_old_key_pair(key_name const & id,
121 old_arc4_rsa_priv_key const & old_priv,
122 rsa_pub_key const & pub);
123};
124
125namespace
126{
127 struct keyreader : public packet_consumer
128 {
129 key_store_state & kss;
130
131 keyreader(key_store_state & kss): kss(kss) {}
132 virtual void consume_file_data(file_id const & ident,
133 file_data const & dat)
134 {E(false, origin::system, F("extraneous data in key store"));}
135 virtual void consume_file_delta(file_id const & id_old,
136 file_id const & id_new,
137 file_delta const & del)
138 {E(false, origin::system, F("extraneous data in key store"));}
139
140 virtual void consume_revision_data(revision_id const & ident,
141 revision_data const & dat)
142 {E(false, origin::system, F("extraneous data in key store"));}
143 virtual void consume_revision_cert(cert const & t)
144 {E(false, origin::system, F("extraneous data in key store"));}
145
146
147 virtual void consume_public_key(key_name const & ident,
148 rsa_pub_key const & k)
149 {E(false, origin::system, F("extraneous data in key store"));}
150
151 virtual void consume_key_pair(key_name const & name,
152 keypair const & kp)
153 {
154 L(FL("reading key pair '%s' from key store") % name);
155
156 key_id ident;
157 key_hash_code(name, kp.pub, ident);
158 E(kss.put_key_pair_memory(full_key_info(ident, key_info(name, kp))),
159 origin::system,
160 F("key store has multiple copies of the key with id %s") % ident);
161
162 L(FL("successfully read key pair %s from key store") % ident);
163 }
164
165 // for backward compatibility
166 virtual void consume_old_private_key(key_name const & ident,
167 old_arc4_rsa_priv_key const & k)
168 {
169 W(F("converting old-format private key %s") % ident);
170
171 rsa_pub_key dummy;
172 kss.migrate_old_key_pair(ident, k, dummy);
173
174 L(FL("successfully read key pair %s from key store") % ident);
175 }
176 };
177}
178
179key_store::key_store(app_state & a)
180 : s(new key_store_state(a))
181{}
182
183key_store::~key_store()
184{}
185
186bool
187key_store::have_signing_key() const
188{
189 return !signing_key.inner()().empty();
190}
191
192system_path const &
193key_store::get_key_dir() const
194{
195 return s->key_dir;
196}
197
198void
199key_store_state::maybe_read_key_dir()
200{
201 if (have_read)
202 return;
203 have_read = true;
204
205 if (!directory_exists(key_dir))
206 {
207 L(FL("key dir '%s' does not exist") % key_dir);
208 return;
209 }
210
211 L(FL("reading key dir '%s'") % key_dir);
212
213 vector<system_path> key_files;
214 fill_path_vec<system_path> fill_key_files(key_dir, key_files, false);
215 dirent_ignore ignore;
216 read_directory(key_dir, fill_key_files, ignore, ignore);
217
218 keyreader kr(*this);
219 for (vector<system_path>::const_iterator i = key_files.begin();
220 i != key_files.end(); ++i)
221 {
222 L(FL("reading keys from file '%s'") % (*i));
223 data dat;
224 read_data(*i, dat);
225 istringstream is(dat());
226 if (read_packets(is, kr) == 0)
227 W(F("ignored invalid key file '%s' in key store") % (*i) );
228 }
229}
230
231void
232key_store::get_key_ids(vector<key_id> & priv)
233{
234 s->maybe_read_key_dir();
235 priv.clear();
236 for (key_map::const_iterator i = s->keys.begin(); i != s->keys.end(); ++i)
237 priv.push_back(i->first);
238}
239
240bool
241key_store::key_pair_exists(key_id const & ident)
242{
243 s->maybe_read_key_dir();
244 return s->keys.find(ident) != s->keys.end();
245}
246
247bool
248key_store::key_pair_exists(key_name const & name)
249{
250 s->maybe_read_key_dir();
251 for (key_map::const_iterator i = s->keys.begin();
252 i != s->keys.end(); ++i)
253 {
254 if (i->second.first == name)
255 return true;
256 }
257 return false;
258}
259
260bool
261key_store_state::maybe_get_key_pair(key_id const & ident,
262 key_name & name,
263 keypair & kp)
264{
265 maybe_read_key_dir();
266 key_map::const_iterator i = keys.find(ident);
267 if (i == keys.end())
268 return false;
269 name = i->second.first;
270 kp = i->second.second;
271 return true;
272}
273
274bool
275key_store::maybe_get_key_pair(key_id const & ident,
276 keypair & kp)
277{
278 key_name name;
279 return s->maybe_get_key_pair(ident, name, kp);
280}
281
282void
283key_store::get_key_pair(key_id const & ident,
284 keypair & kp)
285{
286 MM(ident);
287 bool found = maybe_get_key_pair(ident, kp);
288 I(found);
289}
290
291bool
292key_store::maybe_get_key_pair(key_id const & hash,
293 key_name & keyid,
294 keypair & kp)
295{
296 key_map::const_iterator ki = s->keys.find(hash);
297 if (ki == s->keys.end())
298 return false;
299 keyid = ki->second.first;
300 kp = ki->second.second;
301 return true;
302}
303
304void
305key_store::get_key_pair(key_id const & hash,
306 key_name & keyid,
307 keypair & kp)
308{
309 MM(hash);
310 bool found = maybe_get_key_pair(hash, keyid, kp);
311 I(found);
312}
313
314void
315key_store_state::get_key_file(key_id const & ident,
316 key_name const & name,
317 system_path & file)
318{
319 hexenc<id> encoded;
320 encode_hexenc(ident.inner(), encoded);
321
322 static const string allowed_special_chars("@%^_-+=.,;~[]");
323 string basename;
324 for (string::const_iterator iter = name().begin();
325 iter != name().end(); ++iter)
326 {
327 if (is_alnum(*iter) ||
328 is_space(*iter) ||
329 allowed_special_chars.find(*iter) != string::npos)
330 {
331 basename += *iter;
332 }
333 else
334 {
335 basename += '?';
336 }
337 }
338
339 file = key_dir / path_component(basename + "." + encoded(),
340 origin::internal);
341}
342
343void
344key_store_state::get_old_key_file(key_name const & name,
345 system_path & file)
346{
347 // filename is the keypair id, except that some characters can't be put in
348 // filenames (especially on windows).
349 string leaf = name();
350 for (unsigned int i = 0; i < leaf.size(); ++i)
351 if (leaf.at(i) == '+')
352 leaf.at(i) = '_';
353
354 file = key_dir / path_component(leaf, origin::internal);
355
356}
357
358void
359key_store_state::write_key(full_key_info const & info)
360{
361 ostringstream oss;
362 packet_writer pw(oss);
363 pw.consume_key_pair(info.second.first, info.second.second);
364 data dat(oss.str(), info.second.first.made_from);
365
366 system_path file;
367 get_key_file(info.first, info.second.first, file);
368
369 // Make sure the private key is not readable by anyone other than the user.
370 L(FL("writing key '%s' to file '%s' in dir '%s'")
371 % info.first % file % key_dir);
372 write_data_userprivate(file, dat, key_dir);
373
374 system_path old_file;
375 get_old_key_file(info.second.first, old_file);
376 if (file_exists(old_file))
377 delete_file(old_file);
378}
379
380bool
381key_store_state::put_key_pair(full_key_info const & info)
382{
383 maybe_read_key_dir();
384 bool newkey = put_key_pair_memory(info);
385 if (newkey)
386 write_key(info);
387 return newkey;
388}
389
390bool
391key_store::put_key_pair(key_name const & name,
392 keypair const & kp)
393{
394 key_id ident;
395 key_hash_code(name, kp.pub, ident);
396 return s->put_key_pair(full_key_info(ident, key_info(name, kp)));
397}
398
399bool
400key_store_state::put_key_pair_memory(full_key_info const & info)
401{
402 L(FL("putting key pair '%s'") % info.first);
403 pair<key_map::iterator, bool> res;
404 res = keys.insert(info);
405 if (!res.second)
406 {
407 L(FL("skipping existing key pair %s") % info.first);
408 return false;
409 }
410 return true;
411}
412
413struct key_delete_validator : public packet_consumer
414{
415 key_id expected_ident;
416 system_path file;
417 key_delete_validator(key_id const & id, system_path const & f)
418 : expected_ident(id), file(f) {}
419 virtual ~key_delete_validator() {}
420 virtual void consume_file_data(file_id const & ident,
421 file_data const & dat)
422 { E(false, origin::system, F("invalid data in key file")); }
423 virtual void consume_file_delta(file_id const & id_old,
424 file_id const & id_new,
425 file_delta const & del)
426 { E(false, origin::system, F("invalid data in key file")); }
427 virtual void consume_revision_data(revision_id const & ident,
428 revision_data const & dat)
429 { E(false, origin::system, F("invalid data in key file")); }
430 virtual void consume_revision_cert(cert const & t)
431 { E(false, origin::system, F("invalid data in key file")); }
432 virtual void consume_public_key(key_name const & ident,
433 rsa_pub_key const & k)
434 { E(false, origin::system, F("invalid data in key file")); }
435 virtual void consume_key_pair(key_name const & name,
436 keypair const & kp)
437 {
438 L(FL("reading key pair '%s' from key store for validation") % name);
439 key_id ident;
440 key_hash_code(name, kp.pub, ident);
441 E(ident == expected_ident, origin::user,
442 F("expected key with id %s in key file '%s', got key with id %s")
443 % expected_ident % file % ident);
444 }
445 virtual void consume_old_private_key(key_name const & ident,
446 old_arc4_rsa_priv_key const & k)
447 { L(FL("skipping id check before deleting old private key in '%s'") % file); }
448};
449
450void
451key_store::delete_key(key_id const & ident)
452{
453 s->maybe_read_key_dir();
454 key_map::iterator i = s->keys.find(ident);
455 if (i != s->keys.end())
456 {
457 system_path file;
458 s->get_key_file(ident, i->second.first, file);
459 if (!file_exists(file))
460 s->get_old_key_file(i->second.first, file);
461
462 // sanity: if we read the key originally from a file which did not
463 // follow the NAME.IDENT scheme and have another key pair with NAME
464 // in the key dir, we could accidentially drop the wrong private key
465 // here, so validate if the file really contains the key with the
466 // ID we want to delete, before going mad
467 {
468 key_delete_validator val(ident, file);
469 data dat;
470 read_data(file, dat);
471 istringstream is(dat());
472 I(read_packets(is, val));
473 }
474
475 delete_file(file);
476
477 s->keys.erase(i);
478 s->signer_cache.erase(ident);
479 s->privkey_cache.erase(ident);
480 }
481}
482
483//
484// Crypto operations
485//
486
487// "raw" passphrase prompter; unaware of passphrase caching or the laziness
488// hook. KEYID is used only in prompts. CONFIRM_PHRASE causes the user to
489// be prompted to type the same thing twice, and will loop if they don't
490// match. Prompts are worded slightly differently if GENERATING_KEY is true.
491static void
492get_passphrase(utf8 & phrase,
493 key_name const & keyname,
494 key_id const & keyid,
495 bool confirm_phrase,
496 bool generating_key)
497{
498 string prompt1, prompt2;
499 char pass1[constants::maxpasswd];
500 char pass2[constants::maxpasswd];
501 int i = 0;
502
503 hexenc<id> hexid;
504 encode_hexenc(keyid.inner(), hexid);
505 string const short_id = hexid().substr(0, 8) + "...";
506
507 if (confirm_phrase && !generating_key)
508 prompt1 = (F("enter new passphrase for key ID [%s] (%s): ")
509 % keyname % short_id).str();
510 else
511 prompt1 = (F("enter passphrase for key ID [%s] (%s): ")
512 % keyname % short_id).str();
513
514 if (confirm_phrase)
515 prompt2 = (F("confirm passphrase for key ID [%s] (%s): ")
516 % keyname % short_id).str();
517
518 try
519 {
520 for (;;)
521 {
522 memset(pass1, 0, constants::maxpasswd);
523 memset(pass2, 0, constants::maxpasswd);
524 ui.ensure_clean_line();
525
526 read_password(prompt1, pass1, constants::maxpasswd);
527 if (!confirm_phrase)
528 break;
529
530 ui.ensure_clean_line();
531 read_password(prompt2, pass2, constants::maxpasswd);
532 if (strcmp(pass1, pass2) == 0)
533 break;
534
535 E(i++ < 2, origin::user, F("too many failed passphrases"));
536 P(F("passphrases do not match, try again"));
537 }
538
539 external ext_phrase(pass1);
540 system_to_utf8(ext_phrase, phrase);
541 }
542 catch (...)
543 {
544 memset(pass1, 0, constants::maxpasswd);
545 memset(pass2, 0, constants::maxpasswd);
546 throw;
547 }
548 memset(pass1, 0, constants::maxpasswd);
549 memset(pass2, 0, constants::maxpasswd);
550}
551
552
553
554shared_ptr<RSA_PrivateKey>
555key_store_state::decrypt_private_key(key_id const & id,
556 bool force_from_user)
557{
558 // See if we have this key in the decrypted key cache.
559 map<key_id, shared_ptr<RSA_PrivateKey> >::const_iterator
560 cpk = privkey_cache.find(id);
561 if (cpk != privkey_cache.end())
562 return cpk->second;
563
564 keypair kp;
565 key_name name;
566 E(maybe_get_key_pair(id, name, kp), origin::user,
567 F("no key pair %s found in key store '%s'") % id % key_dir);
568
569 L(FL("%d-byte private key") % kp.priv().size());
570
571 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
572 try // with empty passphrase
573 {
574 Botan::DataSource_Memory ds(kp.priv());
575#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
576 pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), Dummy_UI()));
577#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
578 pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), ""));
579#else
580 pkcs8_key.reset(Botan::PKCS8::load_key(ds, ""));
581#endif
582 }
583#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
584 catch (Passphrase_Required & e)
585#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
586 catch (Botan::Invalid_Argument & e)
587#else
588 catch (Botan::Exception & e)
589#endif
590 {
591 L(FL("failed to load key with no passphrase: %s") % e.what());
592
593 utf8 phrase;
594 string lua_phrase;
595 key_identity_info identity;
596 identity.id = id;
597 identity.given_name = name;
598
599 // See whether a lua hook will tell us the passphrase.
600 if ((!force_from_user || non_interactive) &&
601 lua.hook_get_passphrase(identity, lua_phrase))
602 {
603 phrase = utf8(lua_phrase, origin::user);
604 }
605 else if (!non_interactive)
606 {
607 get_passphrase(phrase, name, id, false, false);
608 }
609
610 int cycles = 0;
611 for (;;)
612 try
613 {
614 Botan::DataSource_Memory ds(kp.priv());
615#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
616 pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(),
617 phrase()));
618#else
619 pkcs8_key.reset(Botan::PKCS8::load_key(ds, phrase()));
620#endif
621 break;
622 }
623#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
624 catch (Botan::Invalid_Argument)
625#else
626 catch (Botan::Exception & e)
627#endif
628 {
629 cycles++;
630 L(FL("decrypt_private_key: failure %d to load encrypted key: %s")
631 % cycles % e.what());
632 E(cycles < 3 && !non_interactive, origin::no_fault,
633 F("failed to decrypt old private RSA key, probably incorrect "
634 "passphrase or missing 'get_passphrase' lua hook"));
635
636 get_passphrase(phrase, name, id, false, false);
637 continue;
638 }
639 }
640
641 I(pkcs8_key);
642
643 shared_ptr<RSA_PrivateKey> priv_key;
644 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
645 E(priv_key, origin::no_fault,
646 F("failed to extract RSA private key from PKCS#8 keypair"));
647
648 // Cache the decrypted key if we're allowed.
649 if (lua.hook_persist_phrase_ok())
650 safe_insert(privkey_cache, make_pair(id, priv_key));
651
652 return priv_key;
653}
654
655void
656key_store::cache_decrypted_key(const key_id & id)
657{
658 signing_key = id;
659 keypair key;
660 get_key_pair(id, key);
661 if (s->get_agent().has_key(key))
662 {
663 L(FL("ssh-agent has key '%s' loaded, skipping internal cache") % id);
664 return;
665 }
666
667 if (s->lua.hook_persist_phrase_ok())
668 s->decrypt_private_key(id);
669}
670
671void
672key_store::create_key_pair(database & db,
673 key_name const & ident,
674 create_key_pair_mode create_mode,
675 utf8 const * maybe_passphrase,
676 key_id * const maybe_hash)
677{
678 conditional_transaction_guard guard(db);
679
680 bool exists = false;
681 for (key_map::iterator i = s->keys.begin(); i != s->keys.end(); ++i)
682 {
683 if (i->second.first == ident)
684 exists = true;
685 }
686 E(!exists, origin::user, F("key '%s' already exists") % ident);
687
688 utf8 prompted_passphrase;
689 if (!maybe_passphrase)
690 {
691 get_passphrase(prompted_passphrase, ident, key_id(), true, true);
692 maybe_passphrase = &prompted_passphrase;
693 }
694
695 // okay, now we can create the key
696 if (create_mode == create_verbose)
697 {
698 P(F("generating key-pair '%s'") % ident);
699 }
700 else
701 {
702 L(FL("generating key-pair '%s'") % ident);
703 }
704#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
705 RSA_PrivateKey priv(lazy_rng::get(),
706 static_cast<Botan::u32bit>(constants::keylen));
707#else
708 RSA_PrivateKey priv(static_cast<Botan::u32bit>(constants::keylen));
709#endif
710
711 // serialize and maybe encrypt the private key
712 keypair kp;
713 SecureVector<Botan::byte> pubkey, privkey;
714
715 unfiltered_pipe->start_msg();
716 if ((*maybe_passphrase)().length())
717 Botan::PKCS8::encrypt_key(priv, *unfiltered_pipe,
718#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
719 lazy_rng::get(),
720#endif
721 (*maybe_passphrase)(),
722 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
723 Botan::RAW_BER);
724 else
725 Botan::PKCS8::encode(priv, *unfiltered_pipe);
726 unfiltered_pipe->end_msg();
727 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
728 origin::internal);
729
730 // serialize the public key
731 unfiltered_pipe->start_msg();
732 Botan::X509::encode(priv, *unfiltered_pipe, Botan::RAW_BER);
733 unfiltered_pipe->end_msg();
734 kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
735 origin::internal);
736
737 // convert to storage format
738 L(FL("generated %d-byte public key\n"
739 "generated %d-byte (encrypted) private key\n")
740 % kp.pub().size()
741 % kp.priv().size());
742
743 // and save it.
744 if (create_mode == create_verbose)
745 {
746 P(F("storing key-pair %s in '%s/'") % ident % get_key_dir());
747 }
748 else
749 {
750 L(FL("storing key-pair %s in '%s/'") % ident % get_key_dir());
751 }
752 put_key_pair(ident, kp);
753
754 if (db.database_specified())
755 {
756 guard.acquire();
757 if (create_mode == create_verbose)
758 {
759 P(F("storing public key %s in '%s'") % ident % db.get_filename());
760 }
761 else
762 {
763 L(FL("storing public key %s in '%s'") % ident % db.get_filename());
764 }
765 db.put_key(ident, kp.pub);
766 guard.commit();
767 }
768
769 key_id hash;
770 key_hash_code(ident, kp.pub, hash);
771 if (maybe_hash)
772 *maybe_hash = hash;
773 if (create_mode == create_verbose)
774 {
775 P(F("key '%s' has hash '%s'") % ident % hash);
776 }
777}
778
779void
780key_store::change_key_passphrase(key_id const & id)
781{
782 key_name name;
783 keypair kp;
784 {
785 bool found = false;
786 s->maybe_read_key_dir();
787 key_map::const_iterator i = s->keys.find(id);
788 E(i != s->keys.end(), origin::user,
789 F("no key pair '%s' found in key store '%s'") % id % s->key_dir);
790 name = i->second.first;
791 kp = i->second.second;
792 }
793 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id, true);
794
795 utf8 new_phrase;
796 get_passphrase(new_phrase, name, id, true, false);
797
798 unfiltered_pipe->start_msg();
799 if (new_phrase().length())
800 Botan::PKCS8::encrypt_key(*priv, *unfiltered_pipe,
801#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
802 lazy_rng::get(),
803#endif
804 new_phrase(),
805 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
806 Botan::RAW_BER);
807 else
808 Botan::PKCS8::encode(*priv, *unfiltered_pipe);
809
810 unfiltered_pipe->end_msg();
811 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
812 origin::internal);
813
814 delete_key(id);
815 put_key_pair(name, kp);
816}
817
818void
819key_store::decrypt_rsa(key_id const & id,
820 rsa_oaep_sha_data const & ciphertext,
821 string & plaintext)
822{
823 try
824 {
825 keypair kp;
826 load_key_pair(*this, id, kp);
827 shared_ptr<RSA_PrivateKey> priv_key = s->decrypt_private_key(id);
828
829 shared_ptr<PK_Decryptor>
830 decryptor(get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
831
832 SecureVector<Botan::byte> plain =
833 decryptor->decrypt(reinterpret_cast<Botan::byte const *>(ciphertext().data()),
834 ciphertext().size());
835 plaintext = string(reinterpret_cast<char const*>(plain.begin()),
836 plain.size());
837 }
838#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
839 catch (std::exception & e)
840#else
841 catch (Botan::Exception & e)
842#endif
843 {
844 E(false, ciphertext.made_from,
845 F("Botan error decrypting data: '%s'") % e.what());
846 }
847}
848
849void
850key_store::make_signature(database & db,
851 key_id const & id,
852 string const & tosign,
853 rsa_sha1_signature & signature)
854{
855 key_name name;
856 keypair key;
857 get_key_pair(id, name, key);
858
859 // If the database doesn't have this public key, add it now.
860 if (!db.public_key_exists(id))
861 db.put_key(name, key.pub);
862
863 string sig_string;
864 ssh_agent & agent = s->get_agent();
865
866 //sign with ssh-agent (if connected)
867 E(agent.connected() || s->ssh_sign_mode != "only", origin::user,
868 F("you have chosen to sign only with ssh-agent but ssh-agent"
869 " does not seem to be running"));
870 if (s->ssh_sign_mode == "yes"
871 || s->ssh_sign_mode == "check"
872 || s->ssh_sign_mode == "only")
873 {
874 if (agent.connected()) {
875 //grab the monotone public key as an RSA_PublicKey
876 SecureVector<Botan::byte> pub_block
877 (reinterpret_cast<Botan::byte const *>(key.pub().data()),
878 key.pub().size());
879 L(FL("make_signature: building %d-byte pub key") % pub_block.size());
880 shared_ptr<X509_PublicKey> x509_key =
881 shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
882 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
883
884 if (!pub_key)
885 throw recoverable_failure(origin::system,
886 "Failed to get monotone RSA public key");
887
888 agent.sign_data(*pub_key, tosign, sig_string);
889 }
890 if (sig_string.length() <= 0)
891 L(FL("make_signature: monotone and ssh-agent keys do not match, will"
892 " use monotone signing"));
893 }
894
895 string ssh_sig = sig_string;
896
897 E(ssh_sig.length() > 0 || s->ssh_sign_mode != "only", origin::user,
898 F("you don't seem to have your monotone key imported "));
899
900 if (ssh_sig.length() <= 0
901 || s->ssh_sign_mode == "check"
902 || s->ssh_sign_mode == "no")
903 {
904 SecureVector<Botan::byte> sig;
905
906 // we permit the user to relax security here, by caching a decrypted key
907 // (if they permit it) through the life of a program run. this helps when
908 // you're making a half-dozen certs during a commit or merge or
909 // something.
910
911 bool persist_phrase = (!s->signer_cache.empty()
912 || s->lua.hook_persist_phrase_ok());
913
914 shared_ptr<PK_Signer> signer;
915 shared_ptr<RSA_PrivateKey> priv_key;
916 if (persist_phrase && s->signer_cache.find(id) != s->signer_cache.end())
917 signer = s->signer_cache[id];
918
919 else
920 {
921 priv_key = s->decrypt_private_key(id);
922 if (agent.connected()
923 && s->ssh_sign_mode != "only"
924 && s->ssh_sign_mode != "no") {
925 L(FL("make_signature: adding private key (%s) to ssh-agent") % id);
926 agent.add_identity(*priv_key, name());
927 }
928 signer = shared_ptr<PK_Signer>(get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
929
930 /* If persist_phrase is true, the RSA_PrivateKey object is
931 cached in s->active_keys and will survive as long as the
932 PK_Signer object does. */
933 if (persist_phrase)
934 s->signer_cache.insert(make_pair(id, signer));
935 }
936
937#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
938 sig = signer->sign_message(
939 reinterpret_cast<Botan::byte const *>(tosign.data()),
940 tosign.size(), lazy_rng::get());
941#else
942 sig = signer->sign_message(
943 reinterpret_cast<Botan::byte const *>(tosign.data()),
944 tosign.size());
945#endif
946 sig_string = string(reinterpret_cast<char const*>(sig.begin()), sig.size());
947 }
948
949 if (s->ssh_sign_mode == "check" && ssh_sig.length() > 0)
950 {
951 E(ssh_sig == sig_string, origin::system,
952 F("make_signature: ssh signature (%i) != monotone signature (%i)\n"
953 "ssh signature : %s\n"
954 "monotone signature: %s")
955 % ssh_sig.length()
956 % sig_string.length()
957 % ssh_sig
958 % sig_string);
959 L(FL("make_signature: signatures from ssh-agent and monotone"
960 " are the same"));
961 }
962
963 L(FL("make_signature: produced %d-byte signature") % sig_string.size());
964 signature = rsa_sha1_signature(sig_string, origin::internal);
965
966 cert_status s = db.check_signature(id, tosign, signature);
967 I(s != cert_unknown);
968 E(s == cert_ok, origin::system, F("make_signature: signature is not valid"));
969}
970
971//
972// Interoperation with ssh-agent (see also above)
973//
974
975void
976key_store::add_key_to_agent(key_id const & id)
977{
978 ssh_agent & agent = s->get_agent();
979 E(agent.connected(), origin::user,
980 F("no ssh-agent is available, cannot add key %s") % id);
981
982 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id);
983
984 key_name name;
985 keypair kp;
986 s->maybe_get_key_pair(id, name, kp);
987 agent.add_identity(*priv, name());
988}
989
990void
991key_store::export_key_for_agent(key_id const & id,
992 std::ostream & os)
993{
994 shared_ptr<RSA_PrivateKey> priv = s->decrypt_private_key(id);
995
996 key_name name;
997 {
998 keypair kp;
999 I(s->maybe_get_key_pair(id, name, kp));
1000 }
1001
1002 utf8 new_phrase;
1003 get_passphrase(new_phrase, name, id, true, false);
1004
1005 // This pipe cannot sensibly be recycled.
1006 Pipe p(new Botan::DataSink_Stream(os));
1007 p.start_msg();
1008 if (new_phrase().length())
1009 Botan::PKCS8::encrypt_key(*priv,
1010 p,
1011#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
1012 lazy_rng::get(),
1013#endif
1014 new_phrase(),
1015 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)");
1016 else
1017 Botan::PKCS8::encode(*priv, p);
1018 p.end_msg();
1019}
1020
1021
1022//
1023// Migration from old databases
1024//
1025
1026void
1027key_store_state::migrate_old_key_pair
1028 (key_name const & id,
1029 old_arc4_rsa_priv_key const & old_priv,
1030 rsa_pub_key const & pub)
1031{
1032 keypair kp;
1033 SecureVector<Botan::byte> arc4_key;
1034 utf8 phrase;
1035 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
1036 shared_ptr<RSA_PrivateKey> priv_key;
1037
1038 // See whether a lua hook will tell us the passphrase.
1039 key_identity_info identity;
1040 identity.given_name = id;
1041 string lua_phrase;
1042 if (lua.hook_get_passphrase(identity, lua_phrase))
1043 phrase = utf8(lua_phrase, origin::user);
1044 else
1045 get_passphrase(phrase, id, key_id(), false, false);
1046
1047 int cycles = 1;
1048 for (;;)
1049 try
1050 {
1051#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
1052 arc4_key.resize(phrase().size());
1053 arc4_key.copy(reinterpret_cast<Botan::byte const *>(phrase().data()),
1054 phrase().size());
1055#else
1056 arc4_key.set(reinterpret_cast<Botan::byte const *>(phrase().data()),
1057 phrase().size());
1058#endif
1059
1060 Pipe arc4_decryptor(get_cipher("ARC4", arc4_key, Botan::DECRYPTION));
1061
1062 arc4_decryptor.process_msg(old_priv());
1063
1064 // This is necessary because PKCS8::load_key() cannot currently
1065 // recognize an unencrypted, raw-BER blob as such, but gets it
1066 // right if it's PEM-coded.
1067 SecureVector<Botan::byte> arc4_decrypt(arc4_decryptor.read_all());
1068 Botan::DataSource_Memory ds(Botan::PEM_Code::encode(arc4_decrypt,
1069 "PRIVATE KEY"));
1070#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
1071 pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get()));
1072#else
1073 pkcs8_key.reset(Botan::PKCS8::load_key(ds));
1074#endif
1075 break;
1076 }
1077#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
1078 catch (Botan::Invalid_Argument & e)
1079#else
1080 catch (Botan::Exception & e)
1081#endif
1082 {
1083 L(FL("migrate_old_key_pair: failure %d to load old private key: %s")
1084 % cycles % e.what());
1085
1086 E(cycles <= 3, origin::no_fault,
1087 F("failed to decrypt old private RSA key, "
1088 "probably incorrect passphrase"));
1089
1090 get_passphrase(phrase, id, key_id(), false, false);
1091 cycles++;
1092 continue;
1093 }
1094
1095 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
1096 I(priv_key);
1097
1098 // now we can write out the new key
1099 unfiltered_pipe->start_msg();
1100 Botan::PKCS8::encrypt_key(*priv_key, *unfiltered_pipe,
1101#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
1102 lazy_rng::get(),
1103#endif
1104 phrase(),
1105 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
1106 Botan::RAW_BER);
1107 unfiltered_pipe->end_msg();
1108 kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
1109 origin::internal);
1110
1111 // also the public key (which is derivable from the private key; asking
1112 // Botan for the X.509 encoding of the private key implies that we want
1113 // it to derive and produce the public key)
1114 unfiltered_pipe->start_msg();
1115 Botan::X509::encode(*priv_key, *unfiltered_pipe, Botan::RAW_BER);
1116 unfiltered_pipe->end_msg();
1117 kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
1118 origin::internal);
1119
1120 // if the database had a public key entry for this key, make sure it
1121 // matches what we derived from the private key entry, but don't abort the
1122 // whole migration if it doesn't.
1123 if (!pub().empty() && !keys_match(id, pub, id, kp.pub))
1124 W(F("public and private keys for %s do not match") % id);
1125
1126 key_id hash;
1127 key_hash_code(id, kp.pub, hash);
1128 put_key_pair(full_key_info(hash, key_info(id, kp)));
1129}
1130
1131void
1132key_store::migrate_old_key_pair
1133 (key_name const & id,
1134 old_arc4_rsa_priv_key const & old_priv,
1135 rsa_pub_key const & pub)
1136{
1137 s->migrate_old_key_pair(id, old_priv, pub);
1138}
1139
1140// Local Variables:
1141// mode: C++
1142// fill-column: 76
1143// c-file-style: "gnu"
1144// indent-tabs-mode: nil
1145// End:
1146// 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