diverta / onecard-fido

MIT License
0 stars 0 forks source link

[障害・解決済] 鍵・証明書未導入時にヘルスチェックがダウン #1

Closed makmorit closed 6 years ago

makmorit commented 6 years ago

確認された事象

鍵・証明書がU2Fトークン(One Card上のFIDO BLE U2Fサービス)に未導入の場合、U2F管理ツール(U2FMaintenanceTool.app)によりヘルスチェックを実行すると、リクエストがタイムアウトしてしまいます。 (11/30 16:40頃発覚)

ログ

U2F管理ツール(U2FMaintenanceTool.app)のログ

default 16:37:42.183740 +0900   U2FMaintenanceTool  Health check start
default 16:37:42.183957 +0900   U2FMaintenanceTool  FIDO U2Fデバイスのスキャンを開始します
default 16:37:42.590655 +0900   U2FMaintenanceTool  FIDO U2Fデバイスのスキャンを完了しました
default 16:37:44.999651 +0900   U2FMaintenanceTool  FIDO U2Fデバイスに接続しました。
default 16:37:44.004600 +0900   U2FMaintenanceTool  FIDO BLE U2Fサービスが見つかりました。
default 16:37:44.068907 +0900   U2FMaintenanceTool  受信データの監視を開始します。
default 16:37:44.321386 +0900   U2FMaintenanceTool  Sent request <83004900 01000000 0040124d c843bb8b a61f035a 7d093825 1f5dd4cb fc96f545 3b130d89 0a1cdbae 322023be 84e16cd6 ae529049 f1f1bbe9 ebb3a6db 3c870c3e>
default 16:37:44.572080 +0900   U2FMaintenanceTool  Sent request <0099245e 0d1c06b7 47deb300 00>
default 16:37:44.572260 +0900   U2FMaintenanceTool  リクエストを送信しました。
default 16:37:48.730677 +0900   U2FMaintenanceTool  FIDO U2Fデバイスの接続を切断しました。
default 16:38:04.578082 +0900   U2FMaintenanceTool  リクエストがタイムアウトしました。

nRF52のUARTログ

「response_message_buffer allocated (1024 bytes) 」のログ出力を最後に、ソフトデバイスがダウンしてしまった様子。

APP:INFO:[APP]Connected to a previously bonded device.
nrf_ble_gatt:DEBUG:Requesting to update ATT MTU to 67 bytes on connection 0x0.
nrf_ble_gatt:DEBUG:Requesting to update data length to 71 on connection 0x0.
ble_u2f_command:DEBUG:ble_u2f_command_initialize_context done 
:INFO:[BLE]BLE_GAP_EVT_CONNECTED
APP:INFO:[APP]Connected.
nrf_ble_gatt:DEBUG:Data length updated to 71 on connection 0x0.
nrf_ble_gatt:DEBUG:max_rx_octets: 71
nrf_ble_gatt:DEBUG:max_tx_octets: 71
nrf_ble_gatt:DEBUG:max_rx_time: 680
nrf_ble_gatt:DEBUG:max_tx_time: 680
APP:INFO:[APP]Connection secured: role: 1, conn_handle: 0x0, procedure: 0.
ble_u2f_pairing_lesc:INFO:Security connection updated: Security Mode=1, Level=2 
nrf_ble_gatt:DEBUG:Peer on connection 0x0 requested an ATT MTU of 1000 bytes.
nrf_ble_gatt:DEBUG:Updating ATT MTU to 67 bytes (desired: 67) on connection 0x0.
:INFO:[BLE]BLE_GATTS_EVT_WRITE
ble_u2f:DEBUG:on_cccd_write: Notification status changed to enabled 
ble_u2f_control_point:DEBUG:ble_u2f_control_point_receive length=64 
ble_u2f_control_point:DEBUG:INIT frame: CMD(0x83) LEN(73) SEQ(255) 
ble_u2f_control_point:DEBUG:u2f_request_receive: CONT frame will receive 
ble_u2f_control_point_apdu:DEBUG:CLA(0x00) INS(0x01) P1(0x00) P2(0x00) 
ble_u2f_control_point_apdu:DEBUG:Lc(64 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 64) 
nrf_ble_gatt:DEBUG:ATT MTU updated to 67 bytes on connection 0x0 (response).
ble_u2f_control_point:DEBUG:ble_u2f_control_point_receive length=13 
ble_u2f_control_point:DEBUG:CONT frame: CMD(0x83) LEN(73) SEQ(0) 
ble_u2f_control_point_apdu:DEBUG:Le(65536 bytes) in Extended Length Encoding 
ble_u2f_control_point_apdu:DEBUG:CONT frame: received data (64 bytes) 
ble_u2f_command:DEBUG:get_command_type: Registration Request Message received 
ble_u2f_register:DEBUG:ble_u2f_register start 
ble_u2f_flash:DEBUG:securekey_buffer allocated (1060 bytes) 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_generate_keypair start 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_generate_keypair: nrf_crypto_ecc_key_pair_generate() returns 0x00 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_generate_keypair end 
ble_u2f_util:DEBUG:signature_data_buffer allocated (162 bytes) 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_sign start 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_sign: nrf_crypto_hash_compute() returns 0x00 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_sign: nrf_crypto_ecdsa_sign_hash() returns 0x03 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_sign end 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_create_asn1_signature start 
ble_u2f_crypto:DEBUG:ble_u2f_crypto_create_asn1_signature end 
ble_u2f_util:DEBUG:response_message_buffer allocated (1024 bytes) 
makmorit commented 6 years ago

