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);
}