Use the AMS_LOWMEM define to indicate memory size as the .lds files do in config...
[kugel-rb.git] / apps / plugins / keybox.c
blob5d625a33a2a6ca8a25ed8112912b45dc2b12f81c
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
25 #define KEYBOX_FILE PLUGIN_APPS_DIR "/keybox.dat"
26 #define BLOCK_SIZE 8
27 #define MAX_ENTRIES 12*BLOCK_SIZE /* keep this a multiple of BLOCK_SIZE */
28 #define FIELD_LEN 32 /* should be enough for anyone ;) */
30 /* The header begins with the unencrypted salt (4 bytes) padded with 4 bytes of
31 zeroes. After that comes the encrypted hash of the master password (16 bytes) */
34 #define HEADER_LEN 24
36 enum
38 FILE_OPEN_ERROR = -1
41 struct pw_entry
43 bool used;
44 char title[FIELD_LEN];
45 char name[FIELD_LEN];
46 char password[FIELD_LEN];
47 struct pw_entry *next;
50 struct pw_list
52 struct pw_entry first; /* always points to the first element in the list */
53 struct pw_entry entries[MAX_ENTRIES];
54 int num_entries;
55 } pw_list;
57 /* use this to access hashes in different ways, not byte order
58 independent but does it matter? */
59 union hash
61 uint8_t bytes[16];
62 uint32_t words[4];
65 static char buffer[sizeof(struct pw_entry)*MAX_ENTRIES];
66 static int bytes_read = 0; /* bytes read into the buffer */
67 static struct gui_synclist kb_list;
68 static union hash key;
69 static char master_pw[FIELD_LEN];
70 static uint32_t salt;
71 static union hash pwhash;
72 static bool data_changed = false;
74 static int context_item_cb(int action, const struct menu_item_ex *this_item);
75 static void encrypt_buffer(char *buf, size_t size, uint32_t *key);
76 static void decrypt_buffer(char *buf, size_t size, uint32_t *key);
78 /* the following two functions are the reference TEA implementation by
79 David Wheeler and Roger Needham taken from
80 http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
82 static void encrypt(uint32_t* v, uint32_t* k)
84 uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
85 static const uint32_t delta=0x9e3779b9; /* a key schedule constant */
86 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
87 for (i=0; i < 32; i++) { /* basic cycle start */
88 sum += delta;
89 v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
90 v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); /* end cycle */
92 v[0]=v0; v[1]=v1;
95 static void decrypt(uint32_t* v, uint32_t* k)
97 uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
98 static const uint32_t delta=0x9e3779b9; /* a key schedule constant */
99 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
100 for (i=0; i<32; i++) { /* basic cycle start */
101 v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
102 v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
103 sum -= delta; /* end cycle */
105 v[0]=v0; v[1]=v1;
108 MENUITEM_RETURNVALUE(context_add_entry, "Add entry", 0,
109 NULL, Icon_NOICON);
110 MENUITEM_RETURNVALUE(context_edit_title, "Edit title", 1,
111 &context_item_cb, Icon_NOICON);
112 MENUITEM_RETURNVALUE(context_edit_name, "Edit user name", 2,
113 &context_item_cb, Icon_NOICON);
114 MENUITEM_RETURNVALUE(context_edit_password, "Edit password", 3,
115 &context_item_cb, Icon_NOICON);
116 MENUITEM_RETURNVALUE(context_delete_entry, "Delete entry", 4,
117 &context_item_cb, Icon_NOICON);
118 MENUITEM_RETURNVALUE(context_debug, "debug", 5,
119 &context_item_cb, Icon_NOICON);
121 MAKE_MENU(context_m, "Context menu",
122 context_item_cb, Icon_NOICON,
123 &context_add_entry, &context_edit_title, &context_edit_name,
124 &context_edit_password, &context_delete_entry);
126 static int context_item_cb(int action, const struct menu_item_ex *this_item)
128 if (action == ACTION_REQUEST_MENUITEM
129 && pw_list.num_entries == 0
130 && this_item != &context_add_entry)
132 return ACTION_EXIT_MENUITEM;
134 return action;
137 static char * kb_list_cb(int selected_item, void *data,
138 char *buffer, size_t buffer_len)
140 (void)data;
141 int i;
142 struct pw_entry *entry = pw_list.first.next;
143 for (i = 0; i < selected_item; i++)
145 if (entry)
146 entry = entry->next;
148 if (!entry)
149 return NULL;
151 rb->snprintf(buffer, buffer_len, "%s", entry->title);
153 return buffer;
156 static void init_ll(void)
158 pw_list.first.next = &pw_list.entries[0];
159 pw_list.entries[0].next = NULL;
160 pw_list.num_entries = 0;
163 static void delete_entry(int selected_item)
165 int i;
166 struct pw_entry *entry = &pw_list.first;
167 struct pw_entry *entry2;
169 /* find the entry before the one to delete */
170 for (i = 0; i < selected_item; i++)
172 if (entry->next)
173 entry = entry->next;
175 entry2 = entry->next;
176 if (!entry2)
177 return;
179 entry->next = entry2->next;
181 entry2->used = false;
182 entry2->name[0] = '\0';
183 entry2->password[0] = '\0';
184 entry2->next = NULL;
186 rb->gui_synclist_set_nb_items(&kb_list, --pw_list.num_entries);
187 data_changed = true;
190 static void add_entry(int selected_item)
192 int i, j;
193 struct pw_entry *entry = pw_list.first.next;
194 for (i = 0; i < MAX_ENTRIES && pw_list.entries[i].used; i++)
197 if (pw_list.entries[i].used)
199 rb->splash(HZ, "Password list full");
200 return;
203 rb->splash(HZ, "Enter title");
204 pw_list.entries[i].title[0] = '\0';
205 if (rb->kbd_input(pw_list.entries[i].title, FIELD_LEN))
206 return;
208 rb->splash(HZ, "Enter name");
209 pw_list.entries[i].name[0] = '\0';
210 if (rb->kbd_input(pw_list.entries[i].name, FIELD_LEN))
212 pw_list.entries[i].title[0] = '\0';
213 return;
216 rb->splash(HZ, "Enter password");
217 pw_list.entries[i].password[0] = '\0';
218 if (rb->kbd_input(pw_list.entries[i].password, FIELD_LEN))
220 pw_list.entries[i].title[0] = '\0';
221 pw_list.entries[i].name[0] = '\0';
222 return;
225 for (j = 0; j < selected_item; j++)
227 if (entry->next)
228 entry = entry->next;
231 rb->gui_synclist_set_nb_items(&kb_list, ++pw_list.num_entries);
233 pw_list.entries[i].used = true;
234 pw_list.entries[i].next = entry->next;
236 entry->next = &pw_list.entries[i];
238 if (entry->next == entry)
239 entry->next = NULL;
241 data_changed = true;
244 static void edit_title(int selected_item)
246 int i;
247 struct pw_entry *entry = pw_list.first.next;
248 for (i = 0; i < selected_item; i++)
250 if (entry->next)
251 entry = entry->next;
253 if (rb->kbd_input(entry->title, FIELD_LEN) == 0)
254 data_changed = true;
257 static void edit_name(int selected_item)
259 int i;
260 struct pw_entry *entry = pw_list.first.next;
261 for (i = 0; i < selected_item; i++)
263 if (entry->next)
264 entry = entry->next;
266 if (rb->kbd_input(entry->name, FIELD_LEN) == 0)
267 data_changed = true;
270 static void edit_pw(int selected_item)
272 int i;
273 struct pw_entry *entry = pw_list.first.next;
274 for (i = 0; i < selected_item; i++)
276 if (entry->next)
277 entry = entry->next;
279 if (rb->kbd_input(entry->password, FIELD_LEN) == 0)
280 data_changed = true;
283 static void context_menu(int selected_item)
285 int selection, result;
286 bool exit = false;
288 do {
289 result = rb->do_menu(&context_m, &selection, NULL, false);
290 switch (result) {
291 case 0:
292 add_entry(selected_item);
293 return;
294 case 1:
295 edit_title(selected_item);
296 return;
297 case 2:
298 edit_name(selected_item);
299 return;
300 case 3:
301 edit_pw(selected_item);
302 return;
303 case 4:
304 delete_entry(selected_item);
305 return;
306 default:
307 exit = true;
308 break;
310 rb->yield();
311 } while (!exit);
314 static void splash_pw(int selected_item)
316 int i;
317 struct pw_entry *entry = pw_list.first.next;
319 for (i = 0; i < selected_item; i++)
321 if (entry->next)
322 entry = entry->next;
324 if (entry->name != '\0')
325 rb->splashf(0, "%s %s", entry->name, entry->password);
326 else
327 rb->splashf(0, "%s", entry->password);
328 rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK);
331 static void hash_pw(union hash *out)
333 int i;
334 struct md5_s pw_md5;
336 InitMD5(&pw_md5);
337 AddMD5(&pw_md5, master_pw, rb->strlen(master_pw));
338 EndMD5(&pw_md5);
340 for (i = 0; i < 4; i++)
341 out->words[i] = htole32(pw_md5.p_digest[i]);
344 static void make_key(void)
346 int i;
347 char buf[sizeof(master_pw) + sizeof(salt) + 1];
348 struct md5_s key_md5;
349 size_t len = rb->strlen(master_pw);
351 rb->strncpy(buf, master_pw, sizeof(buf));
353 rb->memcpy(&buf[len], &salt, sizeof(salt));
355 InitMD5(&key_md5);
356 AddMD5(&key_md5, buf, rb->strlen(buf));
357 EndMD5(&key_md5);
359 for (i = 0; i < 4; i++)
360 key.words[i] = key_md5.p_digest[i];
363 static void decrypt_buffer(char *buf, size_t size, uint32_t *key)
365 unsigned int i;
366 uint32_t block[2];
368 for (i = 0; i < size/BLOCK_SIZE; i++)
370 rb->memcpy(&block[0], &buf[i*BLOCK_SIZE], sizeof(block));
372 block[0] = letoh32(block[0]);
373 block[1] = letoh32(block[1]);
375 decrypt(&block[0], key);
377 /* byte swap one block */
378 block[0] = letoh32(block[0]);
379 block[1] = letoh32(block[1]);
381 rb->memcpy(&buf[i*BLOCK_SIZE], &block[0], sizeof(block));
385 static void encrypt_buffer(char *buf, size_t size, uint32_t *key)
387 unsigned int i;
388 uint32_t block[2];
390 for (i = 0; i < size/BLOCK_SIZE; i++)
392 rb->memcpy(&block[0], &buf[i*BLOCK_SIZE], sizeof(block));
394 /* byte swap one block */
395 block[0] = htole32(block[0]);
396 block[1] = htole32(block[1]);
398 encrypt(&block[0], key);
400 block[0] = htole32(block[0]);
401 block[1] = htole32(block[1]);
403 rb->memcpy(&buf[i*BLOCK_SIZE], &block[0], sizeof(block));
407 static int parse_buffer(void)
409 int i;
410 int len;
411 struct pw_entry *entry = pw_list.first.next;
412 char *start, *end;
413 start = &buffer[HEADER_LEN];
415 rb->memcpy(&salt, &buffer[0], sizeof(salt));
416 make_key();
418 decrypt_buffer(&buffer[8], bytes_read - 8, &key.words[0]);
420 if (rb->memcmp(&buffer[8], &pwhash, sizeof(union hash)))
422 rb->splash(HZ*2, "Wrong password");
423 return -1;
426 for (i = 0; i < MAX_ENTRIES; i++)
428 end = rb->strchr(start, '\0'); /* find eol */
429 len = end - &buffer[HEADER_LEN];
430 if ((len > bytes_read + HEADER_LEN) || start == end)
432 break;
435 rb->strncpy(entry->title, start, FIELD_LEN);
436 start = end + 1;
438 end = rb->strchr(start, '\0'); /* find eol */
439 len = end - &buffer[HEADER_LEN];
440 if (len > bytes_read + HEADER_LEN)
442 break;
445 rb->strncpy(entry->name, start, FIELD_LEN);
446 start = end + 1;
448 end = rb->strchr(start, '\0'); /* find eol */
449 len = end - &buffer[HEADER_LEN];
450 if (len > bytes_read + HEADER_LEN)
452 break;
454 rb->strncpy(entry->password, start, FIELD_LEN);
455 start = end + 1;
456 entry->used = true;
457 if (i + 1 < MAX_ENTRIES - 1)
459 entry->next = &pw_list.entries[i+1];
460 entry = entry->next;
462 else
464 break;
467 entry->next = NULL;
468 pw_list.num_entries = i;
469 rb->gui_synclist_set_nb_items(&kb_list, pw_list.num_entries);
470 return 0;
473 static void write_output(int fd)
475 size_t bytes_written;
476 int i;
477 size_t len, size;
478 char *p = &buffer[HEADER_LEN]; /* reserve space for salt + hash */
480 rb->memcpy(&buffer[8], &pwhash, sizeof(union hash));
481 struct pw_entry *entry = pw_list.first.next;
483 for (i = 0; i < pw_list.num_entries; i++)
485 len = rb->strlen(entry->title);
486 rb->strncpy(p, entry->title, len+1);
487 p += len+1;
488 len = rb->strlen(entry->name);
489 rb->strncpy(p, entry->name, len+1);
490 p += len+1;
491 len = rb->strlen(entry->password);
492 rb->strncpy(p, entry->password, len+1);
493 p += len+1;
494 if (entry->next)
495 entry = entry->next;
497 *p++ = '\0'; /* mark the end of the list */
499 /* round up to a number divisible by BLOCK_SIZE */
500 size = ((p - buffer + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
502 salt = rb->rand();
503 make_key();
505 encrypt_buffer(&buffer[8], size, &key.words[0]);
506 rb->memcpy(&buffer[0], &salt, sizeof(salt));
508 bytes_written = rb->write(fd, &buffer, size);
511 static int enter_pw(char *pw_buf, size_t buflen, bool new_pw)
513 char buf[2][sizeof(master_pw)];
514 rb->memset(buf, 0, sizeof(buf));
515 rb->memset(master_pw, 0, sizeof(master_pw));
517 if (new_pw)
519 rb->splash(HZ, "Enter new master password");
520 if (rb->kbd_input(buf[0], sizeof(buf[0])))
521 return -1;
523 rb->splash(HZ, "Confirm master password");
524 if (rb->kbd_input(buf[1], sizeof(buf[1])))
525 return -1;
527 if (rb->strcmp(buf[0], buf[1]))
529 rb->splash(HZ, "Password mismatch");
530 return -1;
532 else
534 rb->strncpy(pw_buf, buf[0], buflen);
535 hash_pw(&pwhash);
536 return 0;
540 rb->splash(HZ, "Enter master password");
541 if (rb->kbd_input(pw_buf, buflen))
542 return -1;
543 hash_pw(&pwhash);
544 return 0;
547 static int keybox(void)
549 int button, fd;
550 bool new_file = !rb->file_exists(KEYBOX_FILE);
551 bool done = false;
553 if (enter_pw(master_pw, sizeof (master_pw), new_file))
554 return 0;
556 /* Read the existing file */
557 if (!new_file)
559 fd = rb->open(KEYBOX_FILE, O_RDONLY);
560 if (fd < 0)
561 return FILE_OPEN_ERROR;
562 bytes_read = rb->read(fd, &buffer, sizeof(buffer));
564 if (parse_buffer())
565 return 0;
567 rb->close(fd);
570 while (!done)
572 rb->gui_synclist_draw(&kb_list);
573 button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
574 if (rb->gui_synclist_do_button(&kb_list, &button, LIST_WRAP_ON))
575 continue;
577 switch (button)
579 case ACTION_STD_OK:
580 splash_pw(rb->gui_synclist_get_sel_pos(&kb_list));
581 break;
582 case ACTION_STD_CONTEXT:
583 context_menu(rb->gui_synclist_get_sel_pos(&kb_list));
584 break;
585 case ACTION_STD_CANCEL:
586 done = true;
587 break;
589 rb->yield();
592 if (data_changed)
594 fd = rb->open(KEYBOX_FILE, O_WRONLY | O_CREAT | O_TRUNC);
595 if (fd < 0)
596 return FILE_OPEN_ERROR;
597 write_output(fd);
598 rb->close(fd);
601 return 0;
604 static void reset(void)
606 static const char *message_lines[]=
607 {"Do you really want", "to reset keybox?"};
608 static const char *yes_lines[]=
609 {"Keybox reset."};
610 static const struct text_message message={message_lines, 2};
611 static const struct text_message yes_message={yes_lines, 1};
613 if(rb->gui_syncyesno_run(&message, &yes_message, NULL) == YESNO_YES)
615 rb->remove(KEYBOX_FILE);
616 rb->memset(&buffer, 0, sizeof(buffer));
617 rb->memset(&pw_list, 0, sizeof(pw_list));
618 init_ll();
622 static int main_menu(void)
624 int selection, result, ret;
625 bool exit = false;
627 MENUITEM_STRINGLIST(menu,"Keybox", NULL, "Enter Keybox",
628 "Reset Keybox", "Exit");
630 do {
631 result = rb->do_menu(&menu, &selection, NULL, false);
632 switch (result) {
633 case 0:
634 ret = keybox();
635 if (ret)
636 return ret;
637 break;
638 case 1:
639 reset();
640 break;
641 case 2:
642 exit = true;
643 break;
645 rb->yield();
646 } while (!exit);
648 return 0;
651 enum plugin_status plugin_start(const void *parameter)
653 (void)parameter;
654 int ret;
656 rb->gui_synclist_init(&kb_list, &kb_list_cb, NULL, false, 1, NULL);
658 rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
659 rb->gui_synclist_set_icon_callback(&kb_list, NULL);
660 rb->gui_synclist_set_nb_items(&kb_list, 0);
661 rb->gui_synclist_limit_scroll(&kb_list, false);
662 rb->gui_synclist_select_item(&kb_list, 0);
664 init_ll();
665 ret = main_menu();
667 switch (ret)
669 case FILE_OPEN_ERROR:
670 rb->splash(HZ*2, "Error opening file");
671 return PLUGIN_ERROR;
674 return PLUGIN_OK;