Skip to content
Snippets Groups Projects
cardreader_interface.c 14.58 KiB
#include "cardreader_interface.h"
#include "power_monitor.h"
#include "keystore.h"

#include "../common/transport/transport.h"

#define CARD_CMD_SELECT_APPLET "\x00\xA4\x04\x00"
#define CARD_CMD_INIT_KEY      "\x00\x01\x00\x00"
#define CARD_CMD_AUTH          "\x00\x02\x00\x00"
#define CARD_CMD_UPDATE_KEY    "\x00\x03\x00\x00"
#define CARD_CMD_CLEAR_KEY     "\x00\xFF\x00\x00"

#define CARD_AUTH_APPLET_NAME "\x23\x23\x00\x42\x42"
#define CARD_STATUS_SUCCESS    0x9000

//#define CARDREADER_DEBUG

/** Card auth protcol
 * 1. Host -> Card:   CARD_CMD_SELECT_APPLET
 *    Card -> Host:   CARD_STATUS_SUCCESS
 *                            OR
 *    Card -> Host:   other ISO7816 status code (-> abort)
 * 
 * 2. Host -> Card:   CARD_CMD_AUTH + Host Challange(32 byte)
 *    Host -> Reader: CARD_STATUS_SUCCESS + Host Challange Response (20Byte) + Card challange (32Byte)
 *                            OR
 *    Host -> Reader: other ISO7816 status code (-> abort)
 * 
 * 3. Host -> Card:   CARD_CMD_AUTH + New Key Delta (64Byte) + Card Challange Response (20Byte)
 *    Card -> Host:   CARD_STATUS_SUCCESS (-> success, update key, unlock)
 *                            OR
 *    Card -> Host:   other ISO7816 status code (-> abort)
**/


void unlock(void);
uint8_t check_card_status(uint8_t* msg_buffer, uint8_t length, uint8_t expected_body_length);
void cardreader_select_auth_applet(void);
uint8_t cardreader_select_auth_applet_result(uint8_t* msg_buffer, uint8_t length);
void cardreader_auth_request(void);
uint8_t cardreader_auth_request_result(uint8_t* msg_buffer, uint8_t length);
void cardreader_update_key(void);
uint8_t cardreader_update_key_result(uint8_t* msg_buffer, uint8_t length);
void cardreader_abort(uint8_t result);
void cardreader_timeout(void);
void MGMT_transmit_buffer_hex(unsigned char* data, unsigned char length);

void cardreader_display_clear(void);
void cardreader_display_move(uint8_t x, uint8_t y);
void cardreader_display_write_sz(char* string);
void cardreader_display_write_sz_P(const char* string);
void cardreader_display_show_result(uint8_t success);
void cardreader_display_set_backlight(uint8_t on);
uint8_t cardreader_sys_get_card_status(void);

#define CARDREADER_POWER_STATE_OFF     1
#define CARDREADER_POWER_STATE_STARTUP 2
#define CARDREADER_POWER_STATE_READY   3
static uint8_t cardreader_power_state = CARDREADER_POWER_STATE_OFF;
static volatile uint8_t cardreader_startup_event = 0;

#define AUTH_STATE_NONE 1
#define AUTH_STATE_CARD_INSERTED 2
#define AUTH_STATE_SELECTING_AUTH_APPLET 3
#define AUTH_STATE_AUTH_REQUESTED_SENT 4
#define AUTH_STATE_AUTH_VERYFIED 5

volatile uint8_t auth_state = AUTH_STATE_NONE;
volatile uint16_t state_timeout;
static uint8_t auth_retry_count = 0;
#define AUTH_RETRY_MAX 2

static SALT card_challenge;
static int16_t keyslot_index = -1;
static KEY new_key_delta;

