CTAP2 お勉強メモ#7 - NFC

#はじめに
これはCTAPのお勉強をしたメモです。
**WebAuthn(ウェブオースン)ではなく、CTAP(シータップ)であります。**

今回はCTAP2の**NFC(Near Field Communication)**です。

###教科書

- [CTAP仕様書 Client to Authenticator Protocol (CTAP) Implementation Draft, February 27, 2018](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc)
- [WebAuthn仕様書 Web Authentication: An API for accessing Public Key Credentials Level 1 W3C Candidate Recommendation, 7 August 2018](https://www.w3.org/TR/webauthn/)

###教材

- [FIDO2デバイス Yubico Yubikey5](https://www.yubico.com/product/yubikey-5-nfc/)
- [NFCリーダー Sony PaSoRi RC-S380](https://www.sony.co.jp/Products/felica/consumer/products/RC-S380.html)

###復習
- [CTAP2 お勉強メモ#1](https://qiita.com/gebo/items/d2ffbd4fcf7d75e21b63)
- [CTAP2 お勉強メモ#2](https://qiita.com/gebo/items/e0bd197d607312dcf4fb)
- [CTAP2 お勉強メモ#3](https://qiita.com/gebo/items/2cfc3202cd88a59b24ba)
- [CTAP2 お勉強メモ#4](https://qiita.com/gebo/items/634aa39b0e08d8258682)
- [CTAP2 お勉強メモ#5](https://qiita.com/gebo/items/84454583daeaf6711fd0)
- [CTAP2 お勉強メモ#6](https://qiita.com/gebo/items/cfc6ceb1c7f9aa5fdad6)


#目次

1.[Yubikeyについて](https://qiita.com/gebo/items/2c9d020c0768b95a01b0#1yubikey%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
2.[CTAP NFC](https://qiita.com/gebo/items/2c9d020c0768b95a01b0#2-ctap-nfc)
2.1 [WindowsでNFCで通信するお作法](https://qiita.com/gebo/items/2c9d020c0768b95a01b0#21-windows%E3%81%A7nfc%E3%81%A7%E9%80%9A%E4%BF%A1%E3%81%99%E3%82%8B%E3%81%8A%E4%BD%9C%E6%B3%95)
2.2 [APDU Protocol](https://qiita.com/gebo/items/2c9d020c0768b95a01b0#22-apdu-protocol)
3.[おまけ](https://qiita.com/gebo/items/2c9d020c0768b95a01b0#3%E3%81%8A%E3%81%BE%E3%81%91)


#1.Yubikeyについて
**Yubikey5**です。
HIDだけでなくNFCでFIDO2(CTAP2)を話すことができます。
さらにFIDOだけでなく OTP,OpenPGP,PIV,OATH などいろんなことができるらしいです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/a09d59b8-8619-be31-e473-fd9bcb795b91.png" width=50%>

本デバイスを検証するにあたっていきなりハマったのですが、どうやらOTPの機能が有効になっているとFIDOの機能が使えないようで、CTAPのコマンドが通りません。
今回はCTAPの検証がしたいので、他の機能は無効にしました。

- (1) [Yubikey ManagerをDL](https://www.yubico.com/products/services-software/download/yubikey-manager/)してインストール
- (2) USBにYubikey5をさしてYubikey Managerを起動
- (3) InterfaceメニューからFIDO2関係だけチェックONに変更して「Save」

<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/06d51472-b6ad-77c6-50c1-615da39d22b0.png" width=80%>


#2. CTAP NFC
NFCでYubikeyとお話する方法はCTAP仕様では
[8.2. ISO7816, ISO14443 and Near Field Communication (NFC)](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc)
だけです。
つまり、基本的なことを知っていれば簡単です。

**基本的なこととは何か？**
- CTAP2コマンド
- NFC通信の基本

### CTAP2コマンド
これはこれまでのお勉強メモで学習済みです。過去の投稿を見て思い出します。

### NFC通信の基本
ISO7816, ISO14443と書いてあります。
これは以前検証したマイナンバーカードと通信する方法と同じで、`winscard―API`を使って`APDU`で通信する方法を意味します。
- [ICカードとお話する方法](https://qiita.com/gebo/items/6a334b5453817a587683#%EF%BC%92%EF%BC%91ic%E3%82%AB%E3%83%BC%E3%83%89%E3%81%A8%E3%81%8A%E8%A9%B1%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
- [APDUコマンド](https://qiita.com/gebo/items/6a334b5453817a587683#%EF%BC%92%EF%BC%94apdu%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

##2.1 WindowsでNFCで通信するお作法
エラー処理をちゃんとやるともう少し複雑になりますが、おおざっぱにはこんな感じです。
SCard系のAPIは決められた手順でCallするおまじないみたいなものです。
重要なのはScardTransmitで送信する`APDU`です。
APDUというバイナリのパケットにCTAPコマンドを詰めて送ります。この辺のめんどくささは[CTAPHID](https://qiita.com/gebo/items/e0bd197d607312dcf4fb#2-hid%E3%82%92open%E3%81%97%E3%81%A6yubikey%E3%81%A8%E6%8E%A5%E7%B6%9A)と同じです。ただCTAPHIDよりはずっと簡単です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/f951055a-36fd-33a8-ce9f-c8bd065b5594.png" width=６0%>

##2.2 APDU Protocol
[CTAP仕様書 8.2.2. Protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc-protocol)

The general protocol between a FIDO2 client and an authenticator over ISO7816/ISO14443 is as follows: 
- Client sends an applet selection command
- Authenticator replies with success if the applet is present
- Client sends a command for an operation
- Authenticator replies with response data or error


*クライアントとAuthenticatorのISO7816/ISO14443手順は以下の通り*
- *Clientはappletを選択するコマンドを送信する*
- *Authenticatorからの応答を受ける(appletへの接続)*
- *Clientは(CTAP)コマンドを送信する*
- *Authenticatorからの応答を受ける(CTAPコマンドの応答)*

###appletを選択するコマンド
ISO7816/ISO14443というのはAuthenticatorがいわゆるICカードのふるまいをするということです。
ICカードは中にアプリ(applet)を複数格納できる仕様になっています。
Clientはまず、FIDOアプリに接続(SELECT)します。

[CTAP仕様書 8.2.3. Applet selection](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc-applet-selection)

- APDU
![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/f589f818-fed1-9b6e-1ec7-c7d95089a8a2.png)
- AID
<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/bce238db-0a6c-9c82-0014-9fa0a53938bc.png" width=50%>

####例 SELECT FILE FIDO applet

```:
// APDU
00 A4 04 00 08 A0 00 00 06 47 2f 00 01 00

// Response
55 32 46 5F 56 32 90 00
```

####APDU解説
APDU = `[0x00, 0xA4, 0x04, 0x00], [0x08], [0xA0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01],[0x00]`

固定値のところは仕様書に書いてある値をそのまま使えばいいです。

| byte no|項目名|値|説明|
|:--|:--|:--|:--|
|0|CLA|00|命令クラス　固定値|
|1|INS|A4|コマンド名　Select File　固定値|
|2|P1|04|パラメータ1　固定値|
|3|P2|00|パラメータ2　固定値|
|4|Lc|08|データサイズ　固定値|
|5-12|Data|A0...01|データ　Selectするアプリ識別子(AID)　固定値|
|13|Le|00|レスポンスバッファサイズ　00でOK|

####Response解説
Response = `[0x55, 0x32, 0x46, 0x5F, 0x56, 0x32],0x90, 0x00` ← 正常終了という意味

| byte no|項目名|値|説明|
|:--|:--|:--|:--|
|0-5|Data|55...32|version information ASCII変換で"U2F_V2"|
|6|SW1|90|ステータスワード1 0x90で正常終了という意味|
|7|SW2|00|ステータスワード2|

厳密にはversion informationの中身を見て自分とこの実装に対応しているかどうかチェックするみたいです。
ステータスワードはAPDUの仕様で以下のような意味です。
※[Eternal Windows](http://eternalwindows.jp/security/scard/scard07.html)より抜粋させていただきました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/5a0acac9-04bc-50d3-a881-eacdf3e31cfb.png"  width=50%>

###CTAPコマンドをAPDUで送信する
FIDO appletをSELECTしてResponseが正常にGETできたら、続けざまにAPDUでCTAPコマンドを送信します。

[CTAP仕様書 8.2.4. Framing](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc-framing)

- APDU
![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/44a97a9b-8af3-ff6f-55f6-e80d983b9728.png)

- Response

![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/dac07146-e88d-5778-e3ff-8134c429a75d.png)

####APDU解説
APDU解説はもういいやってくらいのシンプルさなので `Data In` のところだけ解説。
CTAPコマンドの長さ(バイト数)を1バイトで指定した後、CTAPコマンド(例によってCBORエンコードしたもの)を設定し、最後の`Le`は`0x00`を指定します。

####Response解説
そうするとレスポンスが返ってきます。
最初の1バイトがCTAPのステータスコード([6.3. Status codes](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#error-responses))でその後ろがデータ。最後の2バイトがAPDUのSW1、SW2で`0x9000` だったらOKということです。

####例 authenticatorGetInfo (0x04)コマンド
例えば CTAPのauthenticatorGetInfo コマンドの場合

```:CTAPコマンドだけを取り出すとこんな感じ
//CTAP Command authenticatorGetInfo
04

// Response
00
A6 01 82 66 55 32 46 5F 56 32 68 46 49 44 4F 5F
32 5F 30 02 81 6B 68 6D 61 63 2D 73 65 63 72 65
74 03 50 FA 2B 99 DC 9E 39 42 57 8F 92 4A 30 D2
3C 41 18 04 A4 62 72 6B F5 62 75 70 F5 64 70 6C
61 74 F4 69 63 6C 69 65 6E 74 50 69 6E F5 05 19
04 B0 06 81 01
```

```:CBORデコードして見やすくするとこんな感じ
//CTAP Command authenticatorGetInfo
0x04

// Response
<Status>
0x00(CTAP1_ERR_SUCCESS Indicates successful response.)

<ResponseData-Decoded COBOR>
{
    "1":["U2F_V2","FIDO_2_0"],
    "2":["hmac-secret"],
    "3":"-iuZ3J45QlePkkow0jxBGA",
    "4":{"rk":true,"up":true,"plat":false,"clientPin":true},
    "5":1200,
    "6":[1]
}
```

これをAPDUにするとこうなります

```:APDU
// SendAPDU
80 10 00 00 01 04 00

// Response
00
A6 01 82 66 55 32 46 5F 56 32 68 46 49 44 4F 5F
32 5F 30 02 81 6B 68 6D 61 63 2D 73 65 63 72 65
74 03 50 FA 2B 99 DC 9E 39 42 57 8F 92 4A 30 D2
3C 41 18 04 A4 62 72 6B F5 62 75 70 F5 64 70 6C
61 74 F4 69 63 6C 69 65 6E 74 50 69 6E F5 05 19
04 B0 06 81 01
90 00
```

###CTAPコマンドをAPDUで送信する２
以下のケースに対応する仕様があります。
- 分割送信：送信するCommandが長い時の対応
- 分割受信：受信するResponseが長いときの対応

####分割送信
送信するAPDUの`Data In`では最初の1byte目でデータのレングスを指定します。
1byteということは0x00～0xFFの範囲です。
つまり、255byteを超える場合は1個のコマンドでは送信しきれません。
そんな時は分割して何回かに分けて送ります。

[8.2.5. Fragmentation](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc-fragmentation)

- **データにまだ続きがある**よってときは0x90から始まるこっちのAPDUで送ります。
※`Data In` の書き方がわかりにくいですが、さっき説明したData Inの形式と同じで最初の1byte目がレングスです。つまり `CTAP Command Byte || COBR Encoded Data`。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/ef83cb26-bd1b-416a-1e9c-3db64de79203.png)
このコマンドに対する応答は`9000`の2byteだけ返ってきます。

- **これが最後**ってときは先ほど説明した0x80から始まるAPDUで送ります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/44a97a9b-8af3-ff6f-55f6-e80d983b9728.png)

####分割受信
レスポンスにはレングスフィールドがないんですけど、同じように分割して送られて来る時があります。
そんな時はSW1が `0x61` になっています。[ISO 7816-4](http://cardwerk.com/smart-card-standard-iso7816-4-section-5-basic-organizations/#chap5_3)の仕様のようです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/5346b355-d831-50ac-4472-75b41644e4bd.png)

この辺のサンプルが
[8.2.5. Fragmentation](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#nfc-fragmentation)
の**EXAMPLE 9**に書いてあります。

#3.おまけ
[WebAuthnぽいことができるWinデスクトップアプリ用ライブラリ　WebAuthnModokiDesktopβ](https://qiita.com/gebo/items/f6d3024f7e164ac0a195)のNFC対応を行いました。
ここで勉強した内容を反映しています。
*もう、ソースがぐちゃぐちゃ...*

#おつかれさまでした
NFCは意外と簡単でした。