~ubuntu-branches/debian/sid/botan/sid

« back to all changes in this revision

Viewing changes to misc/gtk/dsa.cpp

  • Committer: Package Import Robot
  • Author(s): Laszlo Boszormenyi (GCS)
  • Date: 2018-03-01 22:23:25 UTC
  • mfrom: (1.2.2)
  • Revision ID: package-import@ubuntu.com-20180301222325-7p7vc45gu3hta34d
Tags: 2.4.0-2
* Don't remove .doctrees from the manual if it doesn't exist.
* Don't specify parallel to debhelper.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
  This shows some of the details involved in a GUI application that uses
3
 
  Botan. Actually most of the code is just dealing with GTK+, but it shows how
4
 
  the password callback and pulse function stuff works. (See gtk_ui.cpp for the
5
 
  acutal password callback code.)
6
 
 
7
 
  The major points of interest (assuming what you care about is how to use
8
 
  Botan from a GUI, and not looking at my terrible GTK code) are gtk_ui.cpp
9
 
  and, in this file, GTK_Pulse, gen_key(), and get_key():
10
 
 
11
 
     gtk_ui.cpp and get_key() show how to get a passphrase from a user for
12
 
     decrypting (well, in theory, anything), but in this case, PKCS #8 private
13
 
     keys. Which is coincidentally the only thing Botan currently uses UI
14
 
     objects for, though that will probably change eventually. GTK_UI does
15
 
     double duty, for getting passphrases for encryption as well (in
16
 
     do_save_key).
17
 
 
18
 
     gen_key() and GTK_Pulse show how to do an activity meter while doing a
19
 
     long-term operation inside Botan. Since, typically, the only operations
20
 
     which take a long time and can't be broken up into smaller parts are prime
21
 
     generation/testing, that is currently where the pulse hooks are
22
 
     called. It's certainly not the most general callback method in the world,
23
 
     but it's general enough that it's usable without getting in the way too
24
 
     much. The callbacks will eventually be extended to other parts of the
25
 
     library (Pipe, maybe) where it's useful.
26
 
 
27
 
  This program is in the public domain.
28
 
*/
29
 
#include <fstream>
30
 
#include <iostream>
31
 
#include <memory>
32
 
 
33
 
#include <botan/botan.h>
34
 
#include <botan/libstate.h>
35
 
#include <botan/look_pk.h>
36
 
#include <botan/filters.h>
37
 
#include <botan/dsa.h>
38
 
// we don't have a 'using namespace' here, so it's easy to grep for code that
39
 
// is actually dealing with the library (rather than messing around with GTK).
40
 
 
41
 
#include <gtk/gtk.h>
42
 
#include "gtk_ui.h"
43
 
 
44
 
/*************************************************
45
 
* Pop up an message box                          *
46
 
*************************************************/
47
 
static void show_dialog(const std::string& about_message,
48
 
                        const std::string& dialog_name)
49
 
   {
50
 
   GtkDialogFlags flags =
51
 
      (GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL);
52
 
 
53
 
   GtkWidget* dialog = gtk_dialog_new_with_buttons(dialog_name.c_str(),
54
 
                                                   NULL, flags,
55
 
                                                   GTK_STOCK_OK,
56
 
                                                   GTK_RESPONSE_NONE,
57
 
                                                   NULL);
58
 
   GtkWidget* label = gtk_label_new(NULL);
59
 
   gtk_label_set_markup(GTK_LABEL(label), about_message.c_str());
60
 
   gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
61
 
 
62
 
   g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
63
 
                            G_CALLBACK(gtk_widget_destroy),
64
 
                            GTK_OBJECT(dialog));
65
 
 
66
 
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
67
 
   gtk_widget_show_all(dialog);
68
 
   }
69
 
 
70
 
/*************************************************
71
 
* Pop up an About box                            *
72
 
*************************************************/
73
 
