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 ****************************************************************************/
25 #define KEYBOX_FILE PLUGIN_DIR "/apps/keybox.dat"
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 /* salt 4 bytes (needed for decryption) not encrypted padded with 4 bytes of zeroes
31 pwhash 16 bytes (to check for the right password) encrypted
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 const struct plugin_api
* rb
;
66 MEM_FUNCTION_WRAPPERS(rb
);
67 static char buffer
[sizeof(struct pw_entry
)*MAX_ENTRIES
];
68 static int bytes_read
= 0; /* bytes read into the buffer */
69 static struct gui_synclist kb_list
;
70 static union hash key
;
71 static char master_pw
[FIELD_LEN
];
73 static union hash pwhash
;
74 static bool data_changed
= false;
76 static int context_item_cb(int action
, const struct menu_item_ex
*this_item
);
77 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
);
78 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
);
80 /* the following two functions are the reference TEA implementation by
81 David Wheeler and Roger Needham taken from
82 http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
84 static void encrypt(uint32_t* v
, uint32_t* k
)
86 uint32_t v0
=v
[0], v1
=v
[1], sum
=0, i
; /* set up */
87 static const uint32_t delta
=0x9e3779b9; /* a key schedule constant */
88 uint32_t k0
=k
[0], k1
=k
[1], k2
=k
[2], k3
=k
[3]; /* cache key */
89 for (i
=0; i
< 32; i
++) { /* basic cycle start */
91 v0
+= ((v1
<<4) + k0
) ^ (v1
+ sum
) ^ ((v1
>>5) + k1
);
92 v1
+= ((v0
<<4) + k2
) ^ (v0
+ sum
) ^ ((v0
>>5) + k3
); /* end cycle */
97 static void decrypt(uint32_t* v
, uint32_t* k
)
99 uint32_t v0
=v
[0], v1
=v
[1], sum
=0xC6EF3720, i
; /* set up */
100 static const uint32_t delta
=0x9e3779b9; /* a key schedule constant */
101 uint32_t k0
=k
[0], k1
=k
[1], k2
=k
[2], k3
=k
[3]; /* cache key */
102 for (i
=0; i
<32; i
++) { /* basic cycle start */
103 v1
-= ((v0
<<4) + k2
) ^ (v0
+ sum
) ^ ((v0
>>5) + k3
);
104 v0
-= ((v1
<<4) + k0
) ^ (v1
+ sum
) ^ ((v1
>>5) + k1
);
105 sum
-= delta
; /* end cycle */
110 MENUITEM_RETURNVALUE(context_add_entry
, "Add entry", 0,
112 MENUITEM_RETURNVALUE(context_edit_title
, "Edit title", 1,
113 &context_item_cb
, Icon_NOICON
);
114 MENUITEM_RETURNVALUE(context_edit_name
, "Edit user name", 2,
115 &context_item_cb
, Icon_NOICON
);
116 MENUITEM_RETURNVALUE(context_edit_password
, "Edit password", 3,
117 &context_item_cb
, Icon_NOICON
);
118 MENUITEM_RETURNVALUE(context_delete_entry
, "Delete entry", 4,
119 &context_item_cb
, Icon_NOICON
);
120 MENUITEM_RETURNVALUE(context_debug
, "debug", 5,
121 &context_item_cb
, Icon_NOICON
);
123 MAKE_MENU(context_m
, "Context menu",
124 context_item_cb
, Icon_NOICON
,
125 &context_add_entry
, &context_edit_title
, &context_edit_name
,
126 &context_edit_password
, &context_delete_entry
);
128 static int context_item_cb(int action
, const struct menu_item_ex
*this_item
)
130 if (action
== ACTION_REQUEST_MENUITEM
131 && pw_list
.num_entries
== 0
132 && this_item
!= &context_add_entry
)
134 return ACTION_EXIT_MENUITEM
;
139 static char * kb_list_cb(int selected_item
, void *data
,
140 char *buffer
, size_t buffer_len
)
144 struct pw_entry
*entry
= pw_list
.first
.next
;
145 for (i
= 0; i
< selected_item
; i
++)
153 rb
->snprintf(buffer
, buffer_len
, "%s", entry
->title
);
158 static void init_ll(void)
160 pw_list
.first
.next
= &pw_list
.entries
[0];
161 pw_list
.entries
[0].next
= NULL
;
162 pw_list
.num_entries
= 0;
165 static void delete_entry(int selected_item
)
168 struct pw_entry
*entry
= &pw_list
.first
;
169 struct pw_entry
*entry2
;
171 /* find the entry before the one to delete */
172 for (i
= 0; i
< selected_item
; i
++)
177 entry2
= entry
->next
;
181 entry
->next
= entry2
->next
;
183 entry2
->used
= false;
184 entry2
->name
[0] = '\0';
185 entry2
->password
[0] = '\0';
188 rb
->gui_synclist_set_nb_items(&kb_list
, --pw_list
.num_entries
);
192 static void add_entry(int selected_item
)
195 struct pw_entry
*entry
= pw_list
.first
.next
;
196 for (i
= 0; i
< MAX_ENTRIES
&& pw_list
.entries
[i
].used
; i
++)
199 if (pw_list
.entries
[i
].used
)
201 rb
->splash(HZ
, "Password list full");
205 rb
->splash(HZ
, "Enter title");
206 pw_list
.entries
[i
].title
[0] = '\0';
207 rb
->kbd_input(pw_list
.entries
[i
].title
, FIELD_LEN
);
208 rb
->splash(HZ
, "Enter name");
209 pw_list
.entries
[i
].name
[0] = '\0';
210 rb
->kbd_input(pw_list
.entries
[i
].name
, FIELD_LEN
);
211 rb
->splash(HZ
, "Enter password");
212 pw_list
.entries
[i
].password
[0] = '\0';
213 rb
->kbd_input(pw_list
.entries
[i
].password
, FIELD_LEN
);
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
, 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
);
304 static void splash_pw(int selected_item
)
307 struct pw_entry
*entry
= pw_list
.first
.next
;
309 for (i
= 0; i
< selected_item
; i
++)
314 if (entry
->name
!= '\0')
315 rb
->splash(0, "%s %s", entry
->name
, entry
->password
);
317 rb
->splash(0, "%s", entry
->password
);
318 rb
->get_action(CONTEXT_STD
, TIMEOUT_BLOCK
);
321 static void hash_pw(union hash
*out
)
327 AddMD5(&pw_md5
, master_pw
, rb
->strlen(master_pw
));
330 for (i
= 0; i
< 4; i
++)
331 out
->words
[i
] = htole32(pw_md5
.p_digest
[i
]);
334 static void make_key(void)
337 char buf
[sizeof(master_pw
) + sizeof(salt
) + 1];
338 struct md5_s key_md5
;
339 size_t len
= rb
->strlen(master_pw
);
341 rb
->strncpy(buf
, master_pw
, sizeof(buf
));
343 rb
->memcpy(&buf
[len
], &salt
, sizeof(salt
));
346 AddMD5(&key_md5
, buf
, rb
->strlen(buf
));
349 for (i
= 0; i
< 4; i
++)
350 key
.words
[i
] = key_md5
.p_digest
[i
];
353 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
358 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
360 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
362 block
[0] = letoh32(block
[0]);
363 block
[1] = letoh32(block
[1]);
365 decrypt(&block
[0], key
);
367 /* byte swap one block */
368 block
[0] = letoh32(block
[0]);
369 block
[1] = letoh32(block
[1]);
371 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
375 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
380 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
382 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
384 /* byte swap one block */
385 block
[0] = htole32(block
[0]);
386 block
[1] = htole32(block
[1]);
388 encrypt(&block
[0], key
);
390 block
[0] = htole32(block
[0]);
391 block
[1] = htole32(block
[1]);
393 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
397 static int parse_buffer(void)
401 struct pw_entry
*entry
= pw_list
.first
.next
;
403 start
= &buffer
[HEADER_LEN
];
405 rb
->memcpy(&salt
, &buffer
[0], sizeof(salt
));
408 decrypt_buffer(&buffer
[8], bytes_read
- 8, &key
.words
[0]);
410 if (rb
->memcmp(&buffer
[8], &pwhash
, sizeof(union hash
)))
412 rb
->splash(HZ
*2, "Wrong password");
416 for (i
= 0; i
< MAX_ENTRIES
; i
++)
418 end
= rb
->strchr(start
, '\0'); /* find eol */
419 len
= end
- &buffer
[HEADER_LEN
];
420 if ((len
> bytes_read
+ HEADER_LEN
) || start
== end
)
425 rb
->strncpy(entry
->title
, start
, FIELD_LEN
);
428 end
= rb
->strchr(start
, '\0'); /* find eol */
429 len
= end
- &buffer
[HEADER_LEN
];
430 if (len
> bytes_read
+ HEADER_LEN
)
435 rb
->strncpy(entry
->name
, start
, FIELD_LEN
);
438 end
= rb
->strchr(start
, '\0'); /* find eol */
439 len
= end
- &buffer
[HEADER_LEN
];
440 if (len
> bytes_read
+ HEADER_LEN
)
444 rb
->strncpy(entry
->password
, start
, FIELD_LEN
);
447 if (i
+ 1 < MAX_ENTRIES
- 1)
449 entry
->next
= &pw_list
.entries
[i
+1];
458 pw_list
.num_entries
= i
;
459 rb
->gui_synclist_set_nb_items(&kb_list
, pw_list
.num_entries
);
463 static void write_output(int fd
)
465 size_t bytes_written
;
468 char *p
= &buffer
[HEADER_LEN
]; /* reserve space for salt + hash */
470 rb
->memcpy(&buffer
[8], &pwhash
, sizeof(union hash
));
471 struct pw_entry
*entry
= pw_list
.first
.next
;
473 for (i
= 0; i
< pw_list
.num_entries
; i
++)
475 len
= rb
->strlen(entry
->title
);
476 rb
->strncpy(p
, entry
->title
, len
+1);
478 len
= rb
->strlen(entry
->name
);
479 rb
->strncpy(p
, entry
->name
, len
+1);
481 len
= rb
->strlen(entry
->password
);
482 rb
->strncpy(p
, entry
->password
, len
+1);
487 *p
++ = '\0'; /* mark the end of the list */
489 /* round up to a number divisible by BLOCK_SIZE */
490 size
= ((p
- buffer
+ BLOCK_SIZE
- 1) / BLOCK_SIZE
) * BLOCK_SIZE
;
495 encrypt_buffer(&buffer
[8], size
, &key
.words
[0]);
496 rb
->memcpy(&buffer
[0], &salt
, sizeof(salt
));
498 bytes_written
= rb
->write(fd
, &buffer
, size
);
501 static int enter_pw(char *pw_buf
, size_t buflen
, bool new_pw
)
503 char buf
[2][sizeof(master_pw
)];
504 rb
->memset(buf
, 0, sizeof(buf
));
505 rb
->memset(master_pw
, 0, sizeof(master_pw
));
509 rb
->splash(HZ
, "Enter new master password");
510 rb
->kbd_input(buf
[0], sizeof(buf
[0]));
511 rb
->splash(HZ
, "Confirm master password");
512 rb
->kbd_input(buf
[1], sizeof(buf
[1]));
514 if (rb
->strcmp(buf
[0], buf
[1]))
516 rb
->splash(HZ
, "Password mismatch");
521 rb
->strncpy(pw_buf
, buf
[0], buflen
);
527 rb
->splash(HZ
, "Enter master password");
528 if (rb
->kbd_input(pw_buf
, buflen
))
534 static int keybox(void)
537 bool new_file
= !rb
->file_exists(KEYBOX_FILE
);
540 if (enter_pw(master_pw
, sizeof (master_pw
), new_file
))
543 /* Read the existing file */
546 fd
= rb
->open(KEYBOX_FILE
, O_RDONLY
);
548 return FILE_OPEN_ERROR
;
549 bytes_read
= rb
->read(fd
, &buffer
, sizeof(buffer
));
559 rb
->gui_syncstatusbar_draw(rb
->statusbars
, true);
560 rb
->gui_synclist_draw(&kb_list
);
561 button
= rb
->get_action(CONTEXT_LIST
, TIMEOUT_BLOCK
);
562 if (rb
->gui_synclist_do_button(&kb_list
, &button
, LIST_WRAP_ON
))
568 splash_pw(rb
->gui_synclist_get_sel_pos(&kb_list
));
570 case ACTION_STD_CONTEXT
:
571 context_menu(rb
->gui_synclist_get_sel_pos(&kb_list
));
573 case ACTION_STD_CANCEL
:
582 fd
= rb
->open(KEYBOX_FILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
584 return FILE_OPEN_ERROR
;
592 static void reset(void)
594 static const char *message_lines
[]=
595 {"Do you really want", "to reset keybox?"};
596 static const char *yes_lines
[]=
598 static const struct text_message message
={message_lines
, 2};
599 static const struct text_message yes_message
={yes_lines
, 1};
601 if(rb
->gui_syncyesno_run(&message
, &yes_message
, NULL
) == YESNO_YES
)
603 rb
->remove(KEYBOX_FILE
);
604 rb
->memset(&buffer
, 0, sizeof(buffer
));
605 rb
->memset(&pw_list
, 0, sizeof(pw_list
));
610 static int main_menu(void)
612 int selection
, result
, ret
;
615 MENUITEM_STRINGLIST(menu
,"Keybox", NULL
, "Enter Keybox",
616 "Reset Keybox", "Exit");
619 result
= rb
->do_menu(&menu
, &selection
, NULL
, false);
639 enum plugin_status
plugin_start(const struct plugin_api
*api
,
640 const void *parameter
)
646 rb
->gui_synclist_init(&kb_list
, &kb_list_cb
, NULL
, false, 1, NULL
);
648 rb
->gui_synclist_set_title(&kb_list
, "Keybox", NOICON
);
649 rb
->gui_synclist_set_icon_callback(&kb_list
, NULL
);
650 rb
->gui_synclist_set_nb_items(&kb_list
, 0);
651 rb
->gui_synclist_limit_scroll(&kb_list
, false);
652 rb
->gui_synclist_select_item(&kb_list
, 0);
661 case FILE_OPEN_ERROR
:
662 rb
->splash(HZ
*2, "Error opening file");