原因調査

ダウンした場所の特定

下記関数実行時のようです。

static bool create_registration_response_message(ble_u2f_context_t *p_u2f_context, nrf_value_length_t *p_signature)
{
    // メッセージを格納する領域を確保
    // 確保した領域は、共有情報に設定します
    if (ble_u2f_response_message_allocate(p_u2f_context) == false) {
        return false;
    }
    NRF_LOG_DEBUG("ble_u2f_response_message_allocate done \r\n");

    // 確保した領域の先頭アドレスを取得
    uint8_t *response_message_buffer = p_u2f_context->response_message_buffer;
    uint16_t offset = 0;

    // reserved(0x05)
    response_message_buffer[offset++] = 0x05;

    // 公開鍵
    offset += copy_publickey_data(response_message_buffer + offset);
    NRF_LOG_DEBUG("copy_publickey_data done \r\n");

    // キーハンドル長
    uint8_t keyhandle_length = sizeof(keyhandle_buffer);
    response_message_buffer[offset++] = keyhandle_length;

    // キーハンドル
    memcpy(response_message_buffer + offset, keyhandle_buffer, keyhandle_length);
    offset += keyhandle_length;
    NRF_LOG_DEBUG("keyhandle_buffer copied \r\n");

    // 証明書格納領域と長さを取得
    uint8_t *cert_buffer = ble_u2f_securekey_cert(p_u2f_context);
    uint32_t cert_buffer_length = ble_u2f_securekey_cert_length(p_u2f_context);
    NRF_LOG_DEBUG("cert_buffer get \r\n");

    // 証明書格納領域からコピー
    memcpy(response_message_buffer + offset, cert_buffer, cert_buffer_length);
    offset += cert_buffer_length;
    NRF_LOG_DEBUG("cert_buffer copied \r\n");

    // 署名格納領域からコピー
    memcpy(response_message_buffer + offset, p_signature->p_value, p_signature->length);
    offset += p_signature->length;
    NRF_LOG_DEBUG("p_signature copied \r\n");

    if (p_u2f_context->p_apdu->Le < offset) {
        // Leを確認し、メッセージのバイト数がオーバーする場合
        // エラーレスポンスを送信するよう指示
        NRF_LOG_ERROR("Response message length(%d) exceeds Le(%d) \r\n", offset, p_u2f_context->p_apdu->Le);
        p_u2f_context->p_ble_header->STATUS_WORD = U2F_SW_WRONG_LENGTH;
        return false;
    }

    // ステータスワード
    ble_u2f_set_status_word(response_message_buffer + offset, U2F_SW_NO_ERROR);
    offset += 2;
    NRF_LOG_DEBUG("ble_u2f_set_status_word done \r\n");

    // メッセージのバイト数をセット
    p_u2f_context->response_message_buffer_length = offset;

    return true;
}

