monotone

monotone Mtn Source Tree

Root/keys.cc

1#include <iostream>
2#include <string>
3#include <map>
4
5#include <unistd.h>
6#include <string.h>
7
8#include <boost/shared_ptr.hpp>
9
10#include "botan/botan.h"
11#include "botan/rsa.h"
12#include "botan/keypair.h"
13
14#include "constants.hh"
15#include "keys.hh"
16#include "lua.hh"
17#include "netio.hh"
18#include "platform.hh"
19#include "transforms.hh"
20#include "sanity.hh"
21#include "ui.hh"
22
23// copyright (C) 2002, 2003, 2004 graydon hoare <graydon@pobox.com>
24// all rights reserved.
25// licensed to the public under the terms of the GNU GPL (>= 2)
26// see the file COPYING for details
27
28// there will probably forever be bugs in this file. it's very
29// hard to get right, portably and securely. sorry about that.
30
31using namespace Botan;
32using namespace std;
33
34using boost::shared_ptr;
35using boost::shared_dynamic_cast;
36using Botan::byte;
37
38static void
39do_arc4(SecureVector<byte> & sym_key,
40 SecureVector<byte> & payload)
41{
42 L(F("running arc4 process on %d bytes of data\n") % payload.size());
43 Pipe enc(get_cipher("ARC4", sym_key, ENCRYPTION));
44 enc.process_msg(payload);
45 payload = enc.read_all();
46}
47
48// 'force_from_user' means that we don't use the passphrase cache, and we
49// don't use the get_passphrase hook.
50static void
51get_passphrase(lua_hooks & lua,
52 rsa_keypair_id const & keyid,
53 string & phrase,
54 bool confirm_phrase = false,
55 bool force_from_user = false,
56 string prompt_beginning = "enter passphrase")
57{
58
59 // we permit the user to relax security here, by caching a passphrase (if
60 // they permit it) through the life of a program run. this helps when
61 // you're making a half-dozen certs during a commit or merge or
62 // something.
63 bool persist_phrase = lua.hook_persist_phrase_ok();
64 static std::map<rsa_keypair_id, string> phrases;
65
66 if (!force_from_user && phrases.find(keyid) != phrases.end())
67 {
68 phrase = phrases[keyid];
69 return;
70 }
71
72 if (!force_from_user && lua.hook_get_passphrase(keyid, phrase))
73 {
74 // user is being a slob and hooking lua to return his passphrase
75 N(phrase != "",
76 F("got empty passphrase from get_passphrase() hook"));
77 }
78 else
79 {
80 char pass1[constants::maxpasswd];
81 char pass2[constants::maxpasswd];
82 for (int i = 0; i < 3; ++i)
83 {
84 memset(pass1, 0, constants::maxpasswd);
85 memset(pass2, 0, constants::maxpasswd);
86 ui.ensure_clean_line();
87 read_password(prompt_beginning + " for key ID [" + keyid() + "]: ",
88 pass1, constants::maxpasswd);
89 cout << endl;
90 if (strlen(pass1) == 0)
91 {
92 P(F("empty passphrase not allowed"));
93 continue;
94 }
95
96 if (confirm_phrase)
97 {
98 ui.ensure_clean_line();
99 read_password((F("confirm passphrase for key ID [%s]: ") % keyid()).str(),
100 pass2, constants::maxpasswd);
101 cout << endl;
102 if (strlen(pass1) == 0 || strlen(pass2) == 0)
103 {
104 P(F("empty passphrases not allowed, try again\n"));
105 N(i < 2, F("too many failed passphrases\n"));
106 }
107 else if (strcmp(pass1, pass2) == 0)
108 break;
109 else
110 {
111 P(F("passphrases do not match, try again\n"));
112 N(i < 2, F("too many failed passphrases\n"));
113 }
114 }
115 else
116 break;
117 }
118 N(strlen(pass1) != 0, F("no passphrase given"));
119
120 try
121 {
122 phrase = pass1;
123
124 // permit security relaxation. maybe.
125 if (persist_phrase)
126 {
127 phrases.insert(make_pair(keyid,string(pass1)));
128 }
129 }
130 catch (...)
131 {
132 memset(pass1, 0, constants::maxpasswd);
133 memset(pass2, 0, constants::maxpasswd);
134 throw;
135 }
136 memset(pass1, 0, constants::maxpasswd);
137 memset(pass2, 0, constants::maxpasswd);
138 }
139}
140
141
142void
143generate_key_pair(lua_hooks & lua, // to hook for phrase
144 rsa_keypair_id const & id, // to prompting user for phrase
145 keypair & kp_out,
146 string const unit_test_passphrase)
147{
148
149 string phrase;
150 SecureVector<byte> pubkey, privkey;
151 rsa_pub_key raw_pub_key;
152 rsa_priv_key raw_priv_key;
153
154 // generate private key (and encrypt it)
155 RSA_PrivateKey priv(constants::keylen);
156
157 if (unit_test_passphrase.empty())
158 get_passphrase(lua, id, phrase, true, true);
159 else
160 phrase = unit_test_passphrase;
161
162 Pipe p;
163 p.start_msg();
164 PKCS8::encrypt_key(priv, p, phrase,
165 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", RAW_BER);
166 raw_priv_key = rsa_priv_key(p.read_all_as_string());
167
168 // generate public key
169 Pipe p2;
170 p2.start_msg();
171 X509::encode(priv, p2, RAW_BER);
172 raw_pub_key = rsa_pub_key(p2.read_all_as_string());
173
174 // if all that worked, we can return our results to caller
175 encode_base64(raw_priv_key, kp_out.priv);
176 encode_base64(raw_pub_key, kp_out.pub);
177 L(F("generated %d-byte public key\n"
178 "generated %d-byte (encrypted) private key\n")
179 % kp_out.pub().size()
180 % kp_out.priv().size());
181}
182
183// ask for passphrase then decrypt a private key.
184shared_ptr<RSA_PrivateKey>
185get_private_key(lua_hooks & lua,
186 rsa_keypair_id const & id,
187 base64< rsa_priv_key > const & priv,
188 bool force_from_user = false)
189{
190 rsa_priv_key decoded_key;
191 string phrase;
192 bool force = force_from_user;
193
194 L(F("base64-decoding %d-byte private key\n") % priv().size());
195 decode_base64(priv, decoded_key);
196 for (int i = 0; i < 3; ++i)
197 {
198 get_passphrase(lua, id, phrase, false, force);
199 L(F("have %d-byte encrypted private key\n") % decoded_key().size());
200
201 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
202 try
203 {
204 Pipe p;
205 p.process_msg(decoded_key());
206 pkcs8_key = shared_ptr<PKCS8_PrivateKey>(PKCS8::load_key(p, phrase));
207 }
208 catch (...)
209 {
210 if (i >= 2)
211 throw informative_failure("failed to decrypt private RSA key, "
212 "probably incorrect passphrase");
213 // don't use the cache bad one next time
214 force = true;
215 continue;
216 }
217
218 shared_ptr<RSA_PrivateKey> priv_key;
219 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
220 if (!priv_key)
221 throw informative_failure("Failed to get RSA signing key");
222
223 return priv_key;
224 }
225 I(false);
226}
227
228// converts an oldstyle arc4 encrypted key into a newstyle pkcs#8 encoded
229// key. the public key is also included
230void
231migrate_private_key(app_state & app,
232 rsa_keypair_id const & id,
233 base64< arc4<rsa_priv_key> > const & old_priv,
234 keypair & new_kp)
235{
236 arc4<rsa_priv_key> decoded_key;
237 SecureVector<byte> decrypted_key;
238 string phrase;
239
240 bool force = false;
241
242 // need to decrypt the old key
243 shared_ptr<RSA_PrivateKey> priv_key;
244 L(F("base64-decoding %d-byte old private key\n") % old_priv().size());
245 decode_base64(old_priv, decoded_key);
246 for (int i = 0; i < 3; ++i)
247 {
248 decrypted_key.set(reinterpret_cast<byte const *>(decoded_key().data()),
249 decoded_key().size());
250 get_passphrase(app.lua, id, phrase, false, force);
251 SecureVector<byte> sym_key;
252 sym_key.set(reinterpret_cast<byte const *>(phrase.data()), phrase.size());
253 do_arc4(sym_key, decrypted_key);
254
255 L(F("building signer from %d-byte decrypted private key\n") % decrypted_key.size());
256
257 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
258 try
259 {
260 Pipe p;
261 p.process_msg(decrypted_key);
262 pkcs8_key = shared_ptr<PKCS8_PrivateKey>(PKCS8::load_key(p, "", false));
263 }
264 catch (...)
265 {
266 if (i >= 2)
267 throw informative_failure("failed to decrypt old private RSA key, "
268 "probably incorrect passphrase");
269 // don't use the cache bad one next time
270 force = true;
271 continue;
272 }
273
274 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
275 if (!priv_key)
276 throw informative_failure("Failed to get old RSA key");
277 }
278
279 I(priv_key);
280
281 // now we can write out the new key
282 Pipe p;
283 p.start_msg();
284 PKCS8::encrypt_key(*priv_key, p, phrase,
285 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", RAW_BER);
286 rsa_priv_key raw_priv = rsa_priv_key(p.read_all_as_string());
287 encode_base64(raw_priv, new_kp.priv);
288
289 // also the public portion
290 Pipe p2;
291 p2.start_msg();
292 X509::encode(*priv_key, p2, RAW_BER);
293 rsa_pub_key raw_pub = rsa_pub_key(p2.read_all_as_string());
294 encode_base64(raw_pub, new_kp.pub);
295}
296
297void
298change_key_passphrase(lua_hooks & lua,
299 rsa_keypair_id const & id,
300 base64< rsa_priv_key > & encoded_key)
301{
302 shared_ptr<RSA_PrivateKey> priv = get_private_key(lua, id, encoded_key, true);
303
304 string new_phrase;
305 get_passphrase(lua, id, new_phrase, true, true, "enter new passphrase");
306
307 Pipe p;
308 p.start_msg();
309 PKCS8::encrypt_key(*priv, p, new_phrase,
310 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", RAW_BER);
311 rsa_priv_key decoded_key = rsa_priv_key(p.read_all_as_string());
312
313 encode_base64(decoded_key, encoded_key);
314}
315
316void
317make_signature(app_state & app, // to hook for phrase
318 rsa_keypair_id const & id, // to prompting user for phrase
319 base64< rsa_priv_key > const & priv,
320 string const & tosign,
321 base64<rsa_sha1_signature> & signature)
322{
323 SecureVector<byte> sig;
324 string sig_string;
325
326 // we permit the user to relax security here, by caching a decrypted key
327 // (if they permit it) through the life of a program run. this helps when
328 // you're making a half-dozen certs during a commit or merge or
329 // something.
330
331 bool persist_phrase = (!app.signers.empty()) || app.lua.hook_persist_phrase_ok();
332
333 shared_ptr<PK_Signer> signer;
334 shared_ptr<RSA_PrivateKey> priv_key;
335 if (persist_phrase && app.signers.find(id) != app.signers.end())
336 signer = app.signers[id].first;
337
338 else
339 {
340 shared_ptr<RSA_PrivateKey> priv_key;
341 priv_key = get_private_key(app.lua, id, priv);
342 signer = shared_ptr<PK_Signer>(get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
343
344 /* XXX This is ugly. We need to keep the key around as long
345 * as the signer is around, but the shared_ptr for the key will go
346 * away after we leave this scope. Hence we store a pair of
347 * <verifier,key> so they both exist. */
348 if (persist_phrase)
349 app.signers.insert(make_pair(id,make_pair(signer,priv_key)));
350 }
351
352 sig = signer->sign_message(reinterpret_cast<byte const *>(tosign.data()), tosign.size());
353 sig_string = string(reinterpret_cast<char const*>(sig.begin()), sig.size());
354
355 L(F("produced %d-byte signature\n") % sig_string.size());
356 encode_base64(rsa_sha1_signature(sig_string), signature);
357}
358
359bool
360check_signature(app_state &app,
361 rsa_keypair_id const & id,
362 base64<rsa_pub_key> const & pub_encoded,
363 string const & alleged_text,
364 base64<rsa_sha1_signature> const & signature)
365{
366 // examine pubkey
367
368 bool persist_phrase = (!app.verifiers.empty()) || app.lua.hook_persist_phrase_ok();
369
370 shared_ptr<PK_Verifier> verifier;
371 shared_ptr<RSA_PublicKey> pub_key;
372 if (persist_phrase
373 && app.verifiers.find(id) != app.verifiers.end())
374 verifier = app.verifiers[id].first;
375
376 else
377 {
378 rsa_pub_key pub;
379 decode_base64(pub_encoded, pub);
380 SecureVector<byte> pub_block;
381 pub_block.set(reinterpret_cast<byte const *>(pub().data()), pub().size());
382
383 L(F("building verifier for %d-byte pub key\n") % pub_block.size());
384 shared_ptr<X509_PublicKey> x509_key =
385 shared_ptr<X509_PublicKey>(X509::load_key(pub_block));
386 pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
387 if (!pub_key)
388 throw informative_failure("Failed to get RSA verifying key");
389
390 verifier = shared_ptr<PK_Verifier>(get_pk_verifier(*pub_key, "EMSA3(SHA-1)"));
391
392 /* XXX This is ugly. We need to keep the key around
393 * as long as the verifier is around, but the shared_ptr will go
394 * away after we leave this scope. Hence we store a pair of
395 * <verifier,key> so they both exist. */
396 if (persist_phrase)
397 app.verifiers.insert(make_pair(id, make_pair(verifier, pub_key)));
398 }
399
400 // examine signature
401 rsa_sha1_signature sig_decoded;
402 decode_base64(signature, sig_decoded);
403
404 // check the text+sig against the key
405 L(F("checking %d-byte (%d decoded) signature\n") %
406 signature().size() % sig_decoded().size());
407
408 bool valid_sig = verifier->verify_message(
409 reinterpret_cast<byte const*>(alleged_text.data()), alleged_text.size(),
410 reinterpret_cast<byte const*>(sig_decoded().data()), sig_decoded().size());
411
412 return valid_sig;
413}
414
415void encrypt_rsa(lua_hooks & lua,
416 rsa_keypair_id const & id,
417 base64<rsa_pub_key> & pub_encoded,
418 std::string const & plaintext,
419 rsa_oaep_sha_data & ciphertext)
420{
421 rsa_pub_key pub;
422 decode_base64(pub_encoded, pub);
423 SecureVector<byte> pub_block;
424 pub_block.set(reinterpret_cast<byte const *>(pub().data()), pub().size());
425
426 shared_ptr<X509_PublicKey> x509_key = shared_ptr<X509_PublicKey>(X509::load_key(pub_block));
427 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
428 if (!pub_key)
429 throw informative_failure("Failed to get RSA encrypting key");
430
431 shared_ptr<PK_Encryptor> encryptor;
432 encryptor = shared_ptr<PK_Encryptor>(get_pk_encryptor(*pub_key, "EME1(SHA-1)"));
433
434 SecureVector<byte> ct;
435 ct = encryptor->encrypt(
436 reinterpret_cast<byte const *>(plaintext.data()), plaintext.size());
437 ciphertext = rsa_oaep_sha_data(string(reinterpret_cast<char const *>(ct.begin()), ct.size()));
438}
439
440void decrypt_rsa(lua_hooks & lua,
441 rsa_keypair_id const & id,
442 base64< rsa_priv_key > const & priv,
443 rsa_oaep_sha_data const & ciphertext,
444 std::string & plaintext)
445{
446 shared_ptr<RSA_PrivateKey> priv_key = get_private_key(lua, id, priv);
447
448 shared_ptr<PK_Decryptor> decryptor;
449 decryptor = shared_ptr<PK_Decryptor>(get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
450
451 SecureVector<byte> plain;
452 plain = decryptor->decrypt(
453 reinterpret_cast<byte const *>(ciphertext().data()), ciphertext().size());
454 plaintext = string(reinterpret_cast<char const*>(plain.begin()), plain.size());
455}
456
457void
458read_pubkey(string const & in,
459 rsa_keypair_id & id,
460 base64<rsa_pub_key> & pub)
461{
462 string tmp_id, tmp_key;
463 size_t pos = 0;
464 extract_variable_length_string(in, tmp_id, pos, "pubkey id");
465 extract_variable_length_string(in, tmp_key, pos, "pubkey value");
466 id = tmp_id;
467 encode_base64(rsa_pub_key(tmp_key), pub);
468}
469
470void
471write_pubkey(rsa_keypair_id const & id,
472 base64<rsa_pub_key> const & pub,
473 string & out)
474{
475 rsa_pub_key pub_tmp;
476 decode_base64(pub, pub_tmp);
477 insert_variable_length_string(id(), out);
478 insert_variable_length_string(pub_tmp(), out);
479}
480
481
482void
483key_hash_code(rsa_keypair_id const & id,
484 base64<rsa_pub_key> const & pub,
485 hexenc<id> & out)
486{
487 data tdat(id() + ":" + remove_ws(pub()));
488 calculate_ident(tdat, out);
489}
490
491void
492key_hash_code(rsa_keypair_id const & id,
493 base64< rsa_priv_key > const & priv,
494 hexenc<id> & out)
495{
496 data tdat(id() + ":" + remove_ws(priv()));
497 calculate_ident(tdat, out);
498}
499
500// helper to compare if two keys have the same hash
501// (ie are the same key)
502bool
503keys_match(rsa_keypair_id const & id1,
504 base64<rsa_pub_key> const & key1,
505 rsa_keypair_id const & id2,
506 base64<rsa_pub_key> const & key2)
507{
508 hexenc<id> hash1, hash2;
509 key_hash_code(id1, key1, hash1);
510 key_hash_code(id2, key2, hash2);
511 return hash1 == hash2;
512}
513
514bool
515keys_match(rsa_keypair_id const & id1,
516 base64< rsa_priv_key > const & key1,
517 rsa_keypair_id const & id2,
518 base64< rsa_priv_key > const & key2)
519{
520 hexenc<id> hash1, hash2;
521 key_hash_code(id1, key1, hash1);
522 key_hash_code(id2, key2, hash2);
523 return hash1 == hash2;
524}
525
526void
527require_password(rsa_keypair_id const & key,
528 app_state & app)
529{
530 N(priv_key_exists(app, key),
531 F("no key pair '%s' found in key store or get_priv_key hook") % key);
532// N(app.db.public_key_exists(key),
533// F("no public key '%s' found in database") % key);
534 keypair kp;
535 load_key_pair(app, key, kp);
536 if (app.lua.hook_persist_phrase_ok())
537 {
538 string plaintext("hi maude");
539 base64<rsa_sha1_signature> sig;
540 make_signature(app, key, kp.priv, plaintext, sig);
541 N(check_signature(app, key, kp.pub, plaintext, sig),
542 F("passphrase for '%s' is incorrect") % key);
543 }
544}
545
546#ifdef BUILD_UNIT_TESTS
547#include "unit_tests.hh"
548
549static void
550arc4_test()
551{
552
553 string pt("new fascist tidiness regime in place");
554 string phr("still spring water");
555
556 SecureVector<byte> phrase(reinterpret_cast<byte const*>(phr.data()),
557 phr.size());
558
559 SecureVector<byte> orig(reinterpret_cast<byte const*>(pt.data()),
560 pt.size());
561
562 SecureVector<byte> data(orig);
563
564 BOOST_CHECKPOINT("encrypting data");
565 do_arc4(phrase, data);
566
567 BOOST_CHECK(data != orig);
568
569 BOOST_CHECKPOINT("decrypting data");
570 do_arc4(phrase, data);
571
572 BOOST_CHECK(data == orig);
573
574}
575
576static void
577signature_round_trip_test()
578{
579 app_state app;
580 app.lua.add_std_hooks();
581 app.lua.add_test_hooks();
582
583 BOOST_CHECKPOINT("generating key pairs");
584 rsa_keypair_id key("bob123@test.com");
585 keypair kp;
586 generate_key_pair(app.lua, key, kp, "bob123@test.com");
587
588 BOOST_CHECKPOINT("signing plaintext");
589 string plaintext("test string to sign");
590 base64<rsa_sha1_signature> sig;
591 make_signature(app, key, kp.priv, plaintext, sig);
592
593 BOOST_CHECKPOINT("checking signature");
594 BOOST_CHECK(check_signature(app, key, kp.pub, plaintext, sig));
595
596 string broken_plaintext = plaintext + " ...with a lie";
597 BOOST_CHECKPOINT("checking non-signature");
598 BOOST_CHECK(!check_signature(app, key, kp.pub, broken_plaintext, sig));
599}
600
601void
602add_key_tests(test_suite * suite)
603{
604 I(suite);
605 suite->add(BOOST_TEST_CASE(&arc4_test));
606 suite->add(BOOST_TEST_CASE(&signature_round_trip_test));
607}
608
609#endif // BUILD_UNIT_TESTS

Archive Download this file

Branches

Tags

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