From ce338a9727c7d3ea69398e8c6a2b372bdf226f81 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 17 May 2012 17:02:49 +0900 Subject: [PATCH] implement downloading program --- ChangeLog | 22 ++++ src/gnuk.h | 5 + src/openpgp.c | 46 ++++++++- src/usb-icc.c | 11 ++ src/usb_lld.c | 104 +++++++++++++------ src/usb_lld.h | 10 +- src/usb_prop.c | 100 ++++++++++++------ tool/gnuk_upgrade.py | 239 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 471 insertions(+), 66 deletions(-) create mode 100755 tool/gnuk_upgrade.py diff --git a/ChangeLog b/ChangeLog index 703ed63..4bfe9dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,25 @@ 2012-05-17 Niibe Yutaka + * tool/gnuk_upgrade.py: New tool. + + * src/gnuk.h (ICC_STATE_EXITED, ICC_STATE_EXEC_REQUESTED): New. + + * src/openpgp.c (INS_EXTERNAL_AUTHENTICATE) + (cmd_external_authenticate): New. + (INS_GET_CHALLENGE, cmd_get_challenge): New. + + * src/usb-icc.c (USBthread): Finish the thread with + ICC_STATE_EXITED, after EXTERNAL_AUTHENTICATE. + * src/usb_prop.c (gnuk_setup_endpoints_for_interface): Add STOP argument. (gnuk_usb_event): Disable all endpoints when configure(0). + (vcom_port_data_setup): Check direction and support + USB_CDC_REQ_SET_LINE_CODING. + (vcom_port_setup_with_nodata): Check direction. + (gnuk_setup_with_data): Check direction and add FSIJ_GNUK device + requests. + (gnuk_setup_with_nodata): Likewise. * src/usb_lld.c (LAST_OUT_DATA): Remove. (handle_datastage_out): Cleanup and call st103_ep_set_rxtx_status. @@ -10,6 +27,11 @@ st103_ep_set_tx_status. (handle_setup0): Likewise. (handle_out0): Remove LAST_OUT_DATA. + (std_none, std_get_status, std_clear_feature, std_set_feature) + (std_set_address, std_get_descriptor, std_get_configuration) + (std_set_configuration, std_get_interface, std_set_interface): + Check direction. + (handle_setup0): Add length for setup_with_data 2012-05-16 Niibe Yutaka diff --git a/src/gnuk.h b/src/gnuk.h index 1eb923f..1b1874a 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -76,6 +76,9 @@ enum icc_state ICC_STATE_EXECUTE, /* Busy4 */ ICC_STATE_RECEIVE, /* APDU Received Partially */ ICC_STATE_SEND, /* APDU Sent Partially */ + + ICC_STATE_EXITED, /* ICC Thread Terminated */ + ICC_STATE_EXEC_REQUESTED, /* Exec requested */ }; extern enum icc_state *icc_state_p; @@ -385,3 +388,5 @@ extern uint8_t pin_input_len; extern int pinpad_getline (int msg_code, systime_t timeout); #endif + +extern uint8_t __heap_base__, __heap_end__; diff --git a/src/openpgp.c b/src/openpgp.c index 88c92fc..d062f11 100644 --- a/src/openpgp.c +++ b/src/openpgp.c @@ -1,7 +1,7 @@ /* * openpgp.c -- OpenPGP card protocol support * - * Copyright (C) 2010, 2011 Free Software Initiative of Japan + * Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan * Author: NIIBE Yutaka * * This file is a part of Gnuk, a GnuPG USB Token implementation. @@ -39,6 +39,8 @@ #define INS_PSO 0x2a #define INS_RESET_RETRY_COUNTER 0x2c #define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47 +#define INS_EXTERNAL_AUTHENTICATE 0x82 +#define INS_GET_CHALLENGE 0x84 #define INS_INTERNAL_AUTHENTICATE 0x88 #define INS_SELECT_FILE 0xa4 #define INS_READ_BINARY 0xb0 @@ -821,6 +823,43 @@ cmd_write_binary (void) } +static void +cmd_external_authenticate (void) +{ + DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n"); + + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + { + GPG_SECURITY_FAILURE (); + return; + } + + chThdTerminate (chThdSelf ()); + set_res_sw (0xff, 0xff); + DEBUG_INFO ("EXTERNAL AUTHENTICATE done.\r\n"); +} + +static void +cmd_get_challenge (void) +{ + const uint8_t *rand; + int i; + + DEBUG_INFO (" - GET CHALLENGE\r\n"); + + for (i = 0; i < 6; i++) + { + rand = random_bytes_get (); + memcpy (res_APDU + i * 16, rand, 16); + random_bytes_free (rand); + } + + res_APDU_size = 96; + GPG_SUCCESS (); + DEBUG_INFO ("GET CHALLENGE done.\r\n"); +} + + struct command { uint8_t command; @@ -833,11 +872,14 @@ const struct command cmds[] = { { INS_PSO, cmd_pso }, { INS_RESET_RETRY_COUNTER, cmd_reset_user_password }, { INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp }, + { INS_EXTERNAL_AUTHENTICATE, /* Not in OpenPGP card protocol */ + cmd_external_authenticate }, + { INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */ { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate }, { INS_SELECT_FILE, cmd_select_file }, { INS_READ_BINARY, cmd_read_binary }, { INS_GET_DATA, cmd_get_data }, - { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */ + { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */ #if defined(CERTDO_SUPPORT) { INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */ #endif diff --git a/src/usb-icc.c b/src/usb-icc.c index 1f875bc..67b04e5 100644 --- a/src/usb-icc.c +++ b/src/usb-icc.c @@ -1273,6 +1273,7 @@ icc_handle_timeout (struct ccid *c) static struct ccid ccid; +#define GPG_THREAD_TERMINATED 0xffff msg_t USBthread (void *arg) @@ -1303,6 +1304,16 @@ USBthread (void *arg) else if (m == EV_EXEC_FINISHED) if (c->icc_state == ICC_STATE_EXECUTE) { + if (c->a->sw == GPG_THREAD_TERMINATED) + { + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + c->state = APDU_STATE_RESULT; + icc_send_data_block (c, 0); + c->icc_state = ICC_STATE_EXITED; + break; + } + c->a->cmd_apdu_data_len = 0; c->sw1sw2[0] = c->a->sw >> 8; c->sw1sw2[1] = c->a->sw & 0xff; diff --git a/src/usb_lld.c b/src/usb_lld.c index f90cd8c..13fa6c3 100644 --- a/src/usb_lld.c +++ b/src/usb_lld.c @@ -4,8 +4,6 @@ #define USB_MAX_PACKET_SIZE 64 /* For FS device */ -#define RECIPIENT 0x1F /* Mask to get recipient */ - enum STANDARD_REQUESTS { GET_STATUS = 0, @@ -450,25 +448,27 @@ static void handle_datastage_in (void) st103_ep_set_tx_status (ENDP0, EP_TX_VALID); } -typedef int (*HANDLER) (uint8_t rcp, +typedef int (*HANDLER) (uint8_t req, uint16_t value, uint16_t index, uint16_t length); -static int std_none (uint8_t rcp, +static int std_none (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { - (void)rcp; (void)value; (void)index; (void)length; + (void)req; (void)value; (void)index; (void)length; return USB_UNSUPPORT; } -static int std_get_status (uint8_t rcp, +static int std_get_status (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { static uint16_t status_info; + uint8_t rcp = req & RECIPIENT; status_info = 0; /* Reset Status Information */ data_p->addr = (uint8_t *)&status_info; - if (value != 0 || length != 2 || (index >> 8) != 0) + if (value != 0 || length != 2 || (index >> 8) != 0 + || (req & REQUEST_DIR) == 0) return USB_UNSUPPORT; if (rcp == DEVICE_RECIPIENT) @@ -540,9 +540,14 @@ static int std_get_status (uint8_t rcp, return USB_UNSUPPORT; } -static int std_clear_feature (uint8_t rcp, uint16_t value, +static int std_clear_feature (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + if (rcp == DEVICE_RECIPIENT) { if (length != 0 || index != 0) @@ -598,9 +603,14 @@ static int std_clear_feature (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_set_feature (uint8_t rcp, uint16_t value, +static int std_set_feature (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + if (rcp == DEVICE_RECIPIENT) { if (length != 0 || index != 0) @@ -646,9 +656,14 @@ static int std_set_feature (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_set_address (uint8_t rcp, uint16_t value, +static int std_set_address (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + if (rcp == DEVICE_RECIPIENT) { if (length == 0 && value <= 127 && index == 0 @@ -659,9 +674,14 @@ static int std_set_address (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_get_descriptor (uint8_t rcp, uint16_t value, +static int std_get_descriptor (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 0) + return USB_UNSUPPORT; + (void)length; if (rcp == DEVICE_RECIPIENT) return (*method_p->get_descriptor) ((value >> 8), index, value); @@ -669,9 +689,14 @@ static int std_get_descriptor (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_get_configuration (uint8_t rcp, uint16_t value, +static int std_get_configuration (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 0) + return USB_UNSUPPORT; + (void)value; (void)index; (void)length; if (rcp == DEVICE_RECIPIENT) { @@ -683,9 +708,14 @@ static int std_get_configuration (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_set_configuration (uint8_t rcp, uint16_t value, +static int std_set_configuration (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + if (rcp == DEVICE_RECIPIENT && index == 0 && length == 0) { int r; @@ -698,9 +728,14 @@ static int std_set_configuration (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_get_interface (uint8_t rcp, uint16_t value, +static int std_get_interface (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 0) + return USB_UNSUPPORT; + if (rcp == INTERFACE_RECIPIENT) { if (value != 0 || (index >> 8) != 0 || length != 1) @@ -715,9 +750,14 @@ static int std_get_interface (uint8_t rcp, uint16_t value, return USB_UNSUPPORT; } -static int std_set_interface (uint8_t rcp, uint16_t value, +static int std_set_interface (uint8_t req, uint16_t value, uint16_t index, uint16_t length) { + uint8_t rcp = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + if (rcp == INTERFACE_RECIPIENT) { int r; @@ -759,7 +799,6 @@ static void handle_setup0 (void) uint8_t req; int r = USB_UNSUPPORT; HANDLER handler; - uint8_t type_rcp; pw = (uint16_t *)(PMA_ADDR + (uint8_t *)(st103_get_rx_addr (ENDP0) * 2)); w = *pw++; @@ -776,34 +815,35 @@ static void handle_setup0 (void) data_p->len = 0; data_p->offset = 0; - type_rcp = (ctrl_p->bmRequestType & (REQUEST_TYPE | RECIPIENT)); - if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) /* Interface */ - || (ctrl_p->bmRequestType & REQUEST_TYPE) == VENDOR_REQUEST) - { - if (ctrl_p->wLength == 0) - r = (*method_p->setup_with_nodata) (type_rcp, req, ctrl_p->wIndex); - else - { - (*method_p->setup_with_data) (type_rcp, req, ctrl_p->wIndex); - if (data_p->len != 0) - r = USB_SUCCESS; - } - } - else if ((ctrl_p->bmRequestType & REQUEST_TYPE) == STANDARD_REQUEST) + if ((ctrl_p->bmRequestType & REQUEST_TYPE) == STANDARD_REQUEST) { if (req < TOTAL_REQUEST) { handler = std_request_handler[req]; - r = (*handler) (ctrl_p->bmRequestType & RECIPIENT, + r = (*handler) (ctrl_p->bmRequestType, ctrl_p->wValue, ctrl_p->wIndex, ctrl_p->wLength); } } + else + { + if (ctrl_p->wLength == 0) + r = (*method_p->setup_with_nodata) (ctrl_p->bmRequestType, + req, ctrl_p->wIndex); + else + { + (*method_p->setup_with_data) (ctrl_p->bmRequestType, req, + ctrl_p->wIndex, ctrl_p->wLength); + if (data_p->len != 0) + r = USB_SUCCESS; + } + } + if (r != USB_SUCCESS) dev_p->state = STALLED; else { - if (ctrl_p->bmRequestType & 0x80) + if (ctrl_p->bmRequestType & REQUEST_DIR) { uint32_t len = ctrl_p->wLength; diff --git a/src/usb_lld.h b/src/usb_lld.h index f27d017..7355ba3 100644 --- a/src/usb_lld.h +++ b/src/usb_lld.h @@ -39,10 +39,12 @@ enum DESCRIPTOR_TYPE ENDPOINT_DESCRIPTOR }; +#define REQUEST_DIR 0x80 /* Mask to get request dir */ #define REQUEST_TYPE 0x60 /* Mask to get request type */ #define STANDARD_REQUEST 0x00 /* Standard request */ #define CLASS_REQUEST 0x20 /* Class request */ #define VENDOR_REQUEST 0x40 /* Vendor request */ +#define RECIPIENT 0x1F /* Mask to get recipient */ struct Descriptor { @@ -60,7 +62,8 @@ struct usb_device_method { void (*init) (void); void (*reset) (void); - void (*setup_with_data) (uint8_t rcp, uint8_t req_no, uint16_t index); + void (*setup_with_data) (uint8_t rcp, uint8_t req_no, uint16_t index, + uint16_t len); int (*setup_with_nodata) (uint8_t rcp, uint8_t req_no, uint16_t index); int (*get_descriptor) (uint8_t desc_type, uint16_t index, uint16_t value); int (*event) (uint8_t event_type, uint16_t value); @@ -141,3 +144,8 @@ extern uint8_t usb_lld_current_configuration (void); extern void usb_lld_set_feature (uint8_t feature); extern void usb_lld_set_data_to_send (const void *p, size_t len); + +extern inline void usb_lld_set_data_to_recv (void *p, size_t len) +{ + usb_lld_set_data_to_send ((const void *)p, len); +} diff --git a/src/usb_prop.c b/src/usb_prop.c index 1787156..f2996f9 100644 --- a/src/usb_prop.c +++ b/src/usb_prop.c @@ -28,6 +28,7 @@ #include "ch.h" #include "usb_lld.h" #include "usb_conf.h" +#include "gnuk.h" #ifdef ENABLE_VIRTUAL_COM_PORT #include "usb-cdc.h" @@ -40,7 +41,7 @@ struct line_coding uint8_t datatype; }; -static const struct line_coding line_coding = { +static struct line_coding line_coding = { 115200, /* baud rate: 115200 */ 0x00, /* stop bits: 1 */ 0x00, /* parity: none */ @@ -48,20 +49,19 @@ static const struct line_coding line_coding = { }; static void -vcom_port_data_setup (uint8_t RequestNo) +vcom_port_data_setup (uint8_t req, uint8_t req_no) { - if (RequestNo != USB_CDC_REQ_GET_LINE_CODING) - return; + if ((req & REQUEST_DIR) == 1 && req_no == USB_CDC_REQ_GET_LINE_CODING) + usb_lld_set_data_to_send (&line_coding, sizeof(line_coding)); - /* RequestNo == USB_CDC_REQ_SET_LINE_CODING is not supported */ - - usb_lld_set_data_to_send (&line_coding, sizeof(line_coding)); + if ((req & REQUEST_DIR) == 0 && req_no == USB_CDC_REQ_SET_LINE_CODING) + usb_lld_set_data_to_recv (&line_coding, sizeof(line_coding)); } static int -vcom_port_setup_with_nodata (uint8_t RequestNo) +vcom_port_setup_with_nodata (uint8_t req, uint8_t req_no) { - if (RequestNo == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + if ((req & REQUEST_DIR) == 0 && req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE) /* Do nothing and success */ return USB_SUCCESS; @@ -183,26 +183,54 @@ static const uint8_t data_rate_table[] = { 0x80, 0x25, 0, 0, }; /* dwDataRate */ static const uint8_t lun_table[] = { 0, 0, 0, 0, }; #endif +static const uint8_t *mem_info[] = { &__heap_base__, &__heap_end__, }; + +#define USB_FSIJ_GNUK_MEMINFO 0 +#define USB_FSIJ_GNUK_DOWNLOAD 1 +#define USB_FSIJ_GNUK_EXEC 2 + static void -gnuk_setup_with_data (uint8_t recipient, uint8_t RequestNo, uint16_t index) +gnuk_setup_with_data (uint8_t req, uint8_t req_no, uint16_t index, + uint16_t len) { - if (recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */ + uint8_t recipient = req & RECIPIENT; + + if (recipient == (VENDOR_REQUEST | DEVICE_RECIPIENT)) + { + if ((req & REQUEST_DIR) == 1 && req_no == USB_FSIJ_GNUK_MEMINFO) + usb_lld_set_data_to_send (mem_info, sizeof (mem_info)); + else if ((req & REQUEST_DIR) == 0 && req_no == USB_FSIJ_GNUK_DOWNLOAD) + { + if (icc_state_p == NULL || *icc_state_p != ICC_STATE_EXITED) + return; + + if ((uint32_t)(index * 0x100) < (uint32_t)&__heap_base__ + || (uint32_t)((index * 0x100) + len) > (uint32_t)&__heap_end__) + return; + + usb_lld_set_data_to_recv ((void *)0x20000000 + index*0x100, len); + } + } + else if (recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { if (index == 0) { - if (RequestNo == USB_CCID_REQ_GET_CLOCK_FREQUENCIES) + if ((req & REQUEST_DIR) == 1 + && req_no == USB_CCID_REQ_GET_CLOCK_FREQUENCIES) usb_lld_set_data_to_send (freq_table, sizeof (freq_table)); - else if (RequestNo == USB_CCID_REQ_GET_DATA_RATES) - usb_lld_set_data_to_send (data_rate_table, sizeof (data_rate_table)); + else if ((req & REQUEST_DIR) == 1 + && req_no == USB_CCID_REQ_GET_DATA_RATES) + usb_lld_set_data_to_send (data_rate_table, + sizeof (data_rate_table)); } #ifdef ENABLE_VIRTUAL_COM_PORT else if (index == 1) - vcom_port_data_setup (RequestNo); + vcom_port_data_setup (req, req_no); #endif #ifdef PINPAD_DND_SUPPORT else if (index == MSC_INTERFACE_NO) { - if (RequestNo == MSC_GET_MAX_LUN_COMMAND) + if ((req & REQUEST_DIR) == 1 && req_no == MSC_GET_MAX_LUN_COMMAND) usb_lld_set_data_to_send (lun_table, sizeof (lun_table)); } #endif @@ -211,12 +239,28 @@ gnuk_setup_with_data (uint8_t recipient, uint8_t RequestNo, uint16_t index) static int -gnuk_setup_with_nodata (uint8_t recipient, uint8_t RequestNo, uint16_t index) +gnuk_setup_with_nodata (uint8_t req, uint8_t req_no, uint16_t index) { - if (recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */ + uint8_t recipient = req & RECIPIENT; + + if ((req & REQUEST_DIR) == 1) + return USB_UNSUPPORT; + + if (recipient == (VENDOR_REQUEST | DEVICE_RECIPIENT)) + { + if (req_no == USB_FSIJ_GNUK_EXEC) + { + if (icc_state_p == NULL || *icc_state_p != ICC_STATE_EXITED) + return USB_UNSUPPORT; + + *icc_state_p = ICC_STATE_EXEC_REQUESTED; + return USB_SUCCESS; + } + } + else if (recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) if (index == 0) { - if (RequestNo == USB_CCID_REQ_ABORT) + if (req_no == USB_CCID_REQ_ABORT) /* wValue: bSeq, bSlot */ /* Abortion is not supported in Gnuk */ return USB_UNSUPPORT; @@ -225,24 +269,20 @@ gnuk_setup_with_nodata (uint8_t recipient, uint8_t RequestNo, uint16_t index) } #ifdef ENABLE_VIRTUAL_COM_PORT else if (index == 1) - return vcom_port_setup_with_nodata (RequestNo); + return vcom_port_setup_with_nodata (req, req_no); #endif #ifdef PINPAD_DND_SUPPORT else if (index == MSC_INTERFACE_NO) { - if (RequestNo == MSC_MASS_STORAGE_RESET_COMMAND) + if (req_no == MSC_MASS_STORAGE_RESET_COMMAND) { /* Should call resetting MSC thread, something like msc_reset() */ return USB_SUCCESS; } - else - return USB_UNSUPPORT; } #endif - else - return USB_UNSUPPORT; - else - return USB_UNSUPPORT; + + return USB_UNSUPPORT; } static int @@ -278,6 +318,8 @@ gnuk_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value) static int gnuk_usb_event (uint8_t event_type, uint16_t value) { + int i; + switch (event_type) { case USB_EVENT_RESET: @@ -288,10 +330,6 @@ static int gnuk_usb_event (uint8_t event_type, uint16_t value) case USB_EVENT_CONFIG: if (usb_lld_current_configuration () == 0) { - int i; - extern void *main_thread; -#define LED_STATUS_MODE (8) - if (value != 1) return USB_UNSUPPORT; diff --git a/tool/gnuk_upgrade.py b/tool/gnuk_upgrade.py new file mode 100755 index 0000000..c041ef6 --- /dev/null +++ b/tool/gnuk_upgrade.py @@ -0,0 +1,239 @@ +#! /usr/bin/python + +""" +gnuk_upgrade.py - a tool to upgrade firmware of Gnuk Token + +Copyright (C) 2012 Free Software Initiative of Japan +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk 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. + +Gnuk 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 this program. If not, see . +""" + +from intel_hex import * +from struct import * +import sys, time, os, binascii, string + +# INPUT: binary file + +# Assume only single CCID device is attached to computer, and it's Gnuk Token + +import usb + +# USB class, subclass, protocol +CCID_CLASS = 0x0B +CCID_SUBCLASS = 0x00 +CCID_PROTOCOL_0 = 0x00 + +def icc_compose(msg_type, data_len, slot, seq, param, data): + return pack('BBBB', cls, ins, p1, p2) + else: + return pack('>BBBBBh', cls, ins, p1, p2, 0, data_len) + data + +# This class only supports Gnuk (for now) +class gnuk_token: + def __init__(self, device, configuration, interface): + """ + __init__(device, configuration, interface) -> None + Initialize the device. + device: usb.Device object. + configuration: configuration number. + interface: usb.Interface object representing the interface and altenate setting. + """ + if interface.interfaceClass != CCID_CLASS: + raise ValueError, "Wrong interface class" + if interface.interfaceSubClass != CCID_SUBCLASS: + raise ValueError, "Wrong interface sub class" + self.__devhandle = device.open() + try: + self.__devhandle.setConfiguration(configuration) + except: + pass + self.__devhandle.claimInterface(interface) + self.__devhandle.setAltInterface(interface) + + self.__intf = interface.interfaceNumber + self.__alt = interface.alternateSetting + self.__conf = configuration + + self.__bulkout = 2 + self.__bulkin = 0x81 + + self.__timeout = 10000 + self.__seq = 0 + + def icc_get_result(self): + msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout) + if len(msg) < 10: + print msg + raise ValueError, "icc_get_result" + msg_type = msg[0] + data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24) + slot = msg[5] + seq = msg[6] + status = msg[7] + error = msg[8] + chain = msg[9] + data = msg[10:] + # XXX: check msg_type, data_len, slot, seq, error + return (status, chain, data) + + def icc_get_status(self): + msg = icc_compose(0x65, 0, 0, self.__seq, 0, "") + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.__seq += 1 + status, chain, data = self.icc_get_result() + # XXX: check chain, data + return status + + def icc_power_on(self): + msg = icc_compose(0x62, 0, 0, self.__seq, 0, "") + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.__seq += 1 + status, chain, data = self.icc_get_result() + # XXX: check status, chain + return data # ATR + + def icc_power_off(self): + msg = icc_compose(0x63, 0, 0, self.__seq, 0, "") + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.__seq += 1 + status, chain, data = self.icc_get_result() + # XXX: check chain, data + return status + + def icc_send_data_block(self, data): + msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data) + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.__seq += 1 + return self.icc_get_result() + + def icc_send_cmd(self, data): + status, chain, data_rcv = self.icc_send_data_block(data) + if chain == 0: + return data_rcv + elif chain == 1: + d = data_rcv + while True: + msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "") + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.__seq += 1 + status, chain, data_rcv = self.icc_get_result() + # XXX: check status + d += data_rcv + if chain == 2: + break + elif chain == 3: + continue + else: + raise ValueError, "icc_send_cmd chain" + return d + else: + raise ValueError, "icc_send_cmd" + + def cmd_get_response(self, expected_len): + cmd_data = iso7816_compose(0xc0, 0x00, 0x00, [expected_len]) + response = self.icc_send_cmd(cmd_data) + return response + + def cmd_verify(self, who, passwd): + cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd) + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError, "cmd_verify" + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError, "cmd_verify" + + def cmd_select_openpgp(self): + cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\xD2\x76\x00\x01\x24\x01") + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError, "cmd_select_openpgp" + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError, ("%02x%02x" % (sw[0], sw[1])) + + def cmd_external_authenticate(self, who, signed): + cmd_data = iso7816_compose(0x82, 0x00, 0x00, signed) + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError, "cmd_external_authenticate" + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError, "cmd_external_authenticate" + +def compare(data_original, data_in_device): + i = 0 + for d in data_original: + if ord(d) != data_in_device[i]: + raise ValueError, "verify failed at %08x" % i + i += 1 + +def get_device(): + busses = usb.busses() + for bus in busses: + devices = bus.devices + for dev in devices: + for config in dev.configurations: + for intf in config.interfaces: + for alt in intf: + if alt.interfaceClass == CCID_CLASS and \ + alt.interfaceSubClass == CCID_SUBCLASS and \ + alt.interfaceProtocol == CCID_PROTOCOL_0: + return dev, config, alt + raise ValueError, "Device not found" + +def main(passwd, data): + dev, config, intf = get_device() + print "Device: ", dev.filename + print "Configuration: ", config.value + print "Interface: ", intf.interfaceNumber + icc = gnuk_token(dev, config, intf) + if icc.icc_get_status() == 2: + raise ValueError, "No ICC present" + elif icc.icc_get_status() == 1: + icc.icc_power_on() + icc.cmd_verify(3, passwd) + icc.cmd_select_openpgp() + challenge = icc.cmd_get_challenge() + signed = challenge + icc.cmd_external_authenticate(signed) + # set_configure(0) to disable all interfaces but control pipe + # check mem_info + # download flash install program + # exec + # ... + # Then, send upgrade program... + return 0 + +DEFAULT_PW3 = "12345678" + +if __name__ == '__main__': + passwd = DEFAULT_PW3 + if sys.argv[1] == '-p': + from getpass import getpass + passwd = getpass("Admin password: ") + sys.argv.pop(1) + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + print "%s: %d" % (filename, len(data)) + print "Downloading flash upgrade program" + main(passwd, data)