static void show_about()
74
 
   {
75
 
   const std::string about_message =
76
 
      "<big>DSA Utility</big>\n"
77
 
      "\n"
78
 
      "A simple application showing how to use Botan within a GUI.\n"
79
 
      "It lets you generate or load keys, and sign text files.\n"
80
 
      "\n"
81
 
      "Send comments/questions to <tt>lloyd@randombit.net</tt>";
82
 
 
83
 
   show_dialog(about_message, "About");
84
 
   }
85
 
 
86
 
/*************************************************
87
 
* Pop up a help box                              *
88
 
*************************************************/
89
 
static void show_help()
90
 
   {
91
 
   const std::string help_message =
92
 
      "<big>DSA Utility Help</big>\n"
93
 
      "\n"
94
 
      "Simply, this is a (very) simple text editor, with the added ability\n"
95
 
      "of being able to generate or read a DSA private key, and sign the\n"
96
 
      "text buffer using that key.\n"
97
 
      "\n"
98
 
      "You can load, edit, and save text files as you would normally. If a\n"
99
 
      "key is loaded (done using the commands in the Keys menu), you can\n"
100
 
      "also use the Sign command (in the Signing menu) to generate a\n"
101
 
      "signature for the current file. It will be displayed at the bottom\n"
102
 
      "of the screen (if it has been calculated for the current buffer\n"
103
 
      "contents), and can be saved using the \"Save Sig\" command.\n"
104
 
      "\n"
105
 
      "Signatures generated by this program can be verified using a the\n"
106
 
      "<tt>dsa_ver</tt> example included in the Botan distribution.\n";
107
 
 
108
 
   show_dialog(help_message, "Help");
109
 
   }
110
 
 
111
 
/*************************************************
112
 
* Get and return a filename from the user        *
113
 
*************************************************/
114
 
static std::string get_filename(const std::string& title)
115
 
   {
116
 
   GtkWidget* dialog = gtk_file_selection_new(title.c_str());
117
 
 
118
 
   /* Some window managers don't display the titles of transient windows,
119
 
      put a message elsewhere for those people.
120
 
   */
121
 
   GtkWidget* label = gtk_label_new(title.c_str());
122
 
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
123
 
 
124
 
   std::string fsname;
125
 
 
126
 
   gtk_widget_show(label); /* dialog_run won't show sub-widgets */
127
 
   if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
128
 
      fsname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog));
129
 
   gtk_widget_destroy(dialog);
130
 
 
131
 
   /* If it's a directory, that's no good */
132
 
   if(fsname.size() && fsname[fsname.size()-1] == '/')
133
 
      return "";
134
 
 
135
 
   return fsname;
136
 
   }
137
 
 
138
 
/*************************************************
139
 
* Global state                                   *
140
 
*************************************************/
141
 
static Botan::DSA_PrivateKey* key = 0; // our key
142
 
static GtkTextBuffer* buffer = 0; // the text buffer
143
 
static std::string buffer_source;
144
 
  // what file (if any) the buffer's data came from
145
 
static GtkWidget* sig_view = 0; // the signature
146
 
 
147
 
/*************************************************
148
 
* Zap the currently set signature (if any)       *
149
 
*************************************************/
150
 
static void zap_sig()
151
 
   {
152
 
   gtk_editable_delete_text(GTK_EDITABLE(sig_view), 0, -1);
153
 
   }
154
 
 
155
 
/*************************************************
156
 
* Save the current key                           *
157
 
*************************************************/
158
 
static void do_save_key(const std::string& title)
159
 
   {
160
 
   if(key == 0)
161
 
      return;
162
 
 
163
 
   std::string filename = get_filename(title.c_str());
164
 
 
165
 
   if(filename != "")
166
 
      {
167
 
      const std::string msg = "Select a passphrase to encrypt the key:";
168
 
 
169
 
      std::ofstream out_priv(filename.c_str());
170
 
 
171
 
      GTK_UI ui;
172
 
      Botan::User_Interface::UI_Result result;
173
 
      std::string passphrase = ui.get_passphrase(msg, result);
174
 
 
175
 
      if(result == Botan::User_Interface::OK)
176
 
         out_priv << Botan::PKCS8::PEM_encode(*key, passphrase);
177
 
      else
178
 
         out_priv << Botan::PKCS8::PEM_encode(*key);
179
 
 
180
 
      // for testing
181
 
      //std::cout << X509::PEM_encode(*key);
182
 
      }
183
 
   }