出力されたUARTプリント

ble_u2f_util:DEBUG:response_message_buffer allocated (1024 bytes)
ble_u2f_register:DEBUG:ble_u2f_response_message_allocate done
ble_u2f_register:DEBUG:copy_publickey_data done
ble_u2f_register:DEBUG:keyhandle_buffer copied
ble_u2f_register:DEBUG:cert_buffer get

メッセージが「ble_u2f_register:DEBUG:cert_buffer get」で切れているので、下記コード実行時にダウンしたものと切り分けています。

    // 証明書格納領域からコピー
    memcpy(response_message_buffer + offset, cert_buffer, cert_buffer_length);
makmorit commented 6 years ago

ダウンの原因を発生させた場所の特定

鍵・証明書を、Flash ROMから取得している処理は下記の通りです。

void ble_u2f_register_do_process(ble_u2f_context_t *p_u2f_context)
{
:
    if (ble_u2f_flash_keydata_read(p_u2f_context) == false) {
        // 秘密鍵と証明書をFlash ROMから読込
        // NGであれば、エラーレスポンスを生成して戻す
        ble_u2f_send_error_response(p_u2f_context, 0x01);
        return;
    }
:
}
bool ble_u2f_flash_keydata_read(ble_u2f_context_t *p_u2f_context)
{
:
    // 一時領域(確保済み)のアドレスを取得
    uint32_t *keydata_buffer = keydata_buffer_allocate(p_u2f_context);
    if (keydata_buffer == NULL) {
        return false;
    }
:
    ret = fds_record_find(U2F_FILE_ID, U2F_SKEY_CERT_RECORD_KEY, &record_desc, &ftok);
    if (ret == FDS_SUCCESS) {
        // レコードが存在するときは領域にデータを格納
        if (keydata_record_get(&record_desc, keydata_buffer) == false) {
            return false;
        }

    } else if (ret != FDS_ERR_NOT_FOUND) {
        // レコードが存在しないときはOK
        // それ以外の場合はエラー終了
        NRF_LOG_ERROR("fds_record_find returns 0x%02x \r\n", ret);
        return false;
    }

    return true;
}
static uint32_t *keydata_buffer_allocate(ble_u2f_context_t *p_u2f_context)
{
:
    // Flash ROM読込用領域の確保
    keydata_buffer_length = sizeof(uint32_t) * SKEY_CERT_WORD_NUM;
    keydata_buffer = (uint32_t *)malloc(keydata_buffer_length);
:
    // 確保領域は0xff(Flash ROM未書込状態)で初期化
    memset(keydata_buffer, 0xff, keydata_buffer_length);
    return keydata_buffer;
}

まずは、鍵・証明書を格納すべき領域に、1ワードごとに0xffffffffという値が設定されます。 (証明書のバイト長を設定するcert_buffer_lengthという4バイトの変数領域も、mallocで確保された領域の一部なので、同じように0xffffffffという値が設定されます)

本障害発生時は、鍵・証明書が登録されていない状態ですので、鍵・証明書を格納すべき領域には0xffffffffが設定されたままの状態となります。

他方、ble_u2f_flash_keydata_readでは、鍵・証明書が登録されていない場合でもtrueが戻ってしまうので、そのまま処理はエラーでないものとして継続されます。

このロジックにより、処理が先述のmemcpyに到達してしまい、第3引数のサイズ(cert_buffer_length)が、0xffffffff(=4GBytes)という値で指定されることになります。

memcpyにより不正な領域へのデータコピーが発生し、結果、プログラムがダウンしたものと考えられます。

makmorit commented 6 years ago

対策

修正方針

makmorit commented 6 years ago

問題が解決しましたのでcloseします。