1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
22 #include "lib/playback_control.h"
26 #define KEYBOX_FILE PLUGIN_APPS_DIR "/keybox.dat"
28 #define MAX_ENTRIES 12*BLOCK_SIZE /* keep this a multiple of BLOCK_SIZE */
29 #define FIELD_LEN 32 /* should be enough for anyone ;) */
31 /* The header begins with the unencrypted salt (4 bytes) padded with 4 bytes of
32 zeroes. After that comes the encrypted hash of the master password (16 bytes) */
44 char title
[FIELD_LEN
];
46 char password
[FIELD_LEN
];
47 struct pw_entry
*next
;
52 struct pw_entry first
; /* always points to the first element in the list */
53 struct pw_entry entries
[MAX_ENTRIES
];
57 /* use this to access hashes in different ways, not byte order
58 independent but does it matter? */
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
];
71 static union hash pwhash
;
72 static bool data_changed
= false;
74 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
);
75 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
);
77 /* the following two functions are the reference TEA implementation by
78 David Wheeler and Roger Needham taken from
79 http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
81 static void encrypt(uint32_t* v
, uint32_t* k
)
83 uint32_t v0
=v
[0], v1
=v
[1], sum
=0, i
; /* set up */
84 static const uint32_t delta
=0x9e3779b9; /* a key schedule constant */
85 uint32_t k0
=k
[0], k1
=k
[1], k2
=k
[2], k3
=k
[3]; /* cache key */
86 for (i
=0; i
< 32; i
++) { /* basic cycle start */
88 v0
+= ((v1
<<4) + k0
) ^ (v1
+ sum
) ^ ((v1
>>5) + k1
);
89 v1
+= ((v0
<<4) + k2
) ^ (v0
+ sum
) ^ ((v0
>>5) + k3
); /* end cycle */
94 static void decrypt(uint32_t* v
, uint32_t* k
)
96 uint32_t v0
=v
[0], v1
=v
[1], sum
=0xC6EF3720, i
; /* set up */
97 static const uint32_t delta
=0x9e3779b9; /* a key schedule constant */
98 uint32_t k0
=k
[0], k1
=k
[1], k2
=k
[2], k3
=k
[3]; /* cache key */
99 for (i
=0; i
<32; i
++) { /* basic cycle start */
100 v1
-= ((v0
<<4) + k2
) ^ (v0
+ sum
) ^ ((v0
>>5) + k3
);
101 v0
-= ((v1
<<4) + k0
) ^ (v1
+ sum
) ^ ((v1
>>5) + k1
);
102 sum
-= delta
; /* end cycle */
107 static int context_item_cb(int action
, const struct menu_item_ex
*this_item
)
109 int i
= (intptr_t)this_item
;
110 if (action
== ACTION_REQUEST_MENUITEM
111 && pw_list
.num_entries
== 0
112 && (i
!= 0 && i
!= 5))
114 return ACTION_EXIT_MENUITEM
;
119 MENUITEM_STRINGLIST(context_m
, "Context menu", context_item_cb
,
121 "Edit title", "Edit user name", "Edit password",
125 static const char* kb_list_cb(int selected_item
, void *data
,
126 char *buffer
, size_t buffer_len
)
130 struct pw_entry
*entry
= pw_list
.first
.next
;
131 for (i
= 0; i
< selected_item
; i
++)
139 rb
->snprintf(buffer
, buffer_len
, "%s", entry
->title
);
144 static void init_ll(void)
146 pw_list
.first
.next
= &pw_list
.entries
[0];
147 pw_list
.entries
[0].next
= NULL
;
148 pw_list
.num_entries
= 0;
151 static void delete_entry(int selected_item
)
154 struct pw_entry
*entry
= &pw_list
.first
;
155 struct pw_entry
*entry2
;
157 /* find the entry before the one to delete */
158 for (i
= 0; i
< selected_item
; i
++)
163 entry2
= entry
->next
;
167 entry
->next
= entry2
->next
;
169 entry2
->used
= false;
170 entry2
->name
[0] = '\0';
171 entry2
->password
[0] = '\0';
174 rb
->gui_synclist_set_nb_items(&kb_list
, --pw_list
.num_entries
);
178 static void add_entry(int selected_item
)
181 struct pw_entry
*entry
= pw_list
.first
.next
;
182 for (i
= 0; i
< MAX_ENTRIES
&& pw_list
.entries
[i
].used
; i
++)
185 if (MAX_ENTRIES
== i
)
187 rb
->splash(HZ
, "Password list full");
191 rb
->splash(HZ
, "Enter title");
192 pw_list
.entries
[i
].title
[0] = '\0';
193 if (rb
->kbd_input(pw_list
.entries
[i
].title
, FIELD_LEN
) < 0)
196 rb
->splash(HZ
, "Enter name");
197 pw_list
.entries
[i
].name
[0] = '\0';
198 if (rb
->kbd_input(pw_list
.entries
[i
].name
, FIELD_LEN
) < 0)
200 pw_list
.entries
[i
].title
[0] = '\0';
204 rb
->splash(HZ
, "Enter password");
205 pw_list
.entries
[i
].password
[0] = '\0';
206 if (rb
->kbd_input(pw_list
.entries
[i
].password
, FIELD_LEN
) < 0)
208 pw_list
.entries
[i
].title
[0] = '\0';
209 pw_list
.entries
[i
].name
[0] = '\0';
213 for (j
= 0; j
< selected_item
; j
++)
219 rb
->gui_synclist_set_nb_items(&kb_list
, ++pw_list
.num_entries
);
221 pw_list
.entries
[i
].used
= true;
222 pw_list
.entries
[i
].next
= entry
->next
;
224 entry
->next
= &pw_list
.entries
[i
];
226 if (entry
->next
== entry
)
232 static void edit_title(int selected_item
)
235 struct pw_entry
*entry
= pw_list
.first
.next
;
236 for (i
= 0; i
< selected_item
; i
++)
241 if (rb
->kbd_input(entry
->title
, FIELD_LEN
) == 0)
245 static void edit_name(int selected_item
)
248 struct pw_entry
*entry
= pw_list
.first
.next
;
249 for (i
= 0; i
< selected_item
; i
++)
254 if (rb
->kbd_input(entry
->name
, FIELD_LEN
) == 0)
258 static void edit_pw(int selected_item
)
261 struct pw_entry
*entry
= pw_list
.first
.next
;
262 for (i
= 0; i
< selected_item
; i
++)
267 if (rb
->kbd_input(entry
->password
, FIELD_LEN
) == 0)
271 static void context_menu(int selected_item
)
273 int selection
, result
;
277 result
= rb
->do_menu(&context_m
, &selection
, NULL
, false);
280 add_entry(selected_item
);
283 edit_title(selected_item
);
286 edit_name(selected_item
);
289 edit_pw(selected_item
);
292 delete_entry(selected_item
);
295 playback_control(NULL
);
305 static void splash_pw(int selected_item
)
308 struct pw_entry
*entry
= pw_list
.first
.next
;
310 for (i
= 0; i
< selected_item
; i
++)
315 if (entry
->name
!= '\0')
316 rb
->splashf(0, "%s %s", entry
->name
, entry
->password
);
318 rb
->splashf(0, "%s", entry
->password
);
319 rb
->get_action(CONTEXT_STD
, TIMEOUT_BLOCK
);
322 static void hash_pw(union hash
*out
)
328 AddMD5(&pw_md5
, master_pw
, rb
->strlen(master_pw
));
331 for (i
= 0; i
< 4; i
++)
332 out
->words
[i
] = htole32(pw_md5
.p_digest
[i
]);
335 static void make_key(void)
338 char buf
[sizeof(master_pw
) + sizeof(salt
) + 1] = {0};
339 struct md5_s key_md5
;
340 size_t len
= rb
->strlen(master_pw
);
342 rb
->strlcpy(buf
, master_pw
, sizeof(buf
));
344 rb
->memcpy(&buf
[len
], &salt
, sizeof(salt
));
347 AddMD5(&key_md5
, buf
, rb
->strlen(buf
));
350 for (i
= 0; i
< 4; i
++)
351 key
.words
[i
] = key_md5
.p_digest
[i
];
354 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
359 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
361 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
363 block
[0] = letoh32(block
[0]);
364 block
[1] = letoh32(block
[1]);
366 decrypt(&block
[0], key
);
368 /* byte swap one block */
369 block
[0] = letoh32(block
[0]);
370 block
[1] = letoh32(block
[1]);
372 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
376 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
381 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
383 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
385 /* byte swap one block */
386 block
[0] = htole32(block
[0]);
387 block
[1] = htole32(block
[1]);
389 encrypt(&block
[0], key
);
391 block
[0] = htole32(block
[0]);
392 block
[1] = htole32(block
[1]);
394 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
398 static int parse_buffer(void)
402 struct pw_entry
*entry
= pw_list
.first
.next
;
404 start
= &buffer
[HEADER_LEN
];
406 rb
->memcpy(&salt
, &buffer
[0], sizeof(salt
));
409 decrypt_buffer(&buffer
[8], bytes_read
- 8, &key
.words
[0]);
411 if (rb
->memcmp(&buffer
[8], &pwhash
, sizeof(union hash
)))
413 rb
->splash(HZ
*2, "Wrong password");
417 for (i
= 0; i
< MAX_ENTRIES
; i
++)
419 end
= rb
->strchr(start
, '\0'); /* find eol */
420 len
= end
- &buffer
[HEADER_LEN
];
421 if ((len
> bytes_read
+ HEADER_LEN
) || start
== end
)
426 rb
->strlcpy(entry
->title
, start
, FIELD_LEN
);
429 end
= rb
->strchr(start
, '\0'); /* find eol */
430 len
= end
- &buffer
[HEADER_LEN
];
431 if (len
> bytes_read
+ HEADER_LEN
)
436 rb
->strlcpy(entry
->name
, start
, FIELD_LEN
);
439 end
= rb
->strchr(start
, '\0'); /* find eol */
440 len
= end
- &buffer
[HEADER_LEN
];
441 if (len
> bytes_read
+ HEADER_LEN
)
445 rb
->strlcpy(entry
->password
, start
, FIELD_LEN
);
448 if (i
+ 1 < MAX_ENTRIES
- 1)
450 entry
->next
= &pw_list
.entries
[i
+1];
459 pw_list
.num_entries
= i
;
460 rb
->gui_synclist_set_nb_items(&kb_list
, pw_list
.num_entries
);
464 static void write_output(int fd
)
466 size_t bytes_written
;
469 char *p
= &buffer
[HEADER_LEN
]; /* reserve space for salt + hash */
471 rb
->memcpy(&buffer
[8], &pwhash
, sizeof(union hash
));
472 struct pw_entry
*entry
= pw_list
.first
.next
;
474 for (i
= 0; i
< pw_list
.num_entries
; i
++)
476 len
= rb
->strlen(entry
->title
);
477 rb
->strlcpy(p
, entry
->title
, len
+1);
479 len
= rb
->strlen(entry
->name
);
480 rb
->strlcpy(p
, entry
->name
, len
+1);
482 len
= rb
->strlen(entry
->password
);
483 rb
->strlcpy(p
, entry
->password
, len
+1);
488 *p
++ = '\0'; /* mark the end of the list */
490 /* round up to a number divisible by BLOCK_SIZE */
491 size
= ((p
- buffer
+ BLOCK_SIZE
- 1) / BLOCK_SIZE
) * BLOCK_SIZE
;
496 encrypt_buffer(&buffer
[8], size
, &key
.words
[0]);
497 rb
->memcpy(&buffer
[0], &salt
, sizeof(salt
));
499 bytes_written
= rb
->write(fd
, &buffer
, size
);
502 static int enter_pw(char *pw_buf
, size_t buflen
, bool new_pw
)
504 char buf
[2][sizeof(master_pw
)];
505 rb
->memset(buf
, 0, sizeof(buf
));
506 rb
->memset(master_pw
, 0, sizeof(master_pw
));
510 rb
->splash(HZ
, "Enter new master password");
511 if (rb
->kbd_input(buf
[0], sizeof(buf
[0])) < 0)
514 rb
->splash(HZ
, "Confirm master password");
515 if (rb
->kbd_input(buf
[1], sizeof(buf
[1])) < 0)
518 if (rb
->strcmp(buf
[0], buf
[1]))
520 rb
->splash(HZ
, "Password mismatch");
525 rb
->strlcpy(pw_buf
, buf
[0], buflen
);
531 rb
->splash(HZ
, "Enter master password");
532 if (rb
->kbd_input(pw_buf
, buflen
) < 0)
538 static int keybox(void)
541 bool new_file
= !rb
->file_exists(KEYBOX_FILE
);
544 if (enter_pw(master_pw
, sizeof (master_pw
), new_file
))
547 /* Read the existing file */
550 fd
= rb
->open(KEYBOX_FILE
, O_RDONLY
);
552 return FILE_OPEN_ERROR
;
553 bytes_read
= rb
->read(fd
, &buffer
, sizeof(buffer
));
563 rb
->gui_synclist_draw(&kb_list
);
564 button
= rb
->get_action(CONTEXT_LIST
, TIMEOUT_BLOCK
);
565 if (rb
->gui_synclist_do_button(&kb_list
, &button
, LIST_WRAP_ON
))
571 splash_pw(rb
->gui_synclist_get_sel_pos(&kb_list
));
573 case ACTION_STD_CONTEXT
:
574 context_menu(rb
->gui_synclist_get_sel_pos(&kb_list
));
576 case ACTION_STD_CANCEL
:
585 fd
= rb
->open(KEYBOX_FILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
587 return FILE_OPEN_ERROR
;
595 static void reset(void)
597 static const char *message_lines
[]=
598 {"Do you really want", "to reset keybox?"};
599 static const char *yes_lines
[]=
601 static const struct text_message message
={message_lines
, 2};
602 static const struct text_message yes_message
={yes_lines
, 1};
604 if(rb
->gui_syncyesno_run(&message
, &yes_message
, NULL
) == YESNO_YES
)
606 rb
->remove(KEYBOX_FILE
);
607 rb
->memset(&buffer
, 0, sizeof(buffer
));
608 rb
->memset(&pw_list
, 0, sizeof(pw_list
));
613 static int main_menu(void)
615 int selection
, result
, ret
;
618 MENUITEM_STRINGLIST(menu
, "Keybox", NULL
,
619 "Enter Keybox", "Reset Keybox",
620 "Playback Control", "Exit");
623 result
= rb
->do_menu(&menu
, &selection
, NULL
, false);
634 playback_control(NULL
);
646 enum plugin_status
plugin_start(const void *parameter
)
651 rb
->gui_synclist_init(&kb_list
, &kb_list_cb
, NULL
, false, 1, NULL
);
653 rb
->gui_synclist_set_title(&kb_list
, "Keybox", NOICON
);
654 rb
->gui_synclist_set_icon_callback(&kb_list
, NULL
);
655 rb
->gui_synclist_set_nb_items(&kb_list
, 0);
656 rb
->gui_synclist_limit_scroll(&kb_list
, false);
657 rb
->gui_synclist_select_item(&kb_list
, 0);
664 case FILE_OPEN_ERROR
:
665 rb
->splash(HZ
*2, "Error opening file");