monotone

monotone Mtn Source Tree

Root/keys.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.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 <map>
12#include <iostream>
13#include <unistd.h>
14#include <string.h>
15
16#include <boost/scoped_ptr.hpp>
17#include <boost/shared_ptr.hpp>
18
19#include "botan/botan.h"
20#include "botan/rsa.h"
21#include "botan/keypair.h"
22#include "botan/pem.h"
23
24#include "constants.hh"
25#include "keys.hh"
26#include "lua_hooks.hh"
27#include "netio.hh"
28#include "platform.hh"
29#include "safe_map.hh"
30#include "transforms.hh"
31#include "simplestring_xform.hh"
32#include "sanity.hh"
33#include "ui.hh"
34#include "cert.hh"
35#include "app_state.hh"
36#include "charset.hh"
37#include "ssh_agent.hh"
38
39using std::cout;
40using std::make_pair;
41using std::map;
42using std::string;
43using std::vector;
44
45using boost::scoped_ptr;
46using boost::shared_ptr;
47using boost::shared_dynamic_cast;
48
49using Botan::byte;
50using Botan::get_cipher;
51using Botan::PKCS8_PrivateKey;
52using Botan::PK_Decryptor;
53using Botan::PK_Encryptor;
54using Botan::PK_Signer;
55using Botan::PK_Verifier;
56using Botan::Pipe;
57using Botan::RSA_PrivateKey;
58using Botan::RSA_PublicKey;
59using Botan::SecureVector;
60using Botan::X509_PublicKey;
61
62// there will probably forever be bugs in this file. it's very
63// hard to get right, portably and securely. sorry about that.
64
65static void
66do_arc4(SecureVector<Botan::byte> & sym_key,
67 SecureVector<Botan::byte> & payload)
68{
69 L(FL("running arc4 process on %d bytes of data") % payload.size());
70 Pipe enc(get_cipher("ARC4", sym_key, Botan::ENCRYPTION));
71 enc.process_msg(payload);
72 payload = enc.read_all();
73}
74
75// 'force_from_user' means that we don't use the passphrase cache, and we
76// don't use the get_passphrase hook.
77void
78get_passphrase(lua_hooks & lua,
79 rsa_keypair_id const & keyid,
80 utf8 & phrase,
81 bool confirm_phrase,
82 bool force_from_user,
83 bool generating_key)
84{
85
86 // we permit the user to relax security here, by caching a passphrase (if
87 // they permit it) through the life of a program run. this helps when
88 // you're making a half-dozen certs during a commit or merge or
89 // something.
90 bool persist_phrase = lua.hook_persist_phrase_ok();
91 static map<rsa_keypair_id, utf8> phrases;
92
93 if (!force_from_user && phrases.find(keyid) != phrases.end())
94 {
95 phrase = phrases[keyid];
96 return;
97 }
98
99 string lua_phrase;
100 if (!force_from_user && lua.hook_get_passphrase(keyid, lua_phrase))
101 {
102 // user is being a slob and hooking lua to return his passphrase
103 phrase = utf8(lua_phrase);
104 N(phrase != utf8(""),
105 F("got empty passphrase from get_passphrase() hook"));
106 }
107 else
108 {
109 char pass1[constants::maxpasswd];
110 char pass2[constants::maxpasswd];
111 for (int i = 0; i < 3; ++i)
112 {
113 memset(pass1, 0, constants::maxpasswd);
114 memset(pass2, 0, constants::maxpasswd);
115 ui.ensure_clean_line();
116 string prompt1 = ((confirm_phrase && !generating_key
117 ? F("enter new passphrase for key ID [%s]: ")
118 : F("enter passphrase for key ID [%s]: "))
119 % keyid()).str();
120
121 read_password(prompt1, pass1, constants::maxpasswd);
122 if (confirm_phrase)
123 {
124 ui.ensure_clean_line();
125 read_password((F("confirm passphrase for key ID [%s]: ")
126 % keyid()).str(),
127 pass2, constants::maxpasswd);
128 if (strcmp(pass1, pass2) == 0)
129 break;
130 else
131 {
132 P(F("passphrases do not match, try again"));
133 N(i < 2, F("too many failed passphrases"));
134 }
135 }
136 else
137 break;
138 }
139
140 try
141 {
142 external ext_phrase(pass1);
143 system_to_utf8(ext_phrase, phrase);
144
145 // permit security relaxation. maybe.
146 if (persist_phrase)
147 {
148 phrases.erase(keyid);
149 safe_insert(phrases, make_pair(keyid, phrase));
150 }
151 }
152 catch (...)
153 {
154 memset(pass1, 0, constants::maxpasswd);
155 memset(pass2, 0, constants::maxpasswd);
156 throw;
157 }
158 memset(pass1, 0, constants::maxpasswd);
159 memset(pass2, 0, constants::maxpasswd);
160 }
161}
162
163
164void
165generate_key_pair(lua_hooks & lua, // to hook for phrase
166 rsa_keypair_id const & id, // to prompting user for phrase
167 keypair & kp_out)
168{
169 utf8 phrase;
170 get_passphrase(lua, id, phrase, true, true, true);
171 generate_key_pair(kp_out, phrase);
172}
173
174
175void
176generate_key_pair(keypair & kp_out,
177 utf8 const phrase)
178{
179 SecureVector<Botan::byte> pubkey, privkey;
180 rsa_pub_key raw_pub_key;
181 rsa_priv_key raw_priv_key;
182
183 // generate private key (and encrypt it)
184 RSA_PrivateKey priv(constants::keylen);
185
186 Pipe p;
187 p.start_msg();
188 if (phrase().length()) {
189 Botan::PKCS8::encrypt_key(priv,
190 p,
191 phrase(),
192 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
193 Botan::RAW_BER);
194 } else {
195 Botan::PKCS8::encode(priv, p);
196 }
197 raw_priv_key = rsa_priv_key(p.read_all_as_string());
198
199 // generate public key
200 Pipe p2;
201 p2.start_msg();
202 Botan::X509::encode(priv, p2, Botan::RAW_BER);
203 raw_pub_key = rsa_pub_key(p2.read_all_as_string());
204
205 // if all that worked, we can return our results to caller
206 encode_base64(raw_priv_key, kp_out.priv);
207 encode_base64(raw_pub_key, kp_out.pub);
208 L(FL("generated %d-byte public key\n"
209 "generated %d-byte (encrypted) private key\n")
210 % kp_out.pub().size()
211 % kp_out.priv().size());
212}
213
214// ask for passphrase then decrypt a private key.
215shared_ptr<RSA_PrivateKey>
216get_private_key(lua_hooks & lua,
217 rsa_keypair_id const & id,
218 base64< rsa_priv_key > const & priv,
219 bool force_from_user)
220{
221 rsa_priv_key decoded_key;
222 utf8 phrase;
223 bool force = force_from_user;
224
225 L(FL("base64-decoding %d-byte private key") % priv().size());
226 decode_base64(priv, decoded_key);
227 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
228 try //with empty passphrase
229 {
230 Pipe p;
231 p.process_msg(decoded_key());
232 pkcs8_key = shared_ptr<PKCS8_PrivateKey>(Botan::PKCS8::load_key(p, phrase()));
233 }
234 catch (...)
235 {
236 L(FL("failed to decrypt key with no passphrase"));
237 }
238 if (!pkcs8_key)
239 {
240 for (int i = 0; i < 3; ++i)
241 {
242 get_passphrase(lua, id, phrase, false, force);
243 L(FL("have %d-byte encrypted private key") % decoded_key().size());
244
245 try
246 {
247 Pipe p;
248 p.process_msg(decoded_key());
249 pkcs8_key = shared_ptr<PKCS8_PrivateKey>(Botan::PKCS8::load_key(p, phrase()));
250 break;
251 }
252 catch (...)
253 {
254 if (i >= 2)
255 throw informative_failure("failed to decrypt private RSA key, "
256 "probably incorrect passphrase");
257 // don't use the cached bad one next time
258 force = true;
259 continue;
260 }
261 }
262 }
263 if (pkcs8_key)
264 {
265 shared_ptr<RSA_PrivateKey> priv_key;
266 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
267 if (!priv_key)
268 throw informative_failure("Failed to get RSA signing key");
269
270 return priv_key;
271 }
272 I(false);
273}
274
275// converts an oldstyle arc4 encrypted key into a newstyle pkcs#8 encoded
276// key. the public key is also included
277void
278migrate_private_key(app_state & app,
279 rsa_keypair_id const & id,
280 base64< arc4<rsa_priv_key> > const & old_priv,
281 keypair & new_kp)
282{
283 arc4<rsa_priv_key> decoded_key;
284 SecureVector<Botan::byte> decrypted_key;
285 utf8 phrase;
286
287 bool force = false;
288
289 // need to decrypt the old key
290 shared_ptr<RSA_PrivateKey> priv_key;
291 L(FL("base64-decoding %d-byte old private key") % old_priv().size());
292 decode_base64(old_priv, decoded_key);
293 for (int i = 0; i < 3; ++i)
294 {
295 decrypted_key.set(reinterpret_cast<Botan::byte const *>(decoded_key().data()),
296 decoded_key().size());
297 get_passphrase(app.lua, id, phrase, false, force);
298 SecureVector<Botan::byte> sym_key;
299 sym_key.set(reinterpret_cast<Botan::byte const *>(phrase().data()), phrase().size());
300 do_arc4(sym_key, decrypted_key);
301
302 L(FL("building signer from %d-byte decrypted private key") % decrypted_key.size());
303
304 shared_ptr<PKCS8_PrivateKey> pkcs8_key;
305 try
306 {
307 Pipe p;
308 p.process_msg(Botan::PEM_Code::encode(decrypted_key, "PRIVATE KEY"));
309 pkcs8_key = shared_ptr<PKCS8_PrivateKey>(Botan::PKCS8::load_key(p));
310 }
311 catch (...)
312 {
313 if (i >= 2)
314 throw informative_failure("failed to decrypt old private RSA key, "
315 "probably incorrect passphrase");
316 // don't use the cache bad one next time
317 force = true;
318 continue;
319 }
320
321 priv_key = shared_dynamic_cast<RSA_PrivateKey>(pkcs8_key);
322 if (!priv_key)
323 throw informative_failure("Failed to get old RSA key");
324 }
325
326 I(priv_key);
327
328 // now we can write out the new key
329 Pipe p;
330 p.start_msg();
331 Botan::PKCS8::encrypt_key(*priv_key, p, phrase(),
332 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", Botan::RAW_BER);
333 rsa_priv_key raw_priv = rsa_priv_key(p.read_all_as_string());
334 encode_base64(raw_priv, new_kp.priv);
335
336 // also the public portion
337 Pipe p2;
338 p2.start_msg();
339 Botan::X509::encode(*priv_key, p2, Botan::RAW_BER);
340 rsa_pub_key raw_pub = rsa_pub_key(p2.read_all_as_string());
341 encode_base64(raw_pub, new_kp.pub);
342}
343
344void
345change_key_passphrase(lua_hooks & lua,
346 rsa_keypair_id const & id,
347 base64< rsa_priv_key > & encoded_key)
348{
349 shared_ptr<RSA_PrivateKey> priv = get_private_key(lua, id, encoded_key, true);
350
351 utf8 new_phrase;
352 get_passphrase(lua, id, new_phrase, true, true);
353
354 Pipe p;
355 p.start_msg();
356 Botan::PKCS8::encrypt_key(*priv, p, new_phrase(),
357 "PBE-PKCS5v20(SHA-1,TripleDES/CBC)", Botan::RAW_BER);
358 rsa_priv_key decoded_key = rsa_priv_key(p.read_all_as_string());
359
360 encode_base64(decoded_key, encoded_key);
361}
362
363void
364make_signature(app_state & app, // to hook for phrase
365 rsa_keypair_id const & id, // to prompting user for phrase
366 base64< rsa_priv_key > const & priv,
367 string const & tosign,
368 base64<rsa_sha1_signature> & signature)
369{
370 E(!app.opts.ssh_sign.empty(),
371 F("--ssh-sign requires a value ['yes', 'no', 'only', or 'check']"));
372 E(app.opts.ssh_sign == "yes"
373 || app.opts.ssh_sign == "no"
374 || app.opts.ssh_sign == "check"
375 || app.opts.ssh_sign == "only",
376 F("--ssh-sign must be set to 'yes', 'no', 'only', or 'check'"));
377
378 keypair key;
379 app.keys.get_key_pair(id, key);
380
381 string sig_string;
382 //sign with ssh-agent (if connected)
383 N(app.agent.connected() || app.opts.ssh_sign != "only",
384 F("You have chosen to sign only with ssh-agent but ssh-agent"
385 " does not seem to be running."));
386 if (app.opts.ssh_sign == "yes"
387 || app.opts.ssh_sign == "check"
388 || app.opts.ssh_sign == "only")
389 {
390 /*
391 vector<RSA_PublicKey> ssh_keys = app.agent.get_keys();
392 if (ssh_keys.size() <= 0)
393 L(FL("make_signature: no rsa keys received from ssh-agent"));
394 else {
395 */
396 if (app.agent.connected()) {
397 //grab the monotone public key as an RSA_PublicKey
398 app.keys.get_key_pair(id, key);
399 rsa_pub_key pub;
400 decode_base64(key.pub, pub);
401 SecureVector<Botan::byte> pub_block;
402 pub_block.set(reinterpret_cast<Botan::byte const *>(pub().data()),
403 pub().size());
404 L(FL("make_signature: building %d-byte pub key") % pub_block.size());
405 shared_ptr<X509_PublicKey> x509_key =
406 shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
407 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
408
409 if (!pub_key)
410 throw informative_failure("Failed to get monotone RSA public key");
411 /*
412 //if monotone key matches ssh-agent key, sign with ssh-agent
413 for (vector<RSA_PublicKey>::const_iterator
414 si = ssh_keys.begin(); si != ssh_keys.end(); ++si) {
415 if ((*pub_key).get_e() == (*si).get_e()
416 && (*pub_key).get_n() == (*si).get_n()) {
417 L(FL("make_signature: ssh key matches monotone key, signing with"
418 " ssh-agent"));
419 */
420 app.agent.sign_data(*pub_key, tosign, sig_string);
421 /*
422 break;
423 }
424 }
425 */
426 }
427 if (sig_string.length() <= 0)
428 L(FL("make_signature: monotone and ssh-agent keys do not match, will"
429 " use monotone signing"));
430 }
431
432 string ssh_sig = sig_string;
433
434 N(ssh_sig.length() > 0 || app.opts.ssh_sign != "only",
435 F("You don't seem to have your monotone key imported "));
436
437 if (ssh_sig.length() <= 0
438 || app.opts.ssh_sign == "check"
439 || app.opts.ssh_sign == "no")
440 {
441 SecureVector<Botan::byte> sig;
442
443 // we permit the user to relax security here, by caching a decrypted key
444 // (if they permit it) through the life of a program run. this helps when
445 // you're making a half-dozen certs during a commit or merge or
446 // something.
447
448 bool persist_phrase = (!app.signers.empty())
449 || app.lua.hook_persist_phrase_ok();
450
451 shared_ptr<PK_Signer> signer;
452 shared_ptr<RSA_PrivateKey> priv_key;
453 if (persist_phrase && app.signers.find(id) != app.signers.end())
454 signer = app.signers[id].first;
455
456 else
457 {
458 priv_key = get_private_key(app.lua, id, priv);
459 if (app.agent.connected()
460 && app.opts.ssh_sign != "only"
461 && app.opts.ssh_sign != "no") {
462 L(FL("keys.cc: make_signature: adding private key (%s) to ssh-agent") % id());
463 app.agent.add_identity(*priv_key, id());
464 }
465 signer = shared_ptr<PK_Signer>(get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
466
467 /* XXX This is ugly. We need to keep the key around as long
468 * as the signer is around, but the shared_ptr for the key will go
469 * away after we leave this scope. Hence we store a pair of
470 * <verifier,key> so they both exist. */
471 if (persist_phrase)
472 app.signers.insert(make_pair(id,make_pair(signer,priv_key)));
473 }
474
475 sig = signer->sign_message(reinterpret_cast<Botan::byte const *>(tosign.data()), tosign.size());
476 sig_string = string(reinterpret_cast<char const*>(sig.begin()), sig.size());
477 }
478
479 if (app.opts.ssh_sign == "check" && ssh_sig.length() > 0)
480 {
481 E(ssh_sig == sig_string,
482 F("make_signature: ssh signature (%i) != monotone signature (%i)\n"
483 "ssh signature : %s\n"
484 "monotone signature: %s")
485 % ssh_sig.length()
486 % sig_string.length()
487 % encode_hexenc(ssh_sig)
488 % encode_hexenc(sig_string));
489 L(FL("make_signature: signatures from ssh-agent and monotone"
490 " are the same"));
491 }
492
493 L(FL("make_signature: produced %d-byte signature") % sig_string.size());
494 encode_base64(rsa_sha1_signature(sig_string), signature);
495
496 E(check_signature(app, id, key.pub, tosign, signature),
497 F("make_signature: signature is not valid"));
498}
499
500bool
501check_signature(app_state &app,
502 rsa_keypair_id const & id,
503 base64<rsa_pub_key> const & pub_encoded,
504 string const & alleged_text,
505 base64<rsa_sha1_signature> const & signature)
506{
507 // examine pubkey
508
509 bool persist_phrase = (!app.verifiers.empty()) || app.lua.hook_persist_phrase_ok();
510
511 shared_ptr<PK_Verifier> verifier;
512 shared_ptr<RSA_PublicKey> pub_key;
513 if (persist_phrase
514 && app.verifiers.find(id) != app.verifiers.end())
515 verifier = app.verifiers[id].first;
516
517 else
518 {
519 rsa_pub_key pub;
520 decode_base64(pub_encoded, pub);
521 SecureVector<Botan::byte> pub_block;
522 pub_block.set(reinterpret_cast<Botan::byte const *>(pub().data()), pub().size());
523
524 L(FL("building verifier for %d-byte pub key") % pub_block.size());
525 shared_ptr<X509_PublicKey> x509_key =
526 shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
527 pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
528 if (!pub_key)
529 throw informative_failure("Failed to get RSA verifying key");
530
531 verifier = shared_ptr<PK_Verifier>(get_pk_verifier(*pub_key, "EMSA3(SHA-1)"));
532
533 /* XXX This is ugly. We need to keep the key around
534 * as long as the verifier is around, but the shared_ptr will go
535 * away after we leave this scope. Hence we store a pair of
536 * <verifier,key> so they both exist. */
537 if (persist_phrase)
538 app.verifiers.insert(make_pair(id, make_pair(verifier, pub_key)));
539 }
540
541 // examine signature
542 rsa_sha1_signature sig_decoded;
543 decode_base64(signature, sig_decoded);
544
545 // check the text+sig against the key
546 L(FL("checking %d-byte (%d decoded) signature") %
547 signature().size() % sig_decoded().size());
548
549 bool valid_sig = verifier->verify_message(
550 reinterpret_cast<Botan::byte const*>(alleged_text.data()), alleged_text.size(),
551 reinterpret_cast<Botan::byte const*>(sig_decoded().data()), sig_decoded().size());
552
553 return valid_sig;
554}
555
556void encrypt_rsa(lua_hooks & lua,
557 rsa_keypair_id const & id,
558 base64<rsa_pub_key> & pub_encoded,
559 string const & plaintext,
560 rsa_oaep_sha_data & ciphertext)
561{
562 rsa_pub_key pub;
563 decode_base64(pub_encoded, pub);
564 SecureVector<Botan::byte> pub_block;
565 pub_block.set(reinterpret_cast<Botan::byte const *>(pub().data()), pub().size());
566
567 shared_ptr<X509_PublicKey> x509_key = shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
568 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
569 if (!pub_key)
570 throw informative_failure("Failed to get RSA encrypting key");
571
572 shared_ptr<PK_Encryptor> encryptor;
573 encryptor = shared_ptr<PK_Encryptor>(get_pk_encryptor(*pub_key, "EME1(SHA-1)"));
574
575 SecureVector<Botan::byte> ct;
576 ct = encryptor->encrypt(
577 reinterpret_cast<Botan::byte const *>(plaintext.data()), plaintext.size());
578 ciphertext = rsa_oaep_sha_data(string(reinterpret_cast<char const *>(ct.begin()), ct.size()));
579}
580
581void decrypt_rsa(lua_hooks & lua,
582 rsa_keypair_id const & id,
583 base64< rsa_priv_key > const & priv,
584 rsa_oaep_sha_data const & ciphertext,
585 string & plaintext)
586{
587 shared_ptr<RSA_PrivateKey> priv_key = get_private_key(lua, id, priv);
588
589 shared_ptr<PK_Decryptor> decryptor;
590 decryptor = shared_ptr<PK_Decryptor>(get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
591
592 SecureVector<Botan::byte> plain;
593 plain = decryptor->decrypt(
594 reinterpret_cast<Botan::byte const *>(ciphertext().data()), ciphertext().size());
595 plaintext = string(reinterpret_cast<char const*>(plain.begin()), plain.size());
596}
597
598void
599read_pubkey(string const & in,
600 rsa_keypair_id & id,
601 base64<rsa_pub_key> & pub)
602{
603 string tmp_id, tmp_key;
604 size_t pos = 0;
605 extract_variable_length_string(in, tmp_id, pos, "pubkey id");
606 extract_variable_length_string(in, tmp_key, pos, "pubkey value");
607 id = rsa_keypair_id(tmp_id);
608 encode_base64(rsa_pub_key(tmp_key), pub);
609}
610
611void
612write_pubkey(rsa_keypair_id const & id,
613 base64<rsa_pub_key> const & pub,
614 string & out)
615{
616 rsa_pub_key pub_tmp;
617 decode_base64(pub, pub_tmp);
618 insert_variable_length_string(id(), out);
619 insert_variable_length_string(pub_tmp(), out);
620}
621
622
623void
624key_hash_code(rsa_keypair_id const & ident,
625 base64<rsa_pub_key> const & pub,
626 hexenc<id> & out)
627{
628 data tdat(ident() + ":" + remove_ws(pub()));
629 calculate_ident(tdat, out);
630}
631
632void
633key_hash_code(rsa_keypair_id const & ident,
634 base64< rsa_priv_key > const & priv,
635 hexenc<id> & out)
636{
637 data tdat(ident() + ":" + remove_ws(priv()));
638 calculate_ident(tdat, out);
639}
640
641// helper to compare if two keys have the same hash
642// (ie are the same key)
643bool
644keys_match(rsa_keypair_id const & id1,
645 base64<rsa_pub_key> const & key1,
646 rsa_keypair_id const & id2,
647 base64<rsa_pub_key> const & key2)
648{
649 hexenc<id> hash1, hash2;
650 key_hash_code(id1, key1, hash1);
651 key_hash_code(id2, key2, hash2);
652 return hash1 == hash2;
653}
654
655bool
656keys_match(rsa_keypair_id const & id1,
657 base64< rsa_priv_key > const & key1,
658 rsa_keypair_id const & id2,
659 base64< rsa_priv_key > const & key2)
660{
661 hexenc<id> hash1, hash2;
662 key_hash_code(id1, key1, hash1);
663 key_hash_code(id2, key2, hash2);
664 return hash1 == hash2;
665}
666
667void
668require_password(rsa_keypair_id const & key,
669 app_state & app)
670{
671 N(priv_key_exists(app, key),
672 F("no key pair '%s' found in key store '%s'")
673 % key % app.keys.get_key_dir());
674 keypair kp;
675 load_key_pair(app, key, kp);
676 if (app.lua.hook_persist_phrase_ok())
677 {
678 string plaintext("hi maude");
679 base64<rsa_sha1_signature> sig;
680 make_signature(app, key, kp.priv, plaintext, sig);
681 N(check_signature(app, key, kp.pub, plaintext, sig),
682 F("passphrase for '%s' is incorrect") % key);
683 }
684}
685
686#ifdef BUILD_UNIT_TESTS
687#include "unit_tests.hh"
688
689UNIT_TEST(key, arc4)
690{
691
692 string pt("new fascist tidiness regime in place");
693 string phr("still spring water");
694
695 SecureVector<Botan::byte> phrase(reinterpret_cast<Botan::byte const*>(phr.data()),
696 phr.size());
697
698 SecureVector<Botan::byte> orig(reinterpret_cast<Botan::byte const*>(pt.data()),
699 pt.size());
700
701 SecureVector<Botan::byte> data(orig);
702
703 UNIT_TEST_CHECKPOINT("encrypting data");
704 do_arc4(phrase, data);
705
706 UNIT_TEST_CHECK(data != orig);
707
708 UNIT_TEST_CHECKPOINT("decrypting data");
709 do_arc4(phrase, data);
710
711 UNIT_TEST_CHECK(data == orig);
712
713}
714
715UNIT_TEST(key, signature_round_trip)
716{
717 app_state app;
718 app.set_key_dir(system_path(get_current_working_dir()) / ".monotone_tmp" / "keys");
719 app.lua.add_std_hooks();
720 app.lua.add_test_hooks();
721
722 UNIT_TEST_CHECKPOINT("generating key pairs");
723 keypair kp;
724 utf8 passphrase("bob123@example.com");
725 rsa_keypair_id key("bob123@example.com");
726 generate_key_pair(kp, passphrase);
727 app.keys.put_key_pair(key, kp);
728
729 UNIT_TEST_CHECKPOINT("signing plaintext");
730 string plaintext("test string to sign");
731 base64<rsa_sha1_signature> sig;
732 make_signature(app, key, kp.priv, plaintext, sig);
733
734 UNIT_TEST_CHECKPOINT("checking signature");
735 UNIT_TEST_CHECK(check_signature(app, key, kp.pub, plaintext, sig));
736
737 string broken_plaintext = plaintext + " ...with a lie";
738 UNIT_TEST_CHECKPOINT("checking non-signature");
739 UNIT_TEST_CHECK(!check_signature(app, key, kp.pub, broken_plaintext, sig));
740 app.keys.delete_key(key);
741}
742
743#endif // BUILD_UNIT_TESTS
744
745// Local Variables:
746// mode: C++
747// fill-column: 76
748// c-file-style: "gnu"
749// indent-tabs-mode: nil
750// End:
751// 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