void card_power_request_pin_changed(uint8_t power_request){
	if ((power_get_status() & POWER_STATUS_AC_ON) == 0){//If we are on AC, don't give a damn. Keep the Cardreader always powerd.
		if (cardreader_power_state == CARDREADER_POWER_STATE_OFF && power_request != 0){
			cardreader_powerup();
		} else if (cardreader_power_state != CARDREADER_POWER_STATE_OFF && power_request == 0){
			cardreader_shutdown();
		}
	}
}

static void host_alive(void){//answer ping
	transport_send_interrupt_message(CARDREADER_INTERRUPT_HOST_ALIVE);
}
static void cardreader_alive(void){
	printf_P(PSTR("Cardreader Online\n"));
	cardreader_startup_event = 1;
}

static void card_inserted(void){
	printf_P(PSTR("Card Inserted\n"));
	auth_state = AUTH_STATE_CARD_INSERTED;
	power_disable_battery_shutdown();
}
static void card_removed(void){
	auth_state = AUTH_STATE_NONE;
	power_enable_battery_shutdown();
	printf_P(PSTR("Card Removed\n"));
}

void cardreader_init(void){
	transport_init();
	transport_set_interrupt_handler(CARDREADER_INTERRUPT_HOST_ALIVE, host_alive);
	transport_set_interrupt_handler(CARDREADER_INTERRUPT_CARDREADER_ALIVE, cardreader_alive);
	transport_set_interrupt_handler(CARDREADER_INTERRUPT_CARD_INSERTED, card_inserted);
	transport_set_interrupt_handler(CARDREADER_INTERRUPT_CARD_REMOVED, card_removed);
}

void cardreader_powerup(void){
	CARDREADER_POWER_PORT &= ~(1 << CARDREADER_POWER_PIN);
	CARDREADER_POWER_DDR  |= (1 << CARDREADER_POWER_PIN);
	
	cardreader_power_state = CARDREADER_POWER_STATE_STARTUP;
	
	printf_P(PSTR("Cardreader startup...\n"));
}
void cardreader_shutdown(void){
	CARDREADER_POWER_PORT |= (1 << CARDREADER_POWER_PIN);
	cardreader_power_state = CARDREADER_POWER_STATE_OFF;
	
	printf_P(PSTR("Cardreader turned off\n"));
}

uint8_t cardreader_is_powered(){
	return cardreader_power_state != CARDREADER_POWER_STATE_OFF;
}

void cardreader_process(void){
	if (cardreader_startup_event){
		if (cardreader_sys_get_card_status()) auth_state = AUTH_STATE_CARD_INSERTED;
		cardreader_display_set_backlight(power_get_status() & POWER_STATUS_AC_ON);
		cardreader_startup_event = 0;
	}
	
	if (auth_state == AUTH_STATE_CARD_INSERTED){
		auth_retry_count = 0;
		cardreader_select_auth_applet();
		auth_state = AUTH_STATE_SELECTING_AUTH_APPLET;
		return;
	}
	
	if (!transport_data_available()) return;
	
	uint8_t msg_buffer[100];
	
	uint16_t ret = transport_receive_message(msg_buffer, 100, 400);
	if TRANSPORT_IS_ERROR(ret){
		printf_P(PSTR("CardReader Recv Error: 0x%04x\n"), ret);
	} else {
		uint8_t length = ret & 0xFF;
		if (auth_state == AUTH_STATE_SELECTING_AUTH_APPLET){
			if (cardreader_select_auth_applet_result(msg_buffer, length)){
				cardreader_auth_request();
				auth_state = AUTH_STATE_AUTH_REQUESTED_SENT;
			} else {
				auth_state = AUTH_STATE_NONE;
				power_enable_battery_shutdown();
			}
		} else if (auth_state == AUTH_STATE_AUTH_REQUESTED_SENT){
			if (cardreader_auth_request_result(msg_buffer,length)){
				cardreader_update_key();
				auth_state = AUTH_STATE_AUTH_VERYFIED;
			} else if (auth_retry_count < AUTH_RETRY_MAX) {
				printf_P(PSTR("-- Auth Retry --\n"));
				cardreader_auth_request();
				auth_state = AUTH_STATE_AUTH_REQUESTED_SENT;
				auth_retry_count++;
			} else {
				auth_state = AUTH_STATE_NONE;
				power_enable_battery_shutdown();
			}
		} else if (auth_state == AUTH_STATE_AUTH_VERYFIED){
			cardreader_update_key_result(msg_buffer, length);
			auth_state = AUTH_STATE_NONE;
			power_enable_battery_shutdown();
		}
	}
}

