Closed makmorit closed 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);
鍵・証明書を、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により不正な領域へのデータコピーが発生し、結果、プログラムがダウンしたものと考えられます。
鍵・証明書を格納すべき領域に、正しく鍵・証明書がセットされているか、もしくは初期状態かどうかを調べる関数(仮称:ble_u2f_flash_keydata_available)を新設
前述ble_u2f_register_do_process関数において、ble_u2f_flash_keydata_read関数を実行したのちに、ble_u2f_flash_keydata_available関数を実行し、availableでなければ、エラーと判定
ブランチ https://github.com/diverta/onecard-fido/tree/impl-ble_u2f_flash_keydata_available
問題が解決しましたのでcloseします。
確認された事象
鍵・証明書がU2Fトークン(One Card上のFIDO BLE U2Fサービス)に未導入の場合、U2F管理ツール(U2FMaintenanceTool.app)によりヘルスチェックを実行すると、リクエストがタイムアウトしてしまいます。 (11/30 16:40頃発覚)
ログ
U2F管理ツール(U2FMaintenanceTool.app)のログ
nRF52のUARTログ
「response_message_buffer allocated (1024 bytes) 」のログ出力を最後に、ソフトデバイスがダウンしてしまった様子。