184
 
 
185
 
/*************************************************
186
 
* Generate a signature for the text buffer       *
187
 
*************************************************/
188
 
static void sign_buffer()
189
 
   {
190
 
   /* No key? Ignore request. */
191
 
   if(key == 0)
192
 
      return;
193
 
 
194
 
   /* same format as the text-mode dsa_sign example */
195
 
   Botan::Pipe pipe(new Botan::PK_Signer_Filter(
196
 
                       Botan::get_pk_signer(*key, "EMSA1(SHA-1)")
197
 
                       ),
198
 
                    new Botan::Base64_Encoder
199
 
      );
200
 
 
201
 
   /* It would probably be smart to do this a little bit at a time */
202
 
   GtkTextIter start, end;
203
 
   gtk_text_buffer_get_bounds(buffer, &start, &end);
204
 
   gchar* bits = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
205
 
   size_t length = strlen(bits);
206
 
 
207
 
   pipe.start_msg();
208
 
   pipe.write((unsigned char*)bits, length);
209
 
   pipe.end_msg();
210
 
 
211
 
   std::string sig = pipe.read_all_as_string();
212
 
 
213
 
   zap_sig();
214
 
 
215
 
   gint position = 0;
216
 
   gtk_editable_insert_text(GTK_EDITABLE(sig_view), sig.c_str(), sig.length(),
217
 
                            &position);
218
 
 
219
 
   g_free(bits);
220
 
   }
221
 
 
222
 
/*************************************************
223
 
* GTK+ pulse callback                            *
224
 
*************************************************/
225
 
class GTK_Pulse : public Botan::Library_State::UI
226
 
   {
227
 
   public:
228
 
      void pulse(Botan::Pulse_Type);
229
 
   };
230
 
 
231
 
void GTK_Pulse::pulse(Botan::Pulse_Type)
232
 
   {
233
 
   /* We need this to flush the updates, otherwise GTK+ will wait until we're
234
 
   done with the computation before doing any updates (generally the right
235
 
   thing, but not with a progress bar).
236
 
   */
237
 
 
238
 
   while(gtk_events_pending())
239
 
      gtk_main_iteration();
240
 
   }
241
 
 
242
 
/*************************************************
243
 
* Actual do the pulse (as a GTK+ timeout func)   *
244
 
*************************************************/
245
 
static gboolean gtk_pulse_timeout(void* pbar)
246
 
   {
247
 
   GtkWidget* progress_bar = (GtkWidget*)pbar;
248
 
   gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress_bar));
249
 
   return TRUE; /* keep calling us */
250
 
   }
251
 
 
252
 
/*************************************************
253
 
* Generate a new key                             *
254
 
*************************************************/
255
 
static void gen_key()
256
 
   {
257
 
   /* This gives a nice smooth progress bar, though we do use up quite a bit of
258
 
      CPU for it. Keep in mind that if PULSE_INTERVAL is significantly less
259
 
      than the average time between pulses from the library, the progress bar
260
 
      will jerk around going slower or faster. Keep it at at least 50ms.
261
 
   */
262
 
   const double PROGRESS_PER_PULSE = .01; /* % of bar */
263
 
   const guint32 PULSE_INTERVAL = 30; /* ms */
264
 
 
265
 
   delete key;
266
 
 
267
 
   GtkDialogFlags flags =
268
 
      (GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL);
269
 
 
270
 
   GtkWidget* dialog =
271
 
      gtk_dialog_new_with_buttons("Generating Key", NULL, flags, NULL);
272
 
 
273
 
   GtkWidget* label = gtk_label_new(" Generating new key, please wait...  \n");
274
 
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
275
 
 
276
 
   GtkWidget* progress_bar = gtk_progress_bar_new();
277
 
   gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(progress_bar),
278
 
                                   PROGRESS_PER_PULSE);