void cardreader_tick(void){
	if (state_timeout) {
		state_timeout--;
		if (state_timeout == 0 && auth_state != AUTH_STATE_NONE){
			auth_state = AUTH_STATE_NONE;
			power_enable_battery_shutdown();
			cardreader_timeout();
		}
	}
}

uint8_t check_card_status(uint8_t* msg_buffer, uint8_t length, uint8_t expected_body_length){
	if (length < 2) {
		printf_P(PSTR(" Error, answer too short (%i)\n"), length);
		return 0;
	}
	if (CARD_RESULT_BODY_LENGTH(length) != expected_body_length){
		printf_P(PSTR(" Error, answer length invalid (expected: %i, got: %i)\n"), 
		                          expected_body_length, CARD_RESULT_BODY_LENGTH(length));
		return 0;
	}
	uint16_t status = CARD_RESULT_STATUS(msg_buffer);
	if (status == CARD_STATUS_SUCCESS) {
		printf_P(PSTR(" OK\n"));
		return 1;
	} else {
		printf_P(PSTR(" Error 0x%04x\n"), status);
		return 0;
	}
}

void cardreader_select_auth_applet(void){
	printf_P(PSTR("Selecting auth Applet..."));
	cardreader_display_clear();
	cardreader_display_write_sz_P(PSTR("Selecting Applet"));
	
	cardreader_card_message_t msg = {CARDREADER_MSG_TYPE_CARD};
	memcpy(&msg.header, CARD_CMD_SELECT_APPLET, sizeof(ISO7816_APDU_Header));
	memcpy(&msg.body, CARD_AUTH_APPLET_NAME, 5);
	MGMT_transmit_buffer_hex((uint8_t*)&msg, 10); 
	transport_send_message((uint8_t*)&msg, 10);
}
uint8_t cardreader_select_auth_applet_result(uint8_t* msg_buffer, uint8_t length){
	uint8_t ret = check_card_status(msg_buffer, length, 0);
	cardreader_display_show_result(ret);
	return ret;
}

void cardreader_auth_request(){
	printf_P(PSTR("Sending auth request..."));
	cardreader_display_clear();
	cardreader_display_write_sz_P(PSTR("Authenticating.."));
	
	cardreader_card_message_t msg = {CARDREADER_MSG_TYPE_CARD};
	memcpy(&msg.header, CARD_CMD_AUTH, sizeof(ISO7816_APDU_Header));
	memcpy(&msg.body, keystore_get_salt(), sizeof(SALT));
#ifdef CARDREADER_DEBUG
	MGMT_transmit_buffer_hex((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header)+sizeof(SALT)); 
#endif
	transport_send_message((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header)+sizeof(SALT));
}


uint8_t cardreader_auth_request_result(uint8_t* msg_buffer, uint8_t length){
	uint8_t ret = check_card_status(msg_buffer, length, sizeof(HASH)+sizeof(SALT));
	if (!ret) {cardreader_display_show_result(0); return 0;}
	
#ifdef CARDREADER_DEBUG
	printf_P(PSTR("Msg Response: "));
	MGMT_transmit_buffer_hex(msg_buffer, length);
	printf_P(PSTR("\n"));
#endif
	
	uint8_t* keyhash = CARD_RESULT_BODY(msg_buffer);
	memcpy(card_challenge, (CARD_RESULT_BODY(msg_buffer) + sizeof(HASH)), sizeof(SALT));
	
	printf_P(PSTR("Card Response: "));
#ifdef CARDREADER_DEBUG
	MGMT_transmit_buffer_hex(keyhash, sizeof(HASH));
#endif
	
	keyslot_index = keystore_find_key(keyhash);
	if (keyslot_index == -1){
			printf_P(PSTR(" Invalid :(\n"));
			cardreader_display_move(0,1);
			cardreader_display_write_sz_P(PSTR("Invalid :("));
			return 0;
	}
	printf_P(PSTR(" Valid! (Slot: %i) :)\n"), keyslot_index);
	
#ifdef CARDREADER_DEBUG
	printf_P(PSTR("Card Challange: ")); MGMT_transmit_buffer_hex(card_challenge, sizeof(SALT));
	printf_P(PSTR("\n"));
#endif
	
	cardreader_display_show_result(1);
	return 1;
}

