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
);
175 if(!pw_list
.num_entries
)
180 static void add_entry(int selected_item
)
183 struct pw_entry
*entry
= pw_list
.first
.next
;
184 for (i
= 0; i
< MAX_ENTRIES
&& pw_list
.entries
[i
].used
; i
++)
187 if (MAX_ENTRIES
== i
)
189 rb
->splash(HZ
, "Password list full");
193 rb
->splash(HZ
, "Enter title");
194 pw_list
.entries
[i
].title
[0] = '\0';
195 if (rb
->kbd_input(pw_list
.entries
[i
].title
, FIELD_LEN
) < 0)
198 rb
->splash(HZ
, "Enter name");
199 pw_list
.entries
[i
].name
[0] = '\0';
200 if (rb
->kbd_input(pw_list
.entries
[i
].name
, FIELD_LEN
) < 0)
202 pw_list
.entries
[i
].title
[0] = '\0';
206 rb
->splash(HZ
, "Enter password");
207 pw_list
.entries
[i
].password
[0] = '\0';
208 if (rb
->kbd_input(pw_list
.entries
[i
].password
, FIELD_LEN
) < 0)
210 pw_list
.entries
[i
].title
[0] = '\0';
211 pw_list
.entries
[i
].name
[0] = '\0';
215 for (j
= 0; j
< selected_item
; j
++)
221 rb
->gui_synclist_set_nb_items(&kb_list
, ++pw_list
.num_entries
);
223 pw_list
.entries
[i
].used
= true;
224 pw_list
.entries
[i
].next
= entry
->next
;
226 entry
->next
= &pw_list
.entries
[i
];
228 if (entry
->next
== entry
)
234 static void edit_title(int selected_item
)
237 struct pw_entry
*entry
= pw_list
.first
.next
;
238 for (i
= 0; i
< selected_item
; i
++)
243 if (rb
->kbd_input(entry
->title
, FIELD_LEN
) == 0)
247 static void edit_name(int selected_item
)
250 struct pw_entry
*entry
= pw_list
.first
.next
;
251 for (i
= 0; i
< selected_item
; i
++)
256 if (rb
->kbd_input(entry
->name
, FIELD_LEN
) == 0)
260 static void edit_pw(int selected_item
)
263 struct pw_entry
*entry
= pw_list
.first
.next
;
264 for (i
= 0; i
< selected_item
; i
++)
269 if (rb
->kbd_input(entry
->password
, FIELD_LEN
) == 0)
273 static void context_menu(int selected_item
)
275 int selection
= 0, result
;
279 result
= rb
->do_menu(&context_m
, &selection
, NULL
, false);
282 add_entry(selected_item
);
285 edit_title(selected_item
);
288 edit_name(selected_item
);
291 edit_pw(selected_item
);
294 delete_entry(selected_item
);
297 playback_control(NULL
);
307 static void splash_pw(int selected_item
)
310 struct pw_entry
*entry
= pw_list
.first
.next
;
312 for (i
= 0; i
< selected_item
; i
++)
317 if (entry
->name
!= '\0')
318 rb
->splashf(0, "%s %s", entry
->name
, entry
->password
);
320 rb
->splashf(0, "%s", entry
->password
);
321 rb
->get_action(CONTEXT_STD
, TIMEOUT_BLOCK
);
324 static void hash_pw(union hash
*out
)
330 AddMD5(&pw_md5
, master_pw
, rb
->strlen(master_pw
));
333 for (i
= 0; i
< 4; i
++)
334 out
->words
[i
] = htole32(pw_md5
.p_digest
[i
]);
337 static void make_key(void)
340 char buf
[sizeof(master_pw
) + sizeof(salt
) + 1] = {0};
341 struct md5_s key_md5
;
342 size_t len
= rb
->strlen(master_pw
);
344 rb
->strlcpy(buf
, master_pw
, sizeof(buf
));
346 rb
->memcpy(&buf
[len
], &salt
, sizeof(salt
));
349 AddMD5(&key_md5
, buf
, rb
->strlen(buf
));
352 for (i
= 0; i
< 4; i
++)
353 key
.words
[i
] = key_md5
.p_digest
[i
];
356 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
361 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
363 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
365 block
[0] = letoh32(block
[0]);
366 block
[1] = letoh32(block
[1]);
368 decrypt(&block
[0], key
);
370 /* byte swap one block */
371 block
[0] = letoh32(block
[0]);
372 block
[1] = letoh32(block
[1]);
374 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
378 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
383 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
385 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
387 /* byte swap one block */
388 block
[0] = htole32(block
[0]);
389 block
[1] = htole32(block
[1]);
391 encrypt(&block
[0], key
);
393 block
[0] = htole32(block
[0]);
394 block
[1] = htole32(block
[1]);
396 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
400 static int parse_buffer(void)
404 struct pw_entry
*entry
= pw_list
.first
.next
;
406 start
= &buffer
[HEADER_LEN
];
408 rb
->memcpy(&salt
, &buffer
[0], sizeof(salt
));
411 decrypt_buffer(&buffer
[8], bytes_read
- 8, &key
.words
[0]);
413 if (rb
->memcmp(&buffer
[8], &pwhash
, sizeof(union hash
)))
415 rb
->splash(HZ
*2, "Wrong password");
419 for (i
= 0; i
< MAX_ENTRIES
; i
++)
421 end
= rb
->strchr(start
, '\0'); /* find eol */
422 len
= end
- &buffer
[HEADER_LEN
];
423 if ((len
> bytes_read
+ HEADER_LEN
) || start
== end
)
428 rb
->strlcpy(entry
->title
, start
, FIELD_LEN
);
431 end
= rb
->strchr(start
, '\0'); /* find eol */
432 len
= end
- &buffer
[HEADER_LEN
];
433 if (len
> bytes_read
+ HEADER_LEN
)
438 rb
->strlcpy(entry
->name
, start
, FIELD_LEN
);
441 end
= rb
->strchr(start
, '\0'); /* find eol */
442 len
= end
- &buffer
[HEADER_LEN
];
443 if (len
> bytes_read
+ HEADER_LEN
)
447 rb
->strlcpy(entry
->password
, start
, FIELD_LEN
);
450 if (i
+ 1 < MAX_ENTRIES
- 1)
452 entry
->next
= &pw_list
.entries
[i
+1];
461 pw_list
.num_entries
= i
;
462 rb
->gui_synclist_set_nb_items(&kb_list
, pw_list
.num_entries
);
466 static void write_output(int fd
)
468 size_t bytes_written
;
471 char *p
= &buffer
[HEADER_LEN
]; /* reserve space for salt + hash */
473 rb
->memcpy(&buffer
[8], &pwhash
, sizeof(union hash
));
474 struct pw_entry
*entry
= pw_list
.first
.next
;
476 for (i
= 0; i
< pw_list
.num_entries
; i
++)
478 len
= rb
->strlen(entry
->title
);
479 rb
->strlcpy(p
, entry
->title
, len
+1);
481 len
= rb
->strlen(entry
->name
);
482 rb
->strlcpy(p
, entry
->name
, len
+1);
484 len
= rb
->strlen(entry
->password
);
485 rb
->strlcpy(p
, entry
->password
, len
+1);
490 *p
++ = '\0'; /* mark the end of the list */
492 /* round up to a number divisible by BLOCK_SIZE */
493 size
= ((p
- buffer
+ BLOCK_SIZE
- 1) / BLOCK_SIZE
) * BLOCK_SIZE
;
498 encrypt_buffer(&buffer
[8], size
, &key
.words
[0]);
499 rb
->memcpy(&buffer
[0], &salt
, sizeof(salt
));
501 bytes_written
= rb
->write(fd
, &buffer
, size
);
504 static int enter_pw(char *pw_buf
, size_t buflen
, bool new_pw
)
506 char buf
[2][sizeof(master_pw
)];
507 rb
->memset(buf
, 0, sizeof(buf
));
508 rb
->memset(master_pw
, 0, sizeof(master_pw
));
512 rb
->splash(HZ
, "Enter new master password");
513 if (rb
->kbd_input(buf
[0], sizeof(buf
[0])) < 0)
516 rb
->splash(HZ
, "Confirm master password");
517 if (rb
->kbd_input(buf
[1], sizeof(buf
[1])) < 0)
520 if (rb
->strcmp(buf
[0], buf
[1]))
522 rb
->splash(HZ
, "Password mismatch");
527 rb
->strlcpy(pw_buf
, buf
[0], buflen
);
533 rb
->splash(HZ
, "Enter master password");
534 if (rb
->kbd_input(pw_buf
, buflen
) < 0)
540 static int keybox(void)
543 bool new_file
= !rb
->file_exists(KEYBOX_FILE
);
546 if (enter_pw(master_pw
, sizeof (master_pw
), new_file
))
549 /* Read the existing file */
552 fd
= rb
->open(KEYBOX_FILE
, O_RDONLY
);
554 return FILE_OPEN_ERROR
;
555 bytes_read
= rb
->read(fd
, &buffer
, sizeof(buffer
));
565 rb
->gui_synclist_draw(&kb_list
);
566 button
= rb
->get_action(CONTEXT_LIST
, TIMEOUT_BLOCK
);
567 if (rb
->gui_synclist_do_button(&kb_list
, &button
, LIST_WRAP_ON
))
573 splash_pw(rb
->gui_synclist_get_sel_pos(&kb_list
));
575 case ACTION_STD_CONTEXT
:
576 context_menu(rb
->gui_synclist_get_sel_pos(&kb_list
));
578 case ACTION_STD_CANCEL
:
587 fd
= rb
->open(KEYBOX_FILE
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
589 return FILE_OPEN_ERROR
;
597 static void reset(void)
599 static const char *message_lines
[]=
600 {"Do you really want", "to reset keybox?"};
601 static const char *yes_lines
[]=
603 static const struct text_message message
={message_lines
, 2};
604 static const struct text_message yes_message
={yes_lines
, 1};
606 if(rb
->gui_syncyesno_run(&message
, &yes_message
, NULL
) == YESNO_YES
)
608 rb
->remove(KEYBOX_FILE
);
609 rb
->memset(&buffer
, 0, sizeof(buffer
));
610 rb
->memset(&pw_list
, 0, sizeof(pw_list
));
611 rb
->gui_synclist_set_nb_items(&kb_list
, 0);
616 static int main_menu(void)
618 int selection
= 0, result
, ret
;
621 MENUITEM_STRINGLIST(menu
, "Keybox", NULL
,
622 "Enter Keybox", "Reset Keybox",
623 "Playback Control", "Exit");
626 result
= rb
->do_menu(&menu
, &selection
, NULL
, false);
637 playback_control(NULL
);
649 enum plugin_status
plugin_start(const void *parameter
)
654 rb
->gui_synclist_init(&kb_list
, &kb_list_cb
, NULL
, false, 1, NULL
);
656 rb
->gui_synclist_set_title(&kb_list
, "Keybox", NOICON
);
657 rb
->gui_synclist_set_icon_callback(&kb_list
, NULL
);
658 rb
->gui_synclist_set_nb_items(&kb_list
, 0);
659 rb
->gui_synclist_limit_scroll(&kb_list
, false);
660 rb
->gui_synclist_select_item(&kb_list
, 0);
667 case FILE_OPEN_ERROR
:
668 rb
->splash(HZ
*2, "Error opening file");