279
 
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), progress_bar);
280
 
 
281
 
   guint timer_id = gtk_timeout_add(PULSE_INTERVAL, gtk_pulse_timeout,
282
 
                                    progress_bar);
283
 
 
284
 
   gtk_widget_show_all(dialog);
285
 
 
286
 
   while(gtk_events_pending())
287
 
      gtk_main_iteration();
288
 
 
289
 
   /* Register gtk_pulse so it will be called every so often when we embark
290
 
      on our prime generation quest...
291
 
   */
292
 
   /* this just updates the GUI; the GTK+ timeout function actually updates
293
 
      the progress bar. That's because the amount of time between pulses
294
 
      from the library is rather irregular, so the progress bar looks jerky.
295
 
   */
296
 
   Botan::global_state().set_ui(new GTK_Pulse);
297
 
 
298
 
   /* Not generally recommended, since it's slow and there's not much point.
299
 
      However, *because* it's slow, we'll want to put up a progress bar or
300
 
      something, and part of this whole thing is to show how to do that and get
301
 
      the pulse functions to do the right thing.
302
 
   */
303
 
   Botan::DL_Group group(1024, Botan::DL_Group::DSA_Kosherizer);
304
 
   key = new Botan::DSA_PrivateKey(group);
305
 
 
306
 
   gtk_timeout_remove(timer_id);
307
 
   Botan::global_state().set_ui(0); // unset the pulse function
308
 
 
309
 
   gtk_widget_destroy(dialog);
310
 
 
311
 
   do_save_key("Save New Key");
312
 
 
313
 
   /* new key, any old sigs are no longer useful */
314
 
   zap_sig();
315
 
   }
316
 
 
317
 
/*************************************************
318
 
* Load up a key                                  *
319
 
*************************************************/
320
 
static void get_key()
321
 
   {
322
 
   std::string fsname = get_filename("Select a DSA Key");
323
 
 
324
 
   if(fsname != "")
325
 
      {
326
 
      try {
327
 
         delete key;
328
 
         key = 0;
329
 
         zap_sig();
330
 
 
331
 
         /*
332
 
            A GTK_UI is a subclass of User_Interface that pops up a dialog that
333
 
            asks the user for a passphrase. It actually works quite well,
334
 
            though the fixed upper limit on the passphrase size is not
335
 
            ideal. Feel free to use it as-is or modify it however you like
336
 
            (gtk_ui.* is public domain).
337
 
         */
338
 
         GTK_UI ui;
339
 
         Botan::PKCS8_PrivateKey* p8_key = Botan::PKCS8::load_key(fsname, ui);
340
 
         key = dynamic_cast<Botan::DSA_PrivateKey*>(p8_key);
341
 
         if(!key)
342
 
            show_dialog("The key in " + fsname + " is not a DSA key",
343
 
                        "Failure");
344
 
      }
345
 
      catch(std::exception)
346
 
         {
347
 
         key = 0; // make sure it's not something random
348
 
         show_dialog("Loading the key from " + fsname + " failed.", "Failure");
349
 
         }
350
 
      }
351
 
   }
352
 
 
353
 
static void really_sign_buffer()
354
 
   {
355
 
   /* No key? Ask the user for one. */
356
 
   if(key == 0)
357
 
      get_key();
358
 
   sign_buffer();
359
 
   }
360
 
 
361
 
/*************************************************
362
 
* Clear the text buffer                          *
363
 
*************************************************/
364
 
static void new_buffer()
365
 
   {
366
 
   /*
367
 
      In theory, it would be nice to check if this was unsaved text and prompt
368
 
      to save it. However, this really isn't supposed to be a GTK+ example, so
369
 
      we won't.
370
 
   */
371
 
   gtk_text_buffer_set_text(buffer, "", -1);
372
 
   buffer_source = "";
373
 
   }