void cardreader_update_key(){
	cardreader_card_message_t msg = {CARDREADER_MSG_TYPE_CARD};
	memcpy(&msg.header, CARD_CMD_UPDATE_KEY, sizeof(ISO7816_APDU_Header));
	
	HASH card_challenge_response;
	
	printf_P(PSTR("Updating card key..."));
	cardreader_display_clear();
	cardreader_display_write_sz_P(PSTR("Updating Key..."));
	
	keystore_gen_keydelta(new_key_delta);
#ifdef CARDREADER_DEBUG
	printf_P(PSTR("KeyDelta: "));
	MGMT_transmit_buffer_hex(new_key_delta, sizeof(KEY));
#endif
	memcpy(&msg.body, new_key_delta, sizeof(KEY));
	
	uint8_t ret = keystore_calc_keyhash(keyslot_index, card_challenge, card_challenge_response);
	if (ret){
#ifdef CARDREADER_DEBUG
		printf_P(PSTR("\nChallange Response: "));
		MGMT_transmit_buffer_hex(card_challenge_response, sizeof(HASH));
#endif
		memcpy(((uint8_t*)(&msg.body)) + sizeof(KEY), card_challenge_response, sizeof(HASH));
#ifdef CARDREADER_DEBUG
		printf_P(PSTR(" "));
		
		MGMT_transmit_buffer_hex((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header)+ sizeof(KEY)+sizeof(HASH));
#endif
		transport_send_message((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header)+ sizeof(KEY)+sizeof(HASH));
	} else {
		printf_P(PSTR("Failed to calculate key hash.\n"));
	}
}

uint8_t cardreader_update_key_result(uint8_t* msg_buffer, uint8_t length){
	uint8_t ret = check_card_status(msg_buffer, length, 0);
	if (!ret){
		cardreader_display_show_result(0);
		return 0;
	}
	
	printf_P(PSTR("Keystore update slot %i\n"), keyslot_index);
	if (keystore_update_slot(keyslot_index, new_key_delta) == 0){
		printf_P(PSTR("Keystore update failed!"));
		cardreader_display_move(0,1);
		cardreader_display_write_sz_P(PSTR("KS update failed"));
		return 0;
	}
	
	printf_P(PSTR("Update okay.\n"));
	
// 	cardreader_display_show_result(1);
	
	cardreader_display_clear();
	if (toggle_lock_unlock(cardreader_display_write_sz_P)){
// 		cardreader_display_write_sz_P(PSTR("Auth Successful"));
		cardreader_display_move(0,1);
		cardreader_display_write_sz_P(PSTR("Remove Card"));
	}
	
	keystore_update_salt();
	
	return 1;
}
uint8_t cardreader_init_card_key(KEY key){
	cardreader_card_message_t msg = {CARDREADER_MSG_TYPE_CARD};
	memcpy(&msg.header, CARD_CMD_INIT_KEY, sizeof(ISO7816_APDU_Header));
	memcpy(&msg.body, key, sizeof(KEY));
	transport_send_message((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header)+ sizeof(KEY));
	
	uint8_t card_status[4];
	uint16_t length = transport_receive_message((uint8_t*)&card_status, 4, 10000);
	if (TRANSPORT_IS_ERROR(length)){
		printf_P(PSTR("Transport Error %04x\n"), length);
		return 0;
	} else {
		return check_card_status(card_status, length, 0);
	}
}

