Reverting parts of r19760 that was mistakenly committed.
[kugel-rb.git] / apps / plugins / keybox.c
blob0e657f3bcc4b52f245e6061f8621ccf1d5b66711
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 Nils Wallménius
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "lib/md5.h"
23 PLUGIN_HEADER
26 /* FIXME: use "PLUGIN_APPS_DIR" */
27 #define KEYBOX_FILE PLUGIN_DIR "/apps/keybox.dat"
28 #define BLOCK_SIZE 8
29 #define MAX_ENTRIES 12*BLOCK_SIZE /* keep this a multiple of BLOCK_SIZE */
30 #define FIELD_LEN 32 /* should be enough for anyone ;) */
32 /* salt 4 bytes (needed for decryption) not encrypted padded with 4 bytes of zeroes
33 pwhash 16 bytes (to check for the right password) encrypted
34 encrypted data. */
36 #define HEADER_LEN 24
38 enum
40 FILE_OPEN_ERROR = -1
43 struct pw_entry
45 bool used;
46 char title[FIELD_LEN];
47 char name[FIELD_LEN];
48 char password[FIELD_LEN];
49 struct pw_entry *next;
52 struct pw_list
54 struct pw_entry first; /* always points to the first element in the list */
55 struct pw_entry entries[MAX_ENTRIES];
56 int num_entries;
57 } pw_list;
59 /* use this to access hashes in different ways, not byte order
60 independent but does it matter? */
61 union hash
63 uint8_t bytes[16];
64 uint32_t words[4];
67 static const struct plugin_api* rb;
68 MEM_FUNCTION_WRAPPERS(rb);
69 static char buffer[sizeof(struct pw_entry)*MAX_ENTRIES];
70 static int bytes_read = 0; /* bytes read into the buffer */
71 static struct gui_synclist kb_list;
72 static union hash key;
73 static char master_pw[FIELD_LEN];
74 static uint32_t salt;
75 static union hash pwhash;
76 static bool data_changed = false;
78 static int context_item_cb(int action, const struct menu_item_ex *this_item);
79 static void encrypt_buffer(char *buf, size_t size, uint32_t *key);
80 static void decrypt_buffer(char *buf, size_t size, uint32_t *key);
82 /* the following two functions are the reference TEA implementation by
83 David Wheeler and Roger Needham taken from
84 http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
86 static void encrypt(uint32_t* v, uint32_t* k)
88 uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
89 static const uint32_t delta=0x9e3779b9; /* a key schedule constant */
90 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
91 for (i=0; i < 32; i++) { /* basic cycle start */
92 sum += delta;
93 v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
94 v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); /* end cycle */
96 v[0]=v0; v[1]=v1;
99 static void decrypt(uint32_t* v, uint32_t* k)
101 uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
102 static const uint32_t delta=0x9e3779b9; /* a key schedule constant */
103 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
104 for (i=0; i<32; i++) { /* basic cycle start */
105 v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
106 v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
107 sum -= delta; /* end cycle */
109 v[0]=v0; v[1]=v1;
112 MENUITEM_RETURNVALUE(context_add_entry, "Add entry", 0,
113 NULL, Icon_NOICON);
114 MENUITEM_RETURNVALUE(context_edit_title, "Edit title", 1,
115 &context_item_cb, Icon_NOICON);
116 MENUITEM_RETURNVALUE(context_edit_name, "Edit user name", 2,
117 &context_item_cb, Icon_NOICON);
118 MENUITEM_RETURNVALUE(context_edit_password, "Edit password", 3,
119 &context_item_cb, Icon_NOICON);
120 MENUITEM_RETURNVALUE(context_delete_entry, "Delete entry", 4,
121 &context_item_cb, Icon_NOICON);
122 MENUITEM_RETURNVALUE(context_debug, "debug", 5,
123 &context_item_cb, Icon_NOICON);
125 MAKE_MENU(context_m, "Context menu",
126 context_item_cb, Icon_NOICON,
127 &context_add_entry, &context_edit_title, &context_edit_name,
128 &context_edit_password, &context_delete_entry);
130 static int context_item_cb(int action, const struct menu_item_ex *this_item)
132 if (action == ACTION_REQUEST_MENUITEM
133 && pw_list.num_entries == 0
134 && this_item != &context_add_entry)
136 return ACTION_EXIT_MENUITEM;
138 return action;
141 static char * kb_list_cb(int selected_item, void *data,
142 char *buffer, size_t buffer_len)
144 (void)data;
145 int i;
146 struct pw_entry *entry = pw_list.first.next;
147 for (i = 0; i < selected_item; i++)
149 if (entry)
150 entry = entry->next;
152 if (!entry)
153 return NULL;
155 rb->snprintf(buffer, buffer_len, "%s", entry->title);
157 return buffer;
160 static void init_ll(void)
162 pw_list.first.next = &pw_list.entries[0];
163 pw_list.entries[0].next = NULL;
164 pw_list.num_entries = 0;
167 static void delete_entry(int selected_item)
169 int i;
170 struct pw_entry *entry = &pw_list.first;
171 struct pw_entry *entry2;
173 /* find the entry before the one to delete */
174 for (i = 0; i < selected_item; i++)
176 if (entry->next)
177 entry = entry->next;
179 entry2 = entry->next;
180 if (!entry2)
181 return;
183 entry->next = entry2->next;
185 entry2->used = false;
186 entry2->name[0] = '\0';
187 entry2->password[0] = '\0';
188 entry2->next = NULL;
190 rb->gui_synclist_set_nb_items(&kb_list, --pw_list.num_entries);
191 data_changed = true;
194 static void add_entry(int selected_item)
196 int i, j;
197 struct pw_entry *entry = pw_list.first.next;
198 for (i = 0; i < MAX_ENTRIES && pw_list.entries[i].used; i++)
201 if (pw_list.entries[i].used)
203 rb->splash(HZ, "Password list full");
204 return;
207 rb->splash(HZ, "Enter title");
208 pw_list.entries[i].title[0] = '\0';
209 rb->kbd_input(pw_list.entries[i].title, FIELD_LEN);
210 rb->splash(HZ, "Enter name");
211 pw_list.entries[i].name[0] = '\0';
212 rb->kbd_input(pw_list.entries[i].name, FIELD_LEN);
213 rb->splash(HZ, "Enter password");
214 pw_list.entries[i].password[0] = '\0';
215 rb->kbd_input(pw_list.entries[i].password, FIELD_LEN);
217 for (j = 0; j < selected_item; j++)
219 if (entry->next)
220 entry = entry->next;
223 rb->gui_synclist_set_nb_items(&kb_list, ++pw_list.num_entries);
225 pw_list.entries[i].used = true;
226 pw_list.entries[i].next = entry->next;
228 entry->next = &pw_list.entries[i];
230 if (entry->next == entry)
231 entry->next = NULL;
233 data_changed = true;
236 static void edit_title(int selected_item)
238 int i;
239 struct pw_entry *entry = pw_list.first.next;
240 for (i = 0; i < selected_item; i++)
242 if (entry->next)
243 entry = entry->next;
245 if (rb->kbd_input(entry->title, FIELD_LEN) == 0)
246 data_changed = true;
249 static void edit_name(int selected_item)
251 int i;
252 struct pw_entry *entry = pw_list.first.next;
253 for (i = 0; i < selected_item; i++)
255 if (entry->next)
256 entry = entry->next;
258 if (rb->kbd_input(entry->name, FIELD_LEN) == 0)
259 data_changed = true;
262 static void edit_pw(int selected_item)
264 int i;
265 struct pw_entry *entry = pw_list.first.next;
266 for (i = 0; i < selected_item; i++)
268 if (entry->next)
269 entry = entry->next;
271 if (rb->kbd_input(entry->password, FIELD_LEN) == 0)
272 data_changed = true;
275 static void context_menu(int selected_item)
277 int selection, result;
278 bool exit = false;
280 do {
281 result = rb->do_menu(&context_m, &selection, NULL, false);
282 switch (result) {
283 case 0:
284 add_entry(selected_item);
285 return;
286 case 1:
287 edit_title(selected_item);
288 return;
289 case 2:
290 edit_name(selected_item);
291 return;
292 case 3:
293 edit_pw(selected_item);
294 return;
295 case 4:
296 delete_entry(selected_item);
297 return;
298 default:
299 exit = true;
300 break;
302 rb->yield();
303 } while (!exit);
306 static void splash_pw(int selected_item)
308 int i;
309 struct pw_entry *entry = pw_list.first.next;
311 for (i = 0; i < selected_item; i++)
313 if (entry->next)
314 entry = entry->next;
316 if (entry->name != '\0')
317 rb->splashf(0, "%s %s", entry->name, entry->password);
318 else
319 rb->splashf(0, "%s", entry->password);
320 rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK);
323 static void hash_pw(union hash *out)
325 int i;
326 struct md5_s pw_md5;
328 InitMD5(&pw_md5);
329 AddMD5(&pw_md5, master_pw, rb->strlen(master_pw));
330 EndMD5(&pw_md5);
332 for (i = 0; i < 4; i++)
333 out->words[i] = htole32(pw_md5.p_digest[i]);
336 static void make_key(void)
338 int i;
339 char buf[sizeof(master_pw) + sizeof(salt) + 1];
340 struct md5_s key_md5;
341 size_t len = rb->strlen(master_pw);
343 rb->strncpy(buf, master_pw, sizeof(buf));
345 rb->memcpy(&buf[len], &salt, sizeof(salt));
347 InitMD5(&key_md5);
348 AddMD5(&key_md5, buf, rb->strlen(buf));
349 EndMD5(&key_md5);
351 for (i = 0; i < 4; i++)
352 key.words[i] = key_md5.p_digest[i];
355 static void decrypt_buffer(char *buf, size_t size, uint32_t *key)
357 unsigned int i;
358 uint32_t block[2];
360 for (i = 0; i < size/BLOCK_SIZE; i++)
362 rb->memcpy(&block[0], &buf[i*BLOCK_SIZE], sizeof(block));
364 block[0] = letoh32(block[0]);
365 block[1] = letoh32(block[1]);
367 decrypt(&block[0], key);
369 /* byte swap one block */
370 block[0] = letoh32(block[0]);
371 block[1] = letoh32(block[1]);
373 rb->memcpy(&buf[i*BLOCK_SIZE], &block[0], sizeof(block));
377 static void encrypt_buffer(char *buf, size_t size, uint32_t *key)
379 unsigned int i;
380 uint32_t block[2];
382 for (i = 0; i < size/BLOCK_SIZE; i++)
384 rb->memcpy(&block[0], &buf[i*BLOCK_SIZE], sizeof(block));
386 /* byte swap one block */
387 block[0] = htole32(block[0]);
388 block[1] = htole32(block[1]);
390 encrypt(&block[0], key);
392 block[0] = htole32(block[0]);
393 block[1] = htole32(block[1]);
395 rb->memcpy(&buf[i*BLOCK_SIZE], &block[0], sizeof(block));
399 static int parse_buffer(void)
401 int i;
402 int len;
403 struct pw_entry *entry = pw_list.first.next;
404 char *start, *end;
405 start = &buffer[HEADER_LEN];
407 rb->memcpy(&salt, &buffer[0], sizeof(salt));
408 make_key();
410 decrypt_buffer(&buffer[8], bytes_read - 8, &key.words[0]);
412 if (rb->memcmp(&buffer[8], &pwhash, sizeof(union hash)))
414 rb->splash(HZ*2, "Wrong password");
415 return -1;
418 for (i = 0; i < MAX_ENTRIES; i++)
420 end = rb->strchr(start, '\0'); /* find eol */
421 len = end - &buffer[HEADER_LEN];
422 if ((len > bytes_read + HEADER_LEN) || start == end)
424 break;
427 rb->strncpy(entry->title, start, FIELD_LEN);
428 start = end + 1;
430 end = rb->strchr(start, '\0'); /* find eol */
431 len = end - &buffer[HEADER_LEN];
432 if (len > bytes_read + HEADER_LEN)
434 break;
437 rb->strncpy(entry->name, start, FIELD_LEN);
438 start = end + 1;
440 end = rb->strchr(start, '\0'); /* find eol */
441 len = end - &buffer[HEADER_LEN];
442 if (len > bytes_read + HEADER_LEN)
444 break;
446 rb->strncpy(entry->password, start, FIELD_LEN);
447 start = end + 1;
448 entry->used = true;
449 if (i + 1 < MAX_ENTRIES - 1)
451 entry->next = &pw_list.entries[i+1];
452 entry = entry->next;
454 else
456 break;
459 entry->next = NULL;
460 pw_list.num_entries = i;
461 rb->gui_synclist_set_nb_items(&kb_list, pw_list.num_entries);
462 return 0;
465 static void write_output(int fd)
467 size_t bytes_written;
468 int i;
469 size_t len, size;
470 char *p = &buffer[HEADER_LEN]; /* reserve space for salt + hash */
472 rb->memcpy(&buffer[8], &pwhash, sizeof(union hash));
473 struct pw_entry *entry = pw_list.first.next;
475 for (i = 0; i < pw_list.num_entries; i++)
477 len = rb->strlen(entry->title);
478 rb->strncpy(p, entry->title, len+1);
479 p += len+1;
480 len = rb->strlen(entry->name);
481 rb->strncpy(p, entry->name, len+1);
482 p += len+1;
483 len = rb->strlen(entry->password);
484 rb->strncpy(p, entry->password, len+1);
485 p += len+1;
486 if (entry->next)
487 entry = entry->next;
489 *p++ = '\0'; /* mark the end of the list */
491 /* round up to a number divisible by BLOCK_SIZE */
492 size = ((p - buffer + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
494 salt = rb->rand();
495 make_key();
497 encrypt_buffer(&buffer[8], size, &key.words[0]);
498 rb->memcpy(&buffer[0], &salt, sizeof(salt));
500 bytes_written = rb->write(fd, &buffer, size);
503 static int enter_pw(char *pw_buf, size_t buflen, bool new_pw)
505 char buf[2][sizeof(master_pw)];
506 rb->memset(buf, 0, sizeof(buf));
507 rb->memset(master_pw, 0, sizeof(master_pw));
509 if (new_pw)
511 rb->splash(HZ, "Enter new master password");
512 rb->kbd_input(buf[0], sizeof(buf[0]));
513 rb->splash(HZ, "Confirm master password");
514 rb->kbd_input(buf[1], sizeof(buf[1]));
516 if (rb->strcmp(buf[0], buf[1]))
518 rb->splash(HZ, "Password mismatch");
519 return -1;
521 else
523 rb->strncpy(pw_buf, buf[0], buflen);
524 hash_pw(&pwhash);
525 return 0;
529 rb->splash(HZ, "Enter master password");
530 if (rb->kbd_input(pw_buf, buflen))
531 return -1;
532 hash_pw(&pwhash);
533 return 0;
536 static int keybox(void)
538 int button, fd;
539 bool new_file = !rb->file_exists(KEYBOX_FILE);
540 bool done = false;
542 if (enter_pw(master_pw, sizeof (master_pw), new_file))
543 return 0;
545 /* Read the existing file */
546 if (!new_file)
548 fd = rb->open(KEYBOX_FILE, O_RDONLY);
549 if (fd < 0)
550 return FILE_OPEN_ERROR;
551 bytes_read = rb->read(fd, &buffer, sizeof(buffer));
553 if (parse_buffer())
554 return 0;
556 rb->close(fd);
559 while (!done)
561 rb->gui_synclist_draw(&kb_list);
562 button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
563 if (rb->gui_synclist_do_button(&kb_list, &button, LIST_WRAP_ON))
564 continue;
566 switch (button)
568 case ACTION_STD_OK:
569 splash_pw(rb->gui_synclist_get_sel_pos(&kb_list));
570 break;
571 case ACTION_STD_CONTEXT:
572 context_menu(rb->gui_synclist_get_sel_pos(&kb_list));
573 break;
574 case ACTION_STD_CANCEL:
575 done = true;
576 break;
578 rb->yield();
581 if (data_changed)
583 fd = rb->open(KEYBOX_FILE, O_WRONLY | O_CREAT | O_TRUNC);
584 if (fd < 0)
585 return FILE_OPEN_ERROR;
586 write_output(fd);
587 rb->close(fd);
590 return 0;
593 static void reset(void)
595 static const char *message_lines[]=
596 {"Do you really want", "to reset keybox?"};
597 static const char *yes_lines[]=
598 {"Keybox reset."};
599 static const struct text_message message={message_lines, 2};
600 static const struct text_message yes_message={yes_lines, 1};
602 if(rb->gui_syncyesno_run(&message, &yes_message, NULL) == YESNO_YES)
604 rb->remove(KEYBOX_FILE);
605 rb->memset(&buffer, 0, sizeof(buffer));
606 rb->memset(&pw_list, 0, sizeof(pw_list));
607 init_ll();
611 static int main_menu(void)
613 int selection, result, ret;
614 bool exit = false;
616 MENUITEM_STRINGLIST(menu,"Keybox", NULL, "Enter Keybox",
617 "Reset Keybox", "Exit");
619 do {
620 result = rb->do_menu(&menu, &selection, NULL, false);
621 switch (result) {
622 case 0:
623 ret = keybox();
624 if (ret)
625 return ret;
626 break;
627 case 1:
628 reset();
629 break;
630 case 2:
631 exit = true;
632 break;
634 rb->yield();
635 } while (!exit);
637 return 0;
640 enum plugin_status plugin_start(const struct plugin_api *api,
641 const void *parameter)
643 (void)parameter;
644 rb = api;
645 int ret;
647 rb->gui_synclist_init(&kb_list, &kb_list_cb, NULL, false, 1, NULL);
649 rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
650 rb->gui_synclist_set_icon_callback(&kb_list, NULL);
651 rb->gui_synclist_set_nb_items(&kb_list, 0);
652 rb->gui_synclist_limit_scroll(&kb_list, false);
653 rb->gui_synclist_select_item(&kb_list, 0);
655 md5_init(api);
657 init_ll();
658 ret = main_menu();
660 switch (ret)
662 case FILE_OPEN_ERROR:
663 rb->splash(HZ*2, "Error opening file");
664 return PLUGIN_ERROR;
667 return PLUGIN_OK;