374
 
 
375
 
/*************************************************
376
 
* Put the contents of a file into the buffer     *
377
 
*************************************************/
378
 
static void open_buffer()
379
 
   {
380
 
   std::string filename = get_filename("Select File");
381
 
 
382
 
   if(filename == "")
383
 
      return;
384
 
 
385
 
   std::ifstream in(filename.c_str());
386
 
 
387
 
   new_buffer();
388
 
   buffer_source = filename;
389
 
 
390
 
   while(in.good())
391
 
      {
392
 
      char buf[1024] = { 0 };
393
 
 
394
 
      in.read(buf, 1024);
395
 
      size_t got = in.gcount();
396
 
 
397
 
      GtkTextIter iter;
398
 
      gtk_text_buffer_get_end_iter(buffer, &iter);
399
 
      gtk_text_buffer_insert(buffer, &iter, buf, got);
400
 
      }
401
 
   }
402
 
 
403
 
/*************************************************
404
 
* Save the signature to a file                   *
405
 
*************************************************/
406
 
static void save_sig()
407
 
   {
408
 
   std::string sig_file = buffer_source;
409
 
 
410
 
   /* No sig, nothing to save */
411
 
   const gchar* sig = gtk_entry_get_text(GTK_ENTRY(sig_view));
412
 
   if(strlen(sig) == 0)
413
 
      return;
414
 
 
415
 
   if(sig_file == "")
416
 
      sig_file = get_filename("Select Signature Output File");
417
 
   else
418
 
      sig_file += ".sig";
419
 
 
420
 
   std::ofstream out(sig_file.c_str());
421
 
   out << sig << std::endl;
422
 
   }
423
 
 
424
 
/*************************************************
425
 
* Save the current key                           *
426
 
*************************************************/
427
 
static void save_key()
428
 
   {
429
 
   do_save_key("Save Current Key");
430
 
   }
431
 
 
432
 
/*************************************************
433
 
* Common case of Save/Save As                    *
434
 
*************************************************/
435
 
static void do_save(const std::string& filename)
436
 
   {
437
 
   std::ofstream out(filename.c_str());
438
 
 
439
 
   GtkTextIter start, end;
440
 
   gtk_text_buffer_get_bounds(buffer, &start, &end);
441
 
   gchar* bits = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
442
 
   out.write(bits, strlen(bits));
443
 
   g_free(bits);
444
 
   buffer_source = filename;
445
 
   }
446
 
 
447
 
/*************************************************
448
 
* Save the buffer                                *
449
 
*************************************************/
450
 
static void save_buffer_as()
451
 
   {
452
 
   std::string filename = get_filename("Select Output File");
453
 
   if(filename != "")
454
 
      do_save(filename);
455
 
   }
456
 
 
457
 
/*************************************************
458
 
* Save the buffer                                *
459
 
*************************************************/
460
 
static void save_buffer()
461
 
   {
462
 
   if(buffer_source != "")
463
 
      do_save(buffer_source);
464
 
   else
465
 
      save_buffer_as();
466
 
   }
467
 
 
468
 
/*************************************************
469
 
* Make a menubar for the app                     *
470
 
*************************************************/
471
 