uint8_t cardreader_clear_card_key(void){
	cardreader_card_message_t msg = {CARDREADER_MSG_TYPE_CARD};
	memcpy(&msg.header, CARD_CMD_CLEAR_KEY, sizeof(ISO7816_APDU_Header));
	transport_send_message((uint8_t*)&msg, 1+sizeof(ISO7816_APDU_Header));
	
	uint8_t card_status[4];
	uint16_t length = transport_receive_message((uint8_t*)&card_status, 4, 10000);
	if (TRANSPORT_IS_ERROR(length)){
		printf_P(PSTR("Transport Error %04x\n"), length);
		return 0;
	} else {
		return check_card_status(card_status, length, 0);
	}
}

void cardreader_timeout(void){
	printf_P(PSTR(" Timeout\n"));
	keystore_update_salt();
}

uint8_t cardreader_sys_get_card_status(void){
	cardreader_sys_message_t msg = {CARDREADER_MSG_TYPE_SYS};
	msg.command = CARDREADER_SYS_MSG_GET_CARD_STATUS;
	transport_send_message((uint8_t*)&msg, 2);
	
	uint8_t card_status;
	uint8_t ret = transport_receive_message(&card_status, 1, 3000);
	if (ret == 1) return card_status == CARD_STATUS_READY;
	return 0;
}

void cardreader_display_clear(void){
	cardreader_display_message_t msg = {CARDREADER_MSG_TYPE_DISPALY};
	msg.command = CARDREADER_DISPLAY_MSG_CLEAR;
	transport_send_message((uint8_t*)&msg, 2);
	
	uint8_t success;
	transport_receive_message(&success, 1, 3000);
}

void cardreader_display_move(uint8_t x, uint8_t y){
	cardreader_display_message_t msg = {CARDREADER_MSG_TYPE_DISPALY};
	msg.command = CARDREADER_DISPLAY_MSG_SET_POS;
	msg.body[0] = x;
	msg.body[1] = y;
	transport_send_message((uint8_t*)&msg, 4);
	
	uint8_t success;
	transport_receive_message(&success, 1, 3000);
}

void cardreader_display_write_sz(char* string){
	cardreader_display_message_t msg = {CARDREADER_MSG_TYPE_DISPALY};
	msg.command = CARDREADER_DISPLAY_MSG_WRITE;
	uint16_t len = strlen(string);
	if (len > 253) len = 253;
	memcpy(&(msg.body), string, len);
	transport_send_message((uint8_t*)&msg, len+2);
	
	uint8_t success;
	transport_receive_message(&success, 1, 3000);
}

void cardreader_display_write_sz_P(const char* string){
	cardreader_display_message_t msg = {CARDREADER_MSG_TYPE_DISPALY};
	msg.command = CARDREADER_DISPLAY_MSG_WRITE;
	uint16_t len = strlen_P(string);
	if (len > 253) len = 253;
	memcpy_P(&(msg.body), string, len);
	transport_send_message((uint8_t*)&msg, len+2);
	
	uint8_t success;
	transport_receive_message(&success, 1, 3000);
}

void cardreader_display_show_result(uint8_t success){
	cardreader_display_move(0,1);
	if (success) {
		cardreader_display_write_sz_P(PSTR("OK              "));
	} else {
		cardreader_display_write_sz_P(PSTR("Failed :(       "));
	}
}


void cardreader_display_set_backlight(uint8_t on){
	cardreader_display_message_t msg = {CARDREADER_MSG_TYPE_DISPALY};
	msg.command = CARDREADER_DISPLAY_MSG_SET_BACKLIGHT;
	msg.body[0] = on ? 1 : 0;
	transport_send_message((uint8_t*)&msg,2+1);
	
	uint8_t success;
	transport_receive_message(&success, 1, 3000);
}