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

Archive Download this file

Branches

Tags

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