540 lines
15 KiB
C
540 lines
15 KiB
C
/*
|
|
* This file is part of MultiROM.
|
|
*
|
|
* MultiROM is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* MultiROM is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with MultiROM. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
|
|
#include "pw_ui.h"
|
|
#include "encmnt_defines.h"
|
|
#include "../lib/framebuffer.h"
|
|
#include "../lib/colors.h"
|
|
#include "../lib/log.h"
|
|
#include "../lib/input.h"
|
|
#include "../lib/keyboard.h"
|
|
#include "../lib/util.h"
|
|
#include "../lib/notification_card.h"
|
|
#include "../lib/animation.h"
|
|
#include "../lib/workers.h"
|
|
#include "../lib/containers.h"
|
|
#include "../rom_quirks.h"
|
|
|
|
#include "crypto/lollipop/cryptfs.h"
|
|
|
|
#define HEADER_HEIGHT (110*DPI_MUL)
|
|
#define PWUI_DOT_R (15*DPI_MUL)
|
|
#define PWUI_DOT_ACTIVE_R (PWUI_DOT_R/2)
|
|
#define PWUI_DOT_ACTIVE_OFF (PWUI_DOT_R - PWUI_DOT_ACTIVE_R)
|
|
#define PWUI_LINE_W (12*DPI_MUL)
|
|
#define PWUI_DOTS_CNT 9
|
|
|
|
struct pwui_type_pass_data {
|
|
fb_text *passwd_text;
|
|
fb_rect *cursor_rect;
|
|
struct keyboard *keyboard;
|
|
char *pass_buf;
|
|
char *pass_buf_stars;
|
|
size_t pass_buf_cap;
|
|
};
|
|
|
|
struct pwui_type_pattern_data {
|
|
fb_circle **dots;
|
|
fb_circle **active_dots;
|
|
fb_line **complete_lines;
|
|
fb_line *cur_line;
|
|
int connected_dots[PWUI_DOTS_CNT];
|
|
size_t connected_dots_len;
|
|
int touch_id;
|
|
};
|
|
|
|
static pthread_mutex_t exit_code_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static int exit_code = ENCMNT_UIRES_ERROR;
|
|
static void *pwui_type_data = NULL;
|
|
static fb_text *invalid_pass_text = NULL;
|
|
static button *boot_primary_btn = NULL;
|
|
|
|
static void boot_internal_clicked(UNUSED void *data)
|
|
{
|
|
ncard_builder *b = ncard_create_builder();
|
|
ncard_set_pos(b, NCARD_POS_CENTER);
|
|
ncard_set_text(b, "Booting the primary ROM...");
|
|
ncard_show(b, 1);
|
|
|
|
// We need to run quirks for primary ROM to prevent
|
|
// restorecon breaking everything
|
|
rom_quirks_on_initrd_finalized();
|
|
|
|
pthread_mutex_lock(&exit_code_mutex);
|
|
exit_code = ENCMNT_UIRES_BOOT_INTERNAL;
|
|
pthread_mutex_unlock(&exit_code_mutex);
|
|
}
|
|
|
|
static void fade_rect_alpha_step(void *data, float interpolated)
|
|
{
|
|
fb_rect *r = data;
|
|
r->color = (((int)(0xFF*interpolated)) << 24);
|
|
fb_request_draw();
|
|
}
|
|
|
|
static void reveal_rect_alpha_step(void *data, float interpolated)
|
|
{
|
|
fb_rect *r = data;
|
|
interpolated = 1.f - interpolated;
|
|
r->color = (r->color & ~(0xFF << 24)) | (((int)(0xFF*interpolated)) << 24);
|
|
fb_request_draw();
|
|
}
|
|
|
|
static int try_password(const char *pass)
|
|
{
|
|
fb_text_set_content(invalid_pass_text, "");
|
|
|
|
ncard_builder *b = ncard_create_builder();
|
|
ncard_set_pos(b, NCARD_POS_CENTER);
|
|
ncard_set_text(b, "Verifying password...");
|
|
ncard_show(b, 1);
|
|
|
|
if(cryptfs_check_passwd(pass) != 0)
|
|
{
|
|
ncard_hide();
|
|
fb_text_set_content(invalid_pass_text, "Invalid password!");
|
|
center_text(invalid_pass_text, 0, -1, fb_width, -1);
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
ncard_builder *b = ncard_create_builder();
|
|
ncard_set_pos(b, NCARD_POS_CENTER);
|
|
ncard_set_text(b, "Correct!");
|
|
ncard_show(b, 1);
|
|
|
|
fb_rect *r = fb_add_rect_lvl(10000, 0, 0, fb_width, fb_height, 0x00000000);
|
|
call_anim *a = call_anim_create(r, fade_rect_alpha_step, 500, INTERPOLATOR_ACCELERATE);
|
|
call_anim_add(a);
|
|
|
|
pthread_mutex_lock(&exit_code_mutex);
|
|
exit_code = ENCMNT_UIRES_PASS_OK;
|
|
pthread_mutex_unlock(&exit_code_mutex);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void type_pass_key_pressed(void *data, uint8_t code)
|
|
{
|
|
struct pwui_type_pass_data *d = data;
|
|
|
|
if(code < 128)
|
|
{
|
|
size_t pass_len = strlen(d->pass_buf);
|
|
while(d->pass_buf_cap < pass_len + 2)
|
|
{
|
|
d->pass_buf_cap *= 2;
|
|
d->pass_buf = realloc(d->pass_buf, d->pass_buf_cap);
|
|
d->pass_buf_stars = realloc(d->pass_buf_stars, d->pass_buf_cap);
|
|
}
|
|
|
|
if(pass_len > 0)
|
|
d->pass_buf_stars[pass_len-1] = '*';
|
|
d->pass_buf_stars[pass_len] = (char)code;
|
|
d->pass_buf_stars[pass_len+1] = 0;
|
|
|
|
d->pass_buf[pass_len++] = (char)code;
|
|
d->pass_buf[pass_len] = 0;
|
|
|
|
fb_text_set_content(d->passwd_text, d->pass_buf_stars);
|
|
center_text(d->passwd_text, 0, 0, fb_width, fb_height);
|
|
fb_request_draw();
|
|
|
|
return;
|
|
}
|
|
|
|
switch(code)
|
|
{
|
|
case OSK_BACKSPACE:
|
|
{
|
|
size_t pass_len = strlen(d->pass_buf);
|
|
if(pass_len == 0)
|
|
break;
|
|
|
|
d->pass_buf_stars[--pass_len] = 0;
|
|
d->pass_buf[pass_len] = 0;
|
|
|
|
fb_text_set_content(d->passwd_text, d->pass_buf_stars);
|
|
center_text(d->passwd_text, 0, 0, fb_width, fb_height);
|
|
fb_request_draw();
|
|
break;
|
|
}
|
|
case OSK_CLEAR:
|
|
d->pass_buf[0] = 0;
|
|
d->pass_buf_stars[0] = 0;
|
|
fb_text_set_content(d->passwd_text, "");
|
|
fb_request_draw();
|
|
break;
|
|
case OSK_ENTER:
|
|
try_password(d->pass_buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void type_pass_init(int pwtype)
|
|
{
|
|
struct pwui_type_pass_data *d = mzalloc(sizeof(struct pwui_type_pass_data));
|
|
d->keyboard = keyboard_create(pwtype == CRYPT_TYPE_PIN ? KEYBOARD_PIN : KEYBOARD_NORMAL,
|
|
0, fb_height*0.65, fb_width, fb_height*0.35);
|
|
keyboard_set_callback(d->keyboard, type_pass_key_pressed, d);
|
|
|
|
d->passwd_text = fb_add_text(0, 0, C_TEXT, SIZE_BIG, "");
|
|
center_text(d->passwd_text, 0, 0, fb_width, fb_height);
|
|
|
|
d->pass_buf_cap = 12;
|
|
d->pass_buf = mzalloc(d->pass_buf_cap);
|
|
d->pass_buf_stars = mzalloc(d->pass_buf_cap);
|
|
|
|
pwui_type_data = d;
|
|
}
|
|
|
|
static void type_pass_destroy(void)
|
|
{
|
|
struct pwui_type_pass_data *d = pwui_type_data;
|
|
|
|
keyboard_destroy(d->keyboard);
|
|
free(d->pass_buf);
|
|
free(d->pass_buf_stars);
|
|
free(d);
|
|
pwui_type_data = NULL;
|
|
}
|
|
|
|
static inline int type_pattern_in_dot(struct pwui_type_pattern_data *d, touch_event *ev)
|
|
{
|
|
int i;
|
|
fb_circle *c;
|
|
for(i = 0; d->dots[i]; ++i)
|
|
{
|
|
c = d->dots[i];
|
|
if(in_rect(ev->x, ev->y, c->x - PWUI_DOT_R*1.5, c->y - PWUI_DOT_R*1.5, c->w*3, c->h*3))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static inline int type_pattern_dot_used(struct pwui_type_pattern_data *d, int dot_idx)
|
|
{
|
|
size_t i;
|
|
for(i = 0; i < d->connected_dots_len; ++i)
|
|
if(d->connected_dots[i] == dot_idx)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static inline void type_pattern_connect_dot(struct pwui_type_pattern_data *d, int dot_idx)
|
|
{
|
|
if(d->connected_dots_len >= PWUI_DOTS_CNT)
|
|
{
|
|
ERROR("d->connected_dots_len overflowed PWUI_DOTS_CNT!\n");
|
|
return;
|
|
}
|
|
|
|
d->connected_dots[d->connected_dots_len++] = dot_idx;
|
|
|
|
fb_circle *c = d->dots[dot_idx];
|
|
c = fb_add_circle_lvl(100, c->x+PWUI_DOT_ACTIVE_OFF, c->y+PWUI_DOT_ACTIVE_OFF, PWUI_DOT_ACTIVE_R, C_HIGHLIGHT_TEXT);
|
|
list_add(&d->active_dots, c);
|
|
}
|
|
|
|
static int type_pattern_touch_handler(touch_event *ev, void *data)
|
|
{
|
|
struct pwui_type_pattern_data *d = data;
|
|
|
|
if(d->touch_id == -1 && (ev->changed & TCHNG_ADDED) && !ev->consumed)
|
|
{
|
|
const int dot_idx = type_pattern_in_dot(d, ev);
|
|
if(dot_idx == -1)
|
|
return -1;
|
|
|
|
d->touch_id = ev->id;
|
|
d->connected_dots_len = 0;
|
|
|
|
type_pattern_connect_dot(d, dot_idx);
|
|
|
|
fb_circle *c = d->dots[dot_idx];
|
|
d->cur_line = fb_add_line(c->x + PWUI_DOT_R, c->y + PWUI_DOT_R, ev->x, ev->y, PWUI_LINE_W, C_HIGHLIGHT_BG);
|
|
fb_request_draw();
|
|
return 0;
|
|
}
|
|
|
|
if(d->touch_id != ev->id || !d->cur_line)
|
|
return -1;
|
|
|
|
if(ev->changed & TCHNG_POS)
|
|
{
|
|
const int dot_idx = type_pattern_in_dot(d, ev);
|
|
if(dot_idx != -1 && !type_pattern_dot_used(d, dot_idx))
|
|
{
|
|
fb_circle *c = d->dots[dot_idx];
|
|
d->cur_line->x2 = c->x + PWUI_DOT_R;
|
|
d->cur_line->y2 = c->y + PWUI_DOT_R;
|
|
list_add(&d->complete_lines, d->cur_line);
|
|
d->cur_line = fb_add_line(c->x + PWUI_DOT_R, c->y + PWUI_DOT_R, ev->x, ev->y, PWUI_LINE_W, C_HIGHLIGHT_BG);
|
|
|
|
const int last_dot = d->connected_dots[d->connected_dots_len-1];
|
|
int dot_mid = -1;
|
|
// The line is vertical and has crossed a point in the middle
|
|
if(dot_idx%3 == last_dot%3 && iabs(dot_idx - last_dot) > 3)
|
|
dot_mid = 3 + dot_idx%3;
|
|
// the line is horizontal and has crossed a point in the middle
|
|
else if(dot_idx/3 == last_dot/3 && iabs(dot_idx - last_dot) > 1)
|
|
dot_mid = (dot_idx/3)*3 + 1;
|
|
// the line is diagonal and has crossed the middle point
|
|
else if((dot_idx == 0 && last_dot == 8) || (dot_idx == 8 && last_dot == 0) ||
|
|
(dot_idx == 2 && last_dot == 6) || (dot_idx == 6 && last_dot == 2))
|
|
{
|
|
dot_mid = 4;
|
|
}
|
|
|
|
if(dot_mid != -1 && !type_pattern_dot_used(d, dot_mid))
|
|
type_pattern_connect_dot(d, dot_mid);
|
|
type_pattern_connect_dot(d, dot_idx);
|
|
}
|
|
else
|
|
{
|
|
d->cur_line->x2 = ev->x;
|
|
d->cur_line->y2 = ev->y;
|
|
}
|
|
fb_request_draw();
|
|
}
|
|
|
|
if(ev->changed & TCHNG_REMOVED)
|
|
{
|
|
d->touch_id = -1;
|
|
fb_rm_line(d->cur_line);
|
|
d->cur_line = NULL;
|
|
fb_request_draw();
|
|
|
|
char *passwd = malloc(d->connected_dots_len+1);
|
|
size_t i;
|
|
for(i = 0; i < d->connected_dots_len; ++i)
|
|
passwd[i] = '1' + d->connected_dots[i];
|
|
passwd[i] = 0;
|
|
|
|
if(try_password(passwd) < 0)
|
|
{
|
|
list_clear(&d->active_dots, fb_remove_item);
|
|
list_clear(&d->complete_lines, fb_remove_item);
|
|
fb_request_draw();
|
|
}
|
|
|
|
free(passwd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void type_pattern_init(void)
|
|
{
|
|
struct pwui_type_pattern_data *d = mzalloc(sizeof(struct pwui_type_pattern_data));
|
|
int cx, cy;
|
|
|
|
const int start_x = fb_width*0.2;
|
|
const int step = (fb_width*0.6 - PWUI_DOT_R*2) / 2;
|
|
int x = start_x;
|
|
int y = fb_height/2 - fb_width/4;
|
|
|
|
for(cy = 0; cy < 3; ++cy)
|
|
{
|
|
for(cx = 0; cx < 3; ++cx)
|
|
{
|
|
fb_circle *c = fb_add_circle(x, y, PWUI_DOT_R, C_HIGHLIGHT_BG);
|
|
list_add(&d->dots, c);
|
|
|
|
x += step;
|
|
}
|
|
x = start_x;
|
|
y += step;
|
|
}
|
|
|
|
d->touch_id = -1;
|
|
add_touch_handler(type_pattern_touch_handler, d);
|
|
|
|
pwui_type_data = d;
|
|
}
|
|
|
|
static void type_pattern_destroy(void)
|
|
{
|
|
struct pwui_type_pattern_data *d = pwui_type_data;
|
|
list_clear(&d->dots, fb_remove_item);
|
|
list_clear(&d->active_dots, fb_remove_item);
|
|
list_clear(&d->complete_lines, fb_remove_item);
|
|
fb_rm_line(d->cur_line);
|
|
free(d);
|
|
|
|
pwui_type_data = NULL;
|
|
}
|
|
|
|
static void init_ui(int pwtype)
|
|
{
|
|
fb_add_rect_lvl(100, 0, 0, fb_width, HEADER_HEIGHT, C_HIGHLIGHT_BG);
|
|
|
|
ncard_set_top_offset(HEADER_HEIGHT);
|
|
|
|
fb_text_proto *p = fb_text_create(0, 0, C_HIGHLIGHT_TEXT, SIZE_EXTRA, "Encrypted device");
|
|
p->level = 110;
|
|
fb_text *t = fb_text_finalize(p);
|
|
center_text(t, -1, 0, -1, HEADER_HEIGHT);
|
|
t->x = t->y;
|
|
|
|
t = fb_add_text(0, HEADER_HEIGHT + 200*DPI_MUL, C_TEXT, SIZE_NORMAL, "Please enter your password:");
|
|
center_text(t, 0, -1, fb_width, -1);
|
|
|
|
invalid_pass_text = fb_add_text(0, 0, 0xFFFF0000, SIZE_BIG, "");
|
|
center_text(invalid_pass_text, -1, HEADER_HEIGHT, -1, 200*DPI_MUL);
|
|
|
|
if(!mrom_is_second_boot())
|
|
{
|
|
boot_primary_btn = mzalloc(sizeof(button));
|
|
boot_primary_btn->w = fb_width*0.30;
|
|
boot_primary_btn->h = HEADER_HEIGHT;
|
|
boot_primary_btn->x = fb_width - boot_primary_btn->w;
|
|
boot_primary_btn->y = 0;
|
|
boot_primary_btn->level_off = 101;
|
|
boot_primary_btn->clicked = &boot_internal_clicked;
|
|
button_init_ui(boot_primary_btn, "BOOT PRIMARY ROM", SIZE_SMALL);
|
|
}
|
|
|
|
switch(pwtype)
|
|
{
|
|
case CRYPT_TYPE_PASSWORD:
|
|
case CRYPT_TYPE_PIN:
|
|
type_pass_init(pwtype);
|
|
break;
|
|
case CRYPT_TYPE_PATTERN:
|
|
type_pattern_init();
|
|
break;
|
|
default:
|
|
t = fb_add_text(0, 0, C_TEXT, SIZE_NORMAL, "Error: unknown password type %d", pwtype);
|
|
center_text(t, 0, 0, fb_width, fb_height);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void destroy_ui(int pwtype)
|
|
{
|
|
switch(pwtype)
|
|
{
|
|
case CRYPT_TYPE_PASSWORD:
|
|
case CRYPT_TYPE_PIN:
|
|
type_pass_destroy();
|
|
break;
|
|
case CRYPT_TYPE_PATTERN:
|
|
type_pattern_destroy();
|
|
break;
|
|
}
|
|
|
|
if(boot_primary_btn)
|
|
button_destroy(boot_primary_btn);
|
|
}
|
|
|
|
static int pw_ui_shutdown_counter_touch_handler(UNUSED touch_event *ev, void *data)
|
|
{
|
|
int *shutdown_counter = data;
|
|
if(*shutdown_counter == 0)
|
|
return -1;
|
|
|
|
ncard_hide();
|
|
|
|
*shutdown_counter = 0;
|
|
return -1;
|
|
}
|
|
|
|
int pw_ui_run(int pwtype)
|
|
{
|
|
int shutdown_counter = 0;
|
|
|
|
if(fb_open(0) < 0)
|
|
{
|
|
ERROR("Failed to open framebuffer\n");
|
|
return -1;
|
|
}
|
|
|
|
fb_freeze(1);
|
|
fb_set_background(C_BACKGROUND);
|
|
|
|
workers_start();
|
|
anim_init(1.f);
|
|
|
|
init_ui(pwtype);
|
|
|
|
start_input_thread();
|
|
add_touch_handler(pw_ui_shutdown_counter_touch_handler, &shutdown_counter);
|
|
|
|
fb_freeze(0);
|
|
|
|
fb_rect *r = fb_add_rect_lvl(1000, 0, 0, fb_width, fb_height, BLACK);
|
|
call_anim *a = call_anim_create(r, reveal_rect_alpha_step, 500, INTERPOLATOR_ACCELERATE);
|
|
a->on_finished_call = fb_remove_item;
|
|
a->on_finished_data = r;
|
|
call_anim_add(a);
|
|
|
|
while(1)
|
|
{
|
|
pthread_mutex_lock(&exit_code_mutex);
|
|
const int c = exit_code;
|
|
pthread_mutex_unlock(&exit_code_mutex);
|
|
|
|
if(c != ENCMNT_UIRES_ERROR)
|
|
break;
|
|
|
|
if(get_last_key() == KEY_POWER && (!ncard_is_visible() || shutdown_counter))
|
|
{
|
|
++shutdown_counter;
|
|
if(shutdown_counter == 1)
|
|
{
|
|
ncard_builder *b = ncard_create_builder();
|
|
ncard_set_text(b, "Press power button again to shut down the device.");
|
|
ncard_show(b, 1);
|
|
}
|
|
else
|
|
{
|
|
ncard_builder *b = ncard_create_builder();
|
|
ncard_set_pos(b, NCARD_POS_CENTER);
|
|
ncard_set_text(b, "Shutting down...");
|
|
ncard_show(b, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
usleep(100000);
|
|
}
|
|
|
|
anim_stop(1);
|
|
fb_freeze(1);
|
|
fb_force_draw();
|
|
|
|
rm_touch_handler(pw_ui_shutdown_counter_touch_handler, &shutdown_counter);
|
|
|
|
stop_input_thread();
|
|
workers_stop();
|
|
|
|
destroy_ui(pwtype);
|
|
|
|
if(shutdown_counter == 2)
|
|
do_reboot(REBOOT_SHUTDOWN);
|
|
|
|
fb_clear();
|
|
fb_close();
|
|
return exit_code;
|
|
}
|