static GtkWidget* make_menubar(GtkWidget *window)
472
 
   {
473
 
   static GtkItemFactoryEntry menu_items[] = {
474
 
      { "/_File",          NULL,           NULL, 0, "<Branch>", NULL },
475
 
      { "/File/_New",      "<control>N",   new_buffer, 0, NULL, NULL },
476
 
      { "/File/_Open",     "<control>O",   open_buffer, 0, NULL, NULL },
477
 
      { "/File/_Save",     "<control>S",   save_buffer, 0, NULL, NULL },
478
 
      { "/File/Save _As",  NULL,           save_buffer_as, 0, NULL, NULL },
479
 
      { "/File/sep1",      NULL,           NULL, 0, "<Separator>", NULL },
480
 
      { "/File/Save Sig",  NULL,           save_sig, 0, NULL, NULL },
481
 
      { "/File/sep2",      NULL,           NULL, 0, "<Separator>", NULL },
482
 
      { "/File/_Quit",     "<control>Q",   gtk_main_quit, 0, NULL, NULL },
483
 
 
484
 
      { "/_Keys",          NULL,           NULL, 0, "<Branch>", NULL },
485
 
      { "/Keys/Open",      NULL,           get_key, 0, NULL, NULL },
486
 
      { "/Keys/_Generate", NULL,           gen_key, 0, NULL, NULL },
487
 
      { "/Keys/Save Current", NULL,        save_key, 0, NULL, NULL },
488
 
 
489
 
      { "/Signing",        NULL,           NULL, 0, "<Branch>", NULL },
490
 
      { "/Signing/Sign",   NULL,           really_sign_buffer, 0, NULL, NULL },
491
 
 
492
 
      { "/_Help",          NULL,           NULL, 0, "<LastBranch>", NULL },
493
 
      { "/Help/Help",      NULL,           show_help, 0, NULL, NULL },
494
 
      { "/Help/sep1",      NULL,           NULL, 0, "<Separator>", NULL },
495
 
      { "/Help/About",     NULL,           show_about, 0, NULL, NULL },
496
 
   };
497
 
 
498
 
   GtkAccelGroup* accel_group = gtk_accel_group_new();
499
 
   GtkItemFactory* item_factory =
500
 
      gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group);
501
 
   const gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
502
 
   gtk_item_factory_create_items(item_factory, nmenu_items, menu_items, NULL);
503
 
   gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
504
 
   return gtk_item_factory_get_widget(item_factory, "<main>");
505
 
   }
506
 
 
507
 
int main(int argc, char *argv[])
508
 
   {
509
 
   gtk_init(&argc, &argv);
510
 
 
511
 
   try {
512
 
      Botan::LibraryInitializer init;
513
 
 
514
 
      /* Create a new top-level window */
515
 
      GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
516
 
      gtk_window_set_title(GTK_WINDOW(window), "DSA Utility");
517
 
      gtk_signal_connect(GTK_OBJECT(window), "delete_event",
518
 
                         gtk_main_quit, NULL);
519
 
 
520
 
      /* Create the vbox to hold our stuff */
521
 
      GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
522
 
      gtk_container_border_width(GTK_CONTAINER(vbox), 1);
523
 
      gtk_container_add(GTK_CONTAINER(window), vbox);
524
 
 
525
 
      /* Create the menu bar */
526
 
      GtkWidget *menubar = make_menubar(window);
527
 
 
528
 
      /* Create the entry that holds the signature */
529
 
      sig_view = gtk_entry_new();
530
 
      gtk_editable_set_editable(GTK_EDITABLE(sig_view), FALSE);
531
 
 
532
 
      /* Create the text box */
533
 
      GtkWidget* view = gtk_text_view_new();
534
 
      buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
535
 
 
536
 
      gtk_widget_set_size_request(view, 640, 480);
537
 
      gtk_text_buffer_set_text(buffer, "Some initial contents.", -1);
538
 
 
539
 
      // Resign it on each change: fast enough, but probably not really useful
540
 
      //g_signal_connect(G_OBJECT(buffer), "changed", sign_buffer, 0);
541
 
      g_signal_connect(G_OBJECT(buffer), "changed", zap_sig, 0);
542
 
 
543
 
      gtk_container_add(GTK_CONTAINER(vbox), menubar);
544
 
      gtk_container_add(GTK_CONTAINER(vbox), view);
545
 
      gtk_container_add(GTK_CONTAINER(vbox), sig_view);
546
 
 
547
 
      gtk_widget_show_all(window);
548
 
 
549
 
      gtk_main();
550
 
   }
551
 
   catch(std::exception& e)
552
 
      {
553
 
      std::cout << e.what() << std::endl;
554
 
      }
555
 
   return 0;
556
 
   }