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.)
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():
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
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.
27
This program is in the public domain.
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).
44
/*************************************************
45
* Pop up an message box *
46
*************************************************/
47
static void show_dialog(const std::string& about_message,
48
const std::string& dialog_name)
50
GtkDialogFlags flags =
51
(GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL);
53
GtkWidget* dialog = gtk_dialog_new_with_buttons(dialog_name.c_str(),
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);
62
g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
63
G_CALLBACK(gtk_widget_destroy),
66
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
67
gtk_widget_show_all(dialog);
70
/*************************************************
71
* Pop up an About box *
72
*************************************************/
73
static void show_about()
75
const std::string about_message =
76
"<big>DSA Utility</big>\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"
81
"Send comments/questions to <tt>lloyd@randombit.net</tt>";
83
show_dialog(about_message, "About");
86
/*************************************************
88
*************************************************/
89
static void show_help()
91
const std::string help_message =
92
"<big>DSA Utility Help</big>\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"
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"
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";
108
show_dialog(help_message, "Help");
111
/*************************************************
112
* Get and return a filename from the user *
113
*************************************************/
114
static std::string get_filename(const std::string& title)
116
GtkWidget* dialog = gtk_file_selection_new(title.c_str());
118
/* Some window managers don't display the titles of transient windows,
119
put a message elsewhere for those people.
121
GtkWidget* label = gtk_label_new(title.c_str());
122
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
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);
131
/* If it's a directory, that's no good */
132
if(fsname.size() && fsname[fsname.size()-1] == '/')
138
/*************************************************
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
147
/*************************************************
148
* Zap the currently set signature (if any) *
149
*************************************************/
150
static void zap_sig()
152
gtk_editable_delete_text(GTK_EDITABLE(sig_view), 0, -1);
155
/*************************************************
156
* Save the current key *
157
*************************************************/
158
static void do_save_key(const std::string& title)
163
std::string filename = get_filename(title.c_str());
167
const std::string msg = "Select a passphrase to encrypt the key:";
169
std::ofstream out_priv(filename.c_str());
172
Botan::User_Interface::UI_Result result;
173
std::string passphrase = ui.get_passphrase(msg, result);
175
if(result == Botan::User_Interface::OK)
176
out_priv << Botan::PKCS8::PEM_encode(*key, passphrase);
178
out_priv << Botan::PKCS8::PEM_encode(*key);
181
//std::cout << X509::PEM_encode(*key);
185
/*************************************************
186
* Generate a signature for the text buffer *
187
*************************************************/
188
static void sign_buffer()
190
/* No key? Ignore request. */
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)")
198
new Botan::Base64_Encoder
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);
208
pipe.write((unsigned char*)bits, length);
211
std::string sig = pipe.read_all_as_string();
216
gtk_editable_insert_text(GTK_EDITABLE(sig_view), sig.c_str(), sig.length(),
222
/*************************************************
223
* GTK+ pulse callback *
224
*************************************************/
225
class GTK_Pulse : public Botan::Library_State::UI
228
void pulse(Botan::Pulse_Type);
231
void GTK_Pulse::pulse(Botan::Pulse_Type)
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).
238
while(gtk_events_pending())
239
gtk_main_iteration();
242
/*************************************************
243
* Actual do the pulse (as a GTK+ timeout func) *
244
*************************************************/
245
static gboolean gtk_pulse_timeout(void* pbar)
247
GtkWidget* progress_bar = (GtkWidget*)pbar;
248
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress_bar));
249
return TRUE; /* keep calling us */
252
/*************************************************
253
* Generate a new key *
254
*************************************************/
255
static void gen_key()
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.
262
const double PROGRESS_PER_PULSE = .01; /* % of bar */
263
const guint32 PULSE_INTERVAL = 30; /* ms */
267
GtkDialogFlags flags =
268
(GtkDialogFlags)(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL);
271
gtk_dialog_new_with_buttons("Generating Key", NULL, flags, NULL);
273
GtkWidget* label = gtk_label_new(" Generating new key, please wait... \n");
274
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
276
GtkWidget* progress_bar = gtk_progress_bar_new();
277
gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(progress_bar),
279
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), progress_bar);
281
guint timer_id = gtk_timeout_add(PULSE_INTERVAL, gtk_pulse_timeout,
284
gtk_widget_show_all(dialog);
286
while(gtk_events_pending())
287
gtk_main_iteration();
289
/* Register gtk_pulse so it will be called every so often when we embark
290
on our prime generation quest...
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.
296
Botan::global_state().set_ui(new GTK_Pulse);
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.
303
Botan::DL_Group group(1024, Botan::DL_Group::DSA_Kosherizer);
304
key = new Botan::DSA_PrivateKey(group);
306
gtk_timeout_remove(timer_id);
307
Botan::global_state().set_ui(0); // unset the pulse function
309
gtk_widget_destroy(dialog);
311
do_save_key("Save New Key");
313
/* new key, any old sigs are no longer useful */
317
/*************************************************
319
*************************************************/
320
static void get_key()
322
std::string fsname = get_filename("Select a DSA Key");
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).
339
Botan::PKCS8_PrivateKey* p8_key = Botan::PKCS8::load_key(fsname, ui);
340
key = dynamic_cast<Botan::DSA_PrivateKey*>(p8_key);
342
show_dialog("The key in " + fsname + " is not a DSA key",
345
catch(std::exception)
347
key = 0; // make sure it's not something random
348
show_dialog("Loading the key from " + fsname + " failed.", "Failure");
353
static void really_sign_buffer()
355
/* No key? Ask the user for one. */
361
/*************************************************
362
* Clear the text buffer *
363
*************************************************/
364
static void new_buffer()
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
371
gtk_text_buffer_set_text(buffer, "", -1);
375
/*************************************************
376
* Put the contents of a file into the buffer *
377
*************************************************/
378
static void open_buffer()
380
std::string filename = get_filename("Select File");
385
std::ifstream in(filename.c_str());
388
buffer_source = filename;
392
char buf[1024] = { 0 };
395
size_t got = in.gcount();
398
gtk_text_buffer_get_end_iter(buffer, &iter);
399
gtk_text_buffer_insert(buffer, &iter, buf, got);
403
/*************************************************
404
* Save the signature to a file *
405
*************************************************/
406
static void save_sig()
408
std::string sig_file = buffer_source;
410
/* No sig, nothing to save */
411
const gchar* sig = gtk_entry_get_text(GTK_ENTRY(sig_view));
416
sig_file = get_filename("Select Signature Output File");
420
std::ofstream out(sig_file.c_str());
421
out << sig << std::endl;
424
/*************************************************
425
* Save the current key *
426
*************************************************/
427
static void save_key()
429
do_save_key("Save Current Key");
432
/*************************************************
433
* Common case of Save/Save As *
434
*************************************************/
435
static void do_save(const std::string& filename)
437
std::ofstream out(filename.c_str());
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));
444
buffer_source = filename;
447
/*************************************************
449
*************************************************/
450
static void save_buffer_as()
452
std::string filename = get_filename("Select Output File");
457
/*************************************************
459
*************************************************/
460
static void save_buffer()
462
if(buffer_source != "")
463
do_save(buffer_source);
468
/*************************************************
469
* Make a menubar for the app *
470
*************************************************/
471
static GtkWidget* make_menubar(GtkWidget *window)
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 },
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 },
489
{ "/Signing", NULL, NULL, 0, "<Branch>", NULL },
490
{ "/Signing/Sign", NULL, really_sign_buffer, 0, NULL, NULL },
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 },
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>");
507
int main(int argc, char *argv[])
509
gtk_init(&argc, &argv);
512
Botan::LibraryInitializer init;
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);
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);
525
/* Create the menu bar */
526
GtkWidget *menubar = make_menubar(window);
528
/* Create the entry that holds the signature */
529
sig_view = gtk_entry_new();
530
gtk_editable_set_editable(GTK_EDITABLE(sig_view), FALSE);
532
/* Create the text box */
533
GtkWidget* view = gtk_text_view_new();
534
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
536
gtk_widget_set_size_request(view, 640, 480);
537
gtk_text_buffer_set_text(buffer, "Some initial contents.", -1);
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);
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);
547
gtk_widget_show_all(window);
551
catch(std::exception& e)
553
std::cout << e.what() << std::endl;