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