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_APPS_DIR "/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 /* 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) */
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 if (action
== ACTION_REQUEST_MENUITEM
110 && pw_list
.num_entries
== 0
111 && ((intptr_t)this_item
) != 0)
113 return ACTION_EXIT_MENUITEM
;
118 MENUITEM_STRINGLIST(context_m
, "Context menu", context_item_cb
,
120 "Edit title", "Edit user name", "Edit password",
123 static char * kb_list_cb(int selected_item
, void *data
,
124 char *buffer
, size_t buffer_len
)
128 struct pw_entry
*entry
= pw_list
.first
.next
;
129 for (i
= 0; i
< selected_item
; i
++)
137 rb
->snprintf(buffer
, buffer_len
, "%s", entry
->title
);
142 static void init_ll(void)
144 pw_list
.first
.next
= &pw_list
.entries
[0];
145 pw_list
.entries
[0].next
= NULL
;
146 pw_list
.num_entries
= 0;
149 static void delete_entry(int selected_item
)
152 struct pw_entry
*entry
= &pw_list
.first
;
153 struct pw_entry
*entry2
;
155 /* find the entry before the one to delete */
156 for (i
= 0; i
< selected_item
; i
++)
161 entry2
= entry
->next
;
165 entry
->next
= entry2
->next
;
167 entry2
->used
= false;
168 entry2
->name
[0] = '\0';
169 entry2
->password
[0] = '\0';
172 rb
->gui_synclist_set_nb_items(&kb_list
, --pw_list
.num_entries
);
176 static void add_entry(int selected_item
)
179 struct pw_entry
*entry
= pw_list
.first
.next
;
180 for (i
= 0; i
< MAX_ENTRIES
&& pw_list
.entries
[i
].used
; i
++)
183 if (pw_list
.entries
[i
].used
)
185 rb
->splash(HZ
, "Password list full");
189 rb
->splash(HZ
, "Enter title");
190 pw_list
.entries
[i
].title
[0] = '\0';
191 if (rb
->kbd_input(pw_list
.entries
[i
].title
, FIELD_LEN
))
194 rb
->splash(HZ
, "Enter name");
195 pw_list
.entries
[i
].name
[0] = '\0';
196 if (rb
->kbd_input(pw_list
.entries
[i
].name
, FIELD_LEN
))
198 pw_list
.entries
[i
].title
[0] = '\0';
202 rb
->splash(HZ
, "Enter password");
203 pw_list
.entries
[i
].password
[0] = '\0';
204 if (rb
->kbd_input(pw_list
.entries
[i
].password
, FIELD_LEN
))
206 pw_list
.entries
[i
].title
[0] = '\0';
207 pw_list
.entries
[i
].name
[0] = '\0';
211 for (j
= 0; j
< selected_item
; j
++)
217 rb
->gui_synclist_set_nb_items(&kb_list
, ++pw_list
.num_entries
);
219 pw_list
.entries
[i
].used
= true;
220 pw_list
.entries
[i
].next
= entry
->next
;
222 entry
->next
= &pw_list
.entries
[i
];
224 if (entry
->next
== entry
)
230 static void edit_title(int selected_item
)
233 struct pw_entry
*entry
= pw_list
.first
.next
;
234 for (i
= 0; i
< selected_item
; i
++)
239 if (rb
->kbd_input(entry
->title
, FIELD_LEN
) == 0)
243 static void edit_name(int selected_item
)
246 struct pw_entry
*entry
= pw_list
.first
.next
;
247 for (i
= 0; i
< selected_item
; i
++)
252 if (rb
->kbd_input(entry
->name
, FIELD_LEN
) == 0)
256 static void edit_pw(int selected_item
)
259 struct pw_entry
*entry
= pw_list
.first
.next
;
260 for (i
= 0; i
< selected_item
; i
++)
265 if (rb
->kbd_input(entry
->password
, FIELD_LEN
) == 0)
269 static void context_menu(int selected_item
)
271 int selection
, result
;
275 result
= rb
->do_menu(&context_m
, &selection
, NULL
, false);
278 add_entry(selected_item
);
281 edit_title(selected_item
);
284 edit_name(selected_item
);
287 edit_pw(selected_item
);
290 delete_entry(selected_item
);
300 static void splash_pw(int selected_item
)
303 struct pw_entry
*entry
= pw_list
.first
.next
;
305 for (i
= 0; i
< selected_item
; i
++)
310 if (entry
->name
!= '\0')
311 rb
->splashf(0, "%s %s", entry
->name
, entry
->password
);
313 rb
->splashf(0, "%s", entry
->password
);
314 rb
->get_action(CONTEXT_STD
, TIMEOUT_BLOCK
);
317 static void hash_pw(union hash
*out
)
323 AddMD5(&pw_md5
, master_pw
, rb
->strlen(master_pw
));
326 for (i
= 0; i
< 4; i
++)
327 out
->words
[i
] = htole32(pw_md5
.p_digest
[i
]);
330 static void make_key(void)
333 char buf
[sizeof(master_pw
) + sizeof(salt
) + 1];
334 struct md5_s key_md5
;
335 size_t len
= rb
->strlen(master_pw
);
337 rb
->strncpy(buf
, master_pw
, sizeof(buf
));
339 rb
->memcpy(&buf
[len
], &salt
, sizeof(salt
));
342 AddMD5(&key_md5
, buf
, rb
->strlen(buf
));
345 for (i
= 0; i
< 4; i
++)
346 key
.words
[i
] = key_md5
.p_digest
[i
];
349 static void decrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
354 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
356 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
358 block
[0] = letoh32(block
[0]);
359 block
[1] = letoh32(block
[1]);
361 decrypt(&block
[0], key
);
363 /* byte swap one block */
364 block
[0] = letoh32(block
[0]);
365 block
[1] = letoh32(block
[1]);
367 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
371 static void encrypt_buffer(char *buf
, size_t size
, uint32_t *key
)
376 for (i
= 0; i
< size
/BLOCK_SIZE
; i
++)
378 rb
->memcpy(&block
[0], &buf
[i
*BLOCK_SIZE
], sizeof(block
));
380 /* byte swap one block */
381 block
[0] = htole32(block
[0]);
382 block
[1] = htole32(block
[1]);
384 encrypt(&block
[0], key
);
386 block
[0] = htole32(block
[0]);
387 block
[1] = htole32(block
[1]);
389 rb
->memcpy(&buf
[i
*BLOCK_SIZE
], &block
[0], sizeof(block
));
393 static int parse_buffer(void)
397 struct pw_entry
*entry
= pw_list
.first
.next
;
399 start
= &buffer
[HEADER_LEN
];
401 rb
->memcpy(&salt
, &buffer
[0], sizeof(salt
));
404 decrypt_buffer(&buffer
[8], bytes_read
- 8, &key
.words
[0]);
406 if (rb
->memcmp(&buffer
[8], &pwhash
, sizeof(union hash
)))
408 rb
->splash(HZ
*2, "Wrong password");
412 for (i
= 0; i
< MAX_ENTRIES
; i
++)
414 end
= rb
->strchr(start
, '\0'); /* find eol */
415 len
= end
- &buffer
[HEADER_LEN
];
416 if ((len
> bytes_read
+ HEADER_LEN
) || start
== end
)
421 rb
->strncpy(entry
->title
, start
, FIELD_LEN
);
424 end
= rb
->strchr(start
, '\0'); /* find eol */
425 len
= end
- &buffer
[HEADER_LEN
];
426 if (len
> bytes_read
+ HEADER_LEN
)
431 rb
->strncpy(entry
->name
, start
, FIELD_LEN
);
434 end
= rb
->strchr(start
, '\0'); /* find eol */
435 len
= end
- &buffer
[HEADER_LEN
];
436 if (len
> bytes_read
+ HEADER_LEN
)
440 rb
->strncpy(entry
->password
, start
, FIELD_LEN
);
443 if (i
+ 1 < MAX_ENTRIES
- 1)
445 entry
->next
= &pw_list
.entries
[i
+1];
454 pw_list
.num_entries
= i
;
455 rb
->gui_synclist_set_nb_items(&kb_list
, pw_list
.num_entries
);
459 static void write_output(int fd
)
461 size_t bytes_written
;
464 char *p
= &buffer
[HEADER_LEN
]; /* reserve space for salt + hash */
466 rb
->memcpy(&buffer
[8], &pwhash
, sizeof(union hash
));
467 struct pw_entry
*entry
= pw_list
.first
.next
;
469 for (i
= 0; i
< pw_list
.num_entries
; i
++)
471 len
= rb
->strlen(entry
->title
);
472 rb
->strncpy(p
, entry
->title
, len
+1);
474 len
= rb
->strlen(entry
->name
);
475 rb
->strncpy(p
, entry
->name
, len
+1);
477 len
= rb
->strlen(entry
->password
);
478 rb
->strncpy(p
, entry
->password
, len
+1);
483 *p
++ = '\0'; /* mark the end of the list */
485 /* round up to a number divisible by BLOCK_SIZE */
486 size
= ((p
- buffer
+ BLOCK_SIZE
- 1) / BLOCK_SIZE
) * BLOCK_SIZE
;
491 encrypt_buffer(&buffer
[8], size
, &key
.words
[0]);
492 rb
->memcpy(&buffer
[0], &salt
, sizeof(salt
));
494 bytes_written
= rb
->write(fd
, &buffer
, size
);
497 static int enter_pw(char *pw_buf
, size_t buflen
, bool new_pw
)
499 char buf
[2][sizeof(master_pw
)];
500 rb
->memset(buf
, 0, sizeof(buf
));
501 rb
->memset(master_pw
, 0, sizeof(master_pw
));
505 rb
->splash(HZ
, "Enter new master password");
506 if (rb
->kbd_input(buf
[0], sizeof(buf
[0])))
509 rb
->splash(HZ
, "Confirm master password");
510 if (rb
->kbd_input(buf
[1], sizeof(buf
[1])))
513 if (rb
->strcmp(buf
[0], buf
[1]))
515 rb
->splash(HZ
, "Password mismatch");
520 rb
->strncpy(pw_buf
, buf
[0], buflen
);
526 rb
->splash(HZ
, "Enter master password");
527 if (rb
->kbd_input(pw_buf
, buflen
))
533 static int keybox(void)
536 bool new_file
= !rb
->file_exists(KEYBOX_FILE
);
539 if (enter_pw(master_pw
, sizeof (master_pw
), new_file
))
542 /* Read the existing file */
545 fd
= rb
->open(KEYBOX_FILE
, O_RDONLY
);
547 return FILE_OPEN_ERROR
;
548 bytes_read
= rb
->read(fd
, &buffer
, sizeof(buffer
));
558 rb
->gui_synclist_draw(&kb_list
);
559 button
= rb
->get_action(CONTEXT_LIST
, TIMEOUT_BLOCK
);
560 if (rb
->gui_synclist_do_button(&kb_list
, &button
, LIST_WRAP_ON
))
566 splash_pw(rb
->gui_synclist_get_sel_pos(&kb_list
));
568 case ACTION_STD_CONTEXT
:
569 context_menu(rb
->gui_synclist_get_sel_pos(&kb_list
));
571 case ACTION_STD_CANCEL
:
580 fd
= rb
->open(KEYBOX_FILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
582 return FILE_OPEN_ERROR
;
590 static void reset(void)
592 static const char *message_lines
[]=
593 {"Do you really want", "to reset keybox?"};
594 static const char *yes_lines
[]=
596 static const struct text_message message
={message_lines
, 2};
597 static const struct text_message yes_message
={yes_lines
, 1};
599 if(rb
->gui_syncyesno_run(&message
, &yes_message
, NULL
) == YESNO_YES
)
601 rb
->remove(KEYBOX_FILE
);
602 rb
->memset(&buffer
, 0, sizeof(buffer
));
603 rb
->memset(&pw_list
, 0, sizeof(pw_list
));
608 static int main_menu(void)
610 int selection
, result
, ret
;
613 MENUITEM_STRINGLIST(menu
,"Keybox", NULL
, "Enter Keybox",
614 "Reset Keybox", "Exit");
617 result
= rb
->do_menu(&menu
, &selection
, NULL
, false);
637 enum plugin_status
plugin_start(const void *parameter
)
642 rb
->gui_synclist_init(&kb_list
, &kb_list_cb
, NULL
, false, 1, NULL
);
644 rb
->gui_synclist_set_title(&kb_list
, "Keybox", NOICON
);
645 rb
->gui_synclist_set_icon_callback(&kb_list
, NULL
);
646 rb
->gui_synclist_set_nb_items(&kb_list
, 0);
647 rb
->gui_synclist_limit_scroll(&kb_list
, false);
648 rb
->gui_synclist_select_item(&kb_list
, 0);
655 case FILE_OPEN_ERROR
:
656 rb
->splash(HZ
*2, "Error opening file");