summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2016-08-12 22:25:41 -0400
committerFranklin Wei <frankhwei536@gmail.com>2016-08-12 22:25:41 -0400
commit4f4ea9958d4d685ac8eef727aa2b294a3e3f9aaa (patch)
tree9cb1e21230283f3c3512533e790c90f37f16a6f8 /apps/plugins
parent00a489178c5d36a472574a21bec8fbb55fffb9f4 (diff)
downloadrockbox-4f4ea9958d4d685ac8eef727aa2b294a3e3f9aaa.zip
rockbox-4f4ea9958d4d685ac8eef727aa2b294a3e3f9aaa.tar.gz
rockbox-4f4ea9958d4d685ac8eef727aa2b294a3e3f9aaa.tar.bz2
rockbox-4f4ea9958d4d685ac8eef727aa2b294a3e3f9aaa.tar.xz
encrypted export, etc.
Change-Id: I0bde425127f57cb403861a10f2dfbf4116439c7f
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/passmgr/passmgr.c222
1 files changed, 196 insertions, 26 deletions
diff --git a/apps/plugins/passmgr/passmgr.c b/apps/plugins/passmgr/passmgr.c
index 0054ee3..dc475d1 100644
--- a/apps/plugins/passmgr/passmgr.c
+++ b/apps/plugins/passmgr/passmgr.c
@@ -41,9 +41,10 @@
/* these can be changed without breaking anything */
#define PASS_MAX 128
-#define KDF_MIN 5000 /* minimum KDF iterations */
+#define KDF_MIN 5000 /* minimum KDF iterations */
#define KDF_MAX 2500000
#define KDF_DEFAULT (HZ / 4) /* decryption will take about this long by default */
+#define KDF_EXPORT 50000 /* encrypted exports use these many iterations */
/* convenience macros */
#define MAX(a, b) (((a)>(b))?(a):(b))
@@ -57,24 +58,26 @@
#endif
#define DICEWARE_BUF (16 * DICEWARE_WORDS + 1)
+/* this numbering maintans some backwards compatibility: older
+ * versions had a bool that would be false (zero) for HOTP
+ * accounts, but gcc would pad it to 4 bytes; by using this
+ * numbering very little additional logic is needed */
+enum { TYPE_HOTP = 0, TYPE_TOTP = 1, TYPE_STATIC = 3};
+
struct account_t {
char name[MAX_NAME];
- /* this numbering maintans some backwards compatibility: older
- * versions had a bool that would be false (zero) for HOTP
- * accounts, but gcc would pad it to 4 bytes; by using this
- * numbering very little additional logic is needed */
- enum { TYPE_HOTP = 0, TYPE_TOTP = 1, TYPE_STATIC = 3} type;
+ int32_t type;
union {
uint64_t hotp_counter;
- int totp_period;
+ int32_t totp_period;
};
- int digits;
+ uint32_t digits;
unsigned char secret[SECRET_MAX];
- int sec_len;
+ uint32_t sec_len;
};
/* in plugin buffer */
@@ -90,7 +93,7 @@ static int kdf_iters = 0; // calculated on first run
static char encrypted = 0; // 0 = off, 1 = password, 2 = diceware
static char enc_password[PASS_MAX + 1]; // encryption password
-static char data_buf[MAX(MAX_NAME, MAX(SECRET_MAX * 2, MAX(20, MAX(URI_MAX, MAX(sizeof(struct account_t), DICEWARE_BUF)))))];
+static char data_buf[MAX(MAX_NAME, MAX(SECRET_MAX * 2, MAX(20, MAX(URI_MAX, MAX(sizeof(struct account_t), MAX(DICEWARE_BUF, PASS_MAX))))))];
static char temp_sec[SECRET_MAX];
static long background_stack[2 * DEFAULT_STACK_SIZE / sizeof(long)];
@@ -259,9 +262,9 @@ static char choose_letter(char *prefix, char selected, char *fmt, ...)
rb->gui_synclist_init(&list, &charlist_cb, NULL, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
- rb->gui_synclist_select_item(&list, 0);
int n_items = 0;
+ int to_select = 0;
for(char c = 'a'; c <= 'z'; ++c)
{
char temp[8];
@@ -270,12 +273,13 @@ static char choose_letter(char *prefix, char selected, char *fmt, ...)
{
rb->strlcpy(charlist_items[n_items], temp, sizeof(charlist_items[n_items]));
if(selected == c)
- rb->gui_synclist_select_item(&list, n_items);
+ to_select = n_items;
++n_items;
}
}
rb->gui_synclist_set_nb_items(&list, n_items);
rb->gui_synclist_limit_scroll(&list, false);
+ rb->gui_synclist_select_item(&list, to_select);
bool done = false;
rb->gui_synclist_set_title(&list, str, NOICON);
@@ -534,7 +538,8 @@ static bool browse( char *dst, int dst_size, const char *start )
#if 0
/* check an entered password/diceware passphrase with the actual
* one */
-/* used as a very weak security measure */
+/* used as a very weak security measure, and disabled because it
+ * doesn't really do anything */
static bool verify_password(void)
{
char temp_pass[PASS_MAX];
@@ -996,7 +1001,11 @@ static void save_accts(void)
struct aes_ctr_ctx aes_ctx;
aes_ctr_init(&aes_ctx, key, nonce);
+ /* note that in calculating the HMAC we don't use the primary
+ key, but instead another part of the PBKDF2 output */
+
struct hmac_ctx hmac_ctx;
+ /* 16 bytes are used for the encryption key, 4 for verification */
hmac_sha1_init(&hmac_ctx, key + 16, sizeof(key) - 16);
for(int i = 0; i < next_slot; ++i)
@@ -1018,7 +1027,10 @@ static void save_accts(void)
rb->lseek(fd, mac_offs, SEEK_SET);
rb->write(fd, mac, 20);
+ /* cleanup */
aes_ctr_destroy(&aes_ctx);
+ wipe_buf(key, sizeof(key));
+ wipe_buf(tmp, sizeof(tmp));
}
else
for(int i = 0; i < next_slot; ++i)
@@ -1094,6 +1106,10 @@ static void gather_entropy(void *out, size_t len)
timestamp ^= get_utc();
#endif
+#ifdef USEC_TIMER
+ timestamp ^= SWAP32_CONST(USEC_TIMER);
+#endif
+
/* finally mix it all up with an HMAC */
hmac_sha1(&timestamp, sizeof(long), ptr, 20, ptr);
rb->yield();
@@ -1105,6 +1121,7 @@ static void gather_entropy(void *out, size_t len)
char tmp[sizeof(salt) + 4];
PBKDF2(buf, 20, &salt, sizeof(salt), 1, out, len, tmp);
+ /* clear any errant button presses */
rb->button_clear_queue();
}
@@ -1594,13 +1611,13 @@ static void show_code(int acct)
switch(accounts[acct].type)
{
case TYPE_HOTP:
- rb->snprintf(format_buf, sizeof(format_buf), "%%0%dd", accounts[acct].digits);
+ rb->snprintf(format_buf, sizeof(format_buf), "%%0%" PRIu32 "d", accounts[acct].digits);
rb->splashf(0, format_buf, next_code(acct));
background_save();
break;
#if CONFIG_RTC
case TYPE_TOTP:
- rb->snprintf(format_buf, sizeof(format_buf), "%%0%dd (%%ld second(s) left)", accounts[acct].digits);
+ rb->snprintf(format_buf, sizeof(format_buf), "%%0%" PRIu32 "d (%%ld second(s) left)", accounts[acct].digits);
rb->splashf(0, format_buf, next_code(acct),
accounts[acct].totp_period - get_utc() % accounts[acct].totp_period);
break;
@@ -1752,6 +1769,9 @@ static void edit_menu(int acct)
type_change:
+ /* don't want to corrupt a save */
+ rb->mutex_lock(&save_mutex);
+
switch(accounts[acct].type)
{
case TYPE_HOTP:
@@ -1767,9 +1787,6 @@ type_change:
break;
}
- /* don't want to corrupt a save */
- rb->mutex_lock(&save_mutex);
-
while(!quit)
{
switch(rb->do_menu(menu, &sel, NULL, false))
@@ -1808,7 +1825,7 @@ type_change:
rb->snprintf(data_buf, sizeof(data_buf), "%u", (unsigned int) accounts[acct].hotp_counter);
break;
case TYPE_TOTP:
- rb->snprintf(data_buf, sizeof(data_buf), "%d", accounts[acct].totp_period);
+ rb->snprintf(data_buf, sizeof(data_buf), "%" PRIi32, accounts[acct].totp_period);
break;
case TYPE_STATIC:
switch(static_password_menu(accounts[next_slot].secret, SECRET_MAX))
@@ -1855,7 +1872,7 @@ type_change:
}
else
{
- rb->snprintf(data_buf, sizeof(data_buf), "%d", accounts[acct].digits);
+ rb->snprintf(data_buf, sizeof(data_buf), "%" PRIu32, accounts[acct].digits);
if(rb->kbd_input(data_buf, sizeof(data_buf)) < 0)
break;
@@ -2118,11 +2135,11 @@ static void export_uri_list(int typemask)
case TYPE_TOTP:
case TYPE_HOTP:
base32_encode(accounts[i].secret, accounts[i].sec_len, buf, sizeof(buf));
- rb->fdprintf(fd, "otpauth://%s/%s?secret=%s&digits=%d", accounts[i].type == TYPE_TOTP ? "totp" : "hotp",
+ rb->fdprintf(fd, "otpauth://%s/%s?secret=%s&digits=%" PRIu32, accounts[i].type == TYPE_TOTP ? "totp" : "hotp",
accounts[i].name, buf, accounts[i].digits);
if(accounts[i].type == TYPE_TOTP)
- rb->fdprintf(fd, "&period=%d", accounts[i].totp_period);
+ rb->fdprintf(fd, "&period=%" PRIi32, accounts[i].totp_period);
else
rb->fdprintf(fd, "&counter=%u", (unsigned) accounts[i].hotp_counter);
rb->fdprintf(fd, "\n");
@@ -2139,9 +2156,160 @@ static void export_uri_list(int typemask)
rb->splash(HZ, "Success.");
}
+static void export_encrypted(void)
+{
+ if(encrypted)
+ {
+ MENUITEM_STRINGLIST(menu, "Password to Use", NULL,
+ "Current Password/Passphrase",
+ "Enter New Password",
+ "Cancel");
+ bool quit = false;
+ while(!quit)
+ {
+ switch(rb->do_menu(&menu, NULL, NULL, false))
+ {
+ case 0:
+ rb->strlcpy(data_buf, enc_password, PASS_MAX);
+ goto export;
+ case 1:
+ quit = true;
+ break;
+ case 2:
+ default:
+ return;
+ }
+ }
+ }
+
+ /* we need to read a password */
+ rb->splash(HZ, "Enter encryption password:");
+ data_buf[0] = '\0';
+ if(rb->kbd_input(data_buf, PASS_MAX) < 0)
+ return;
+
+ char temp_pass[PASS_MAX];
+
+ rb->splash(HZ, "Re-enter encryption password:");
+ temp_pass[0] = '\0';
+ if(rb->kbd_input(temp_pass, PASS_MAX) < 0)
+ return;
+
+ if(rb->strcmp(temp_pass, data_buf))
+ {
+ rb->splash(HZ, "Passwords do not match!");
+ return;
+ }
+
+ char fname[MAX_PATH];
+
+export:
+
+ rb->snprintf(fname, sizeof(fname), "/");
+ rb->splash(HZ, "Enter output filename:");
+ if(rb->kbd_input(fname, sizeof(fname)) < 0)
+ return;
+
+ if(rb->file_exists(fname))
+ {
+ rb->splash(HZ, "File already exists!");
+ return;
+ }
+
+ /* begin saving */
+
+ rb->splash(0, "Exporting...");
+
+ /* this format essentially mirrors that of the default save
+ * format, but with some slight modifications: */
+ /* all values are stored big-endian */
+ /* no hardware AES core is used */
+ /* a constant 50,000 PBKDF2 iterations are used */
+
+ int fd = rb->open(fname, O_WRONLY | O_CREAT | O_TRUNC);
+ if(fd < 0)
+ {
+ rb->splash(HZ, "Couldn't open file.");
+ return;
+ }
+
+ rb->mutex_lock(&save_mutex);
+
+ rb->fdprintf(fd, "OTPX");
+
+ /* generate/write the nonce */
+ uint64_t nonce = *rb->current_tick;
+#if CONFIG_RTC
+ nonce |= (uint64_t)get_utc() << 32;
+#endif
+
+ rb->write(fd, &nonce, sizeof(nonce));
+
+ /* placeholder for the MAC */
+ off_t mac_offs = rb->lseek(fd, 0, SEEK_CUR);
+ rb->memset(data_buf, 0, 20);
+ rb->write(fd, data_buf, 20);
+
+ char key[20];
+ char tmp[sizeof(nonce) + 4];
+
+ PBKDF2(data_buf, rb->strlen(data_buf), &nonce, sizeof(nonce),
+ KDF_EXPORT, key, sizeof(key), tmp);
+
+ struct aes_ctr_ctx aes_ctx;
+ aes_ctr_init(&aes_ctx, key, nonce);
+
+ /* note that in calculating the HMAC we don't use the primary
+ key, but instead another part of the PBKDF2 output */
+
+ struct hmac_ctx hmac_ctx;
+ /* 16 bytes are used for the encryption key, 4 for verification */
+ hmac_sha1_init(&hmac_ctx, key + 16, sizeof(key) - 16);
+
+ for(int i = 0; i < next_slot; ++i)
+ {
+ /* we don't need data_buf to store the password anymore */
+ rb->memcpy(data_buf, accounts + i, sizeof(struct account_t));
+ struct account_t *acct = (struct account_t*)data_buf;
+
+ /* change endianness */
+ acct->type = htobe32(acct->type);
+ acct->hotp_counter = htobe64(acct->hotp_counter);
+ acct->digits = htobe32(acct->digits);
+ acct->sec_len = htobe32(acct->sec_len);
+
+ /* encrypt */
+ aes_ctr_process(&aes_ctx, data_buf, data_buf, sizeof(struct account_t));
+
+ /* write */
+ rb->write(fd, data_buf, sizeof(struct account_t));
+
+ /* and MAC */
+ hmac_sha1_process_bytes(&hmac_ctx, data_buf, sizeof(struct account_t));
+ }
+
+ /* write the MAC */
+ char mac[20];
+
+ hmac_sha1_finish_ctx(&hmac_ctx, mac);
+
+ rb->lseek(fd, mac_offs, SEEK_SET);
+ rb->write(fd, mac, 20);
+
+ /* cleanup */
+ aes_ctr_destroy(&aes_ctx);
+ wipe_buf(key, sizeof(key));
+ wipe_buf(tmp, sizeof(tmp));
+ wipe_buf(temp_pass, sizeof(temp_pass));
+
+ rb->close(fd);
+ rb->mutex_unlock(&save_mutex);
+}
+
static void export_menu(void)
{
MENUITEM_STRINGLIST(menu, "Export Accounts", NULL,
+ "To Encrypted Backup (all accounts)",
"To URI List (static passwords interleaved)",
"To URI List (only OTP accounts)",
"To 'username:password List' (only static passwords)",
@@ -2156,13 +2324,15 @@ static void export_menu(void)
switch(rb->do_menu(&menu, &sel, NULL, false))
{
case 0:
+ export_encrypted();
+ case 1:
export_uri_list(SAVE_ALL);
break;
- case 1:
+ case 2:
export_uri_list(SAVE_OTP);
break;
- case 2:
- export_uri_list(SAVE_STATIC);
+ case 3:
+ export_uri_list(SAVE_STATIC);
break;
default:
quit = true;
@@ -2944,7 +3114,7 @@ static void type_code(int acct)
/* hackery to get around the lack of %*d support */
char fmt_buf[64], buf[64];
- rb->snprintf(fmt_buf, sizeof(fmt_buf), "%%0%dd", accounts[acct].digits);
+ rb->snprintf(fmt_buf, sizeof(fmt_buf), "%%0%" PRIu32 "d", accounts[acct].digits);
rb->snprintf(buf, sizeof(buf), fmt_buf, code);
char *ptr = buf;