diverta / onecard-fido

MIT License
0 stars 0 forks source link

[障害・解決済] 再起動したChromeブラウザーで、U2F Authenticateが再度失敗 #26

Closed makmorit closed 6 years ago

makmorit commented 6 years ago

確認された事象

U2FデモサイトでU2F Authenticateを実行したところ、何らかのBLE障害により2つ目のフレーム(CONT frame)が送信されず、エラーが発生しました。 (このとき、Chromeブラウザーはハング状態。ただし、1つ目のフレームが送信されなかった #25 の事象とは異なっています)

01_chrome

ハングしたChromeブラウザーを再起動させ、再度U2F Authenticateを実行しました。

02_chrome authenticate

今度は、Chromeブラウザーの子プロセスであるU2F管理ツールから「BLE接続エラーが発生しました。」というエラーメッセージが報告され、再度U2F Authenticateが失敗してしまいました。

03_

ログ

makmorit commented 6 years ago

原因および対策

1回目のChromeハングについての問題(送信された2件目以降のフレームがnRF52で受信されない)は、BLE接続の問題として、別個に切り分けたいと考えます。

本項では、2回目のU2F Authenticate再失敗についての問題に、スコープを限定するものといたします。

障害時の処理の流れ

One Card側で「INIT frame received again while CONT is expected」と判定するロジック[A]は以下の通り。

【ble_u2f_control_point.c】

void ble_u2f_control_point_receive(ble_gatts_evt_write_t *p_evt_write, ble_u2f_context_t *p_u2f_context)
{
    :
    NRF_LOG_DEBUG("ble_u2f_control_point_receive length=%u \r\n", control_point_buffer_length);

    if (control_point_buffer[0] & 0x80) {
        // 先頭データが2回連続で送信された場合はエラー
        if ((ble_header_t.CMD & 0x80) && ble_header_t.CONT == true) {
            NRF_LOG_ERROR("INIT frame received again while CONT is expected \r\n");
            ble_header_t.CMD = U2F_COMMAND_ERROR;
            ble_header_t.ERROR = U2F_ERR_INVALID_SEQ;

        } else {
            // BLEヘッダーとAPDUを初期化
            memset(&ble_header_t, 0, sizeof(BLE_HEADER_T));
            memset(&apdu_t, 0, sizeof(U2F_APDU_T));

            // 先頭パケットに対する処理を行う
            u2f_request_receive_leading_packet(
                p_u2f_context, &ble_header_t, &apdu_t);
        }
:

1件目のフレームがCONT frameである旨を設定するロジック[B]は以下の通り。

【ble_u2f_control_point.c】

static bool u2f_request_receive_leading_packet(ble_u2f_context_t *p_u2f_context, BLE_HEADER_T *p_ble_header, U2F_APDU_T *p_apdu)
{
    :
    // データ(APDUまたはPINGパケット)の長さを取得
    p_ble_header->LEN = (uint32_t)(
                         (control_point_buffer[1] << 8 & 0xFF00) 
                        + control_point_buffer[2]);
    p_ble_header->SEQ = 0xff;

    NRF_LOG_DEBUG("INIT frame: CMD(0x%02x) LEN(%d) SEQ(%d) \r\n", 
        p_ble_header->CMD, p_ble_header->LEN, p_ble_header->SEQ);
    :
    if (p_ble_header->LEN > BLE_U2F_MAX_RECV_CHAR_LEN - 3) {
        // BLEヘッダーに設定されたデータ長が
        // 61文字を超える場合、後続データがあると判断
        NRF_LOG_DEBUG("u2f_request_receive: CONT frame will receive \r\n");
        p_ble_header->CONT = true;
    } else {
        p_ble_header->CONT = false;
    }
    :

1回目の障害では、

その後発生した2回目の障害で、

というのが障害時の処理の流れになります。

対策

1件目の障害で、Disconnectされた(=トランザクションが終了した)にもかかわらず、ble_header_tが初期化されないのが問題のようです。

このような、トランザクションをまたがって使用されることがない変数は、トランザクション開始時(=Connect時)に初期化する必要があるかと存じます。

具体的には、Connect時に、以下のモジュール変数を初期化(0を設定)させるよう、nRF52側のプログラムを修正しようと考えます。

【ble_u2f_control_point.c】

// u2f control point(コマンドバッファ)には、
// 64バイトまで書込み可能とします
static uint8_t  control_point_buffer[BLE_U2F_MAX_RECV_CHAR_LEN];
static uint16_t control_point_buffer_length;

// リクエストデータに含まれる
// BLEヘッダー、APDU項目は
// このモジュール内で保持
static BLE_HEADER_T ble_header_t;
static U2F_APDU_T   apdu_t;
:
makmorit commented 6 years ago

対応後の確認

1a53e67 の修正により、1回目のU2F Authenticate障害発生--->Chrome再起動後、フレーム誤判定によるU2F Authenticate障害が発生しなくなったことを確認できました。

nRF52のUARTログで確認したところ「INIT frame received again while CONT is expected」とは判定されず、正しく「INIT frame: CMD(0x83) LEN(138) SEQ(255)」と判断されたようです。

APP:DEBUG:on_ble_evt_dispatch called (evt_id=0x10) 
:
:INFO:[BLE]BLE_GAP_EVT_CONNECTED
APP:INFO:[APP]Connected.
:
APP:DEBUG:on_ble_evt_dispatch called (evt_id=0x50) 
ble_u2f_control_point:DEBUG:ble_u2f_control_point_receive length=64 
ble_u2f_control_point:DEBUG:INIT frame: CMD(0x83) LEN(138) SEQ(255) 
ble_u2f_control_point:DEBUG:u2f_request_receive: CONT frame will receive 
ble_u2f_control_point_apdu:DEBUG:CLA(0x00) INS(0x02) P1(0x03) P2(0x00) 
ble_u2f_control_point_apdu:DEBUG:Lc(129 bytes) in Extended Length Encoding
ble_u2f_control_point_apdu:DEBUG:response_message_buffer allocated (1024 bytes) 
ble_u2f_control_point_apdu:DEBUG:INIT frame: received data (54 of 129) 
APP:DEBUG:on_ble_evt_dispatch called (evt_id=0x50) 
ble_u2f_control_point:DEBUG:ble_u2f_control_point_receive length=64 
ble_u2f_control_point:DEBUG:CONT frame: CMD(0x83) LEN(138) SEQ(0) 
ble_u2f_control_point_apdu:DEBUG:CONT frame: received data (117 bytes) 
APP:DEBUG:on_ble_evt_dispatch called (evt_id=0x50) 
ble_u2f_control_point:DEBUG:ble_u2f_control_point_receive length=15 
ble_u2f_control_point:DEBUG:CONT frame: CMD(0x83) LEN(138) SEQ(1) 
ble_u2f_control_point_apdu:DEBUG:Le(65536 bytes) in Extended Length Encoding 
ble_u2f_control_point_apdu:DEBUG:CONT frame: received data (129 bytes) 
ble_u2f_command:DEBUG:get_command_type: Authentication Request Message received 
ble_u2f_authenticate:DEBUG:ble_u2f_authenticate start 
:
ble_u2f_status:DEBUG:u2f_response_send (64bytes) 
ble_u2f_status:DEBUG:u2f_response_send (17bytes) 
ble_u2f_authenticate:DEBUG:ble_u2f_authenticate end 
:
:INFO:[BLE]BLE_GAP_EVT_DISCONNECTED
APP:INFO:[APP]Disconnected.
makmorit commented 6 years ago

「再起動したChromeブラウザーで、U2F Authenticateが再度失敗」する問題は解決したので、このIssueはクローズいたします。