WebAuthnぽいことができるWinデスクトップアプリ用ライブラリ　WebAuthnModokiDesktopβ

#はじめに
WindowsデスクトップアプリでもWebAuthnっぽいことができるライブラリを作成したので紹介します。
- 2018/12/18 FEITIAN BioPass FIDO2での動作確認をしました
- 2018/12/24 Verify機能を追加しました
- 2019/01/14 NFC(Yubikey5)に対応しました
- 2019/03/09 ver 2.0.0.0

####注意事項
- 本ライブラリ・デモプログラムはWebAuthn、CTAPを勉強しながら作成した検証プログラムです。
- サンプルレベルの品質なので重要なクレデンシャル情報が入っている認証器は使わないことをお勧めします。
- 本ライブラリ・デモプログラムを利用することによって生じるいかなる問題についても、その責任を負いません。

#１．実行環境
- Windows10 1809
- .Net Framework 4.6.1
- [『2』って書いてある青いYubikey](https://www.amazon.co.jp/dp/B07BYSB7FK/ref=asc_df_B07BYSB7FK2547162/?tag=jpgo-22&creative=9339&creativeASIN=B07BYSB7FK&linkCode=df0&hvadid=275179827080&hvpos=1o1&hvnetw=g&hvrand=17528524305145563707&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009333&hvtargid=pla-650379948392)
- [FEITIAN BioPass FIDO2](https://www.ftsafe.com/onlinestores/product?id=3)
- [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)


※Webとか名前がついていますが、サーバーと通信するなど一切ありません。

#２．とりあえず動かしてみる
- お手元に[『2』って書いてある青いYubikey](https://www.amazon.co.jp/dp/B07BYSB7FK/ref=asc_df_B07BYSB7FK2547162/?tag=jpgo-22&creative=9339&creativeASIN=B07BYSB7FK&linkCode=df0&hvadid=275179827080&hvpos=1o1&hvnetw=g&hvrand=17528524305145563707&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009333&hvtargid=pla-650379948392)をご用意ください
- あるいは[FEITIAN BioPass FIDO2](https://www.ftsafe.com/onlinestores/product?id=3)でもOK
- あるいはYubikey5でもOK
- [ココ](https://github.com/gebogebogebo/WebAuthnModokiDesktop/tree/master/bin)からバイナリ一式をダウンロードします。
- `testUI02.exe` を実行します。
- YubikeyをUSBに刺して「get info」をクリックして「Success」って出たらOKです。
- あるいは パソリにYubikey5を乗っけて「get info」をクリックして「Success」って出たらOKです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/252291/4f2ffdac-5411-2ef9-2e7a-89adc0838d15.png)

##get Info
Authenticatorの情報をGETします。GETして表示するだけです。

##Register
登録。WebAuthnの`credentials.create()`ぽいことをやります。
UserNameを入力してNextして登録します。
YubikeyにPINが設定してある場合は `CTAP2_ERR_PIN_REQUIRED  PIN` とエラーになるので画面右上PIN欄にPINを入力してからNextボタンをクリックしてください。
Enable username-less loginにチェックをつけると`Resident Key`します。
BioPassで指紋認証したい場合は、`User Verification`にチェックONしてください。

※`Regident Key`は認証器にユーザー情報を保存する機能ですが、**無限に保存できるわけではありません**、認証器の保存領域MAXになった時の動作はYubikeyの場合、変なエラーが起きるようになるなど、あんまりうれしくない挙動になるようですのでご注意ください。
※2019/2/11追記 青いYubikeyでは26個目の登録(authenticatorMakeCredential)で `0x28:CTAP2_ERR_KEY_STORE_FULL` のエラーになりました。

##Login
認証。WebAuthnの`credentials.get()`ぽいことをやります。
Credential Idを指定してget()します。
このサンプルは直前にRegisterしたCredential Idをメモリに保持して再利用するだけです。
なので、Registerの後にやってください。
※BioPassで指紋認証したい場合は、`User Verification`にチェックONしてください。

##Login without username
こちらも認証ですが、Credential Idを指定しない方法です。
なのでRegisterしないでも実行可能です。

#３．ライブラリ解説
ソースは以下の場所です。
https://github.com/gebogebogebo/WebAuthnModokiDesktop

##ライブラリ構成
~~ライブラリは64bit専用です。~~
- **WebAuthnModokiDesktop.dll**(本体)
- [BouncyCastle.dll(Bouncy Castle)](http://www.bouncycastle.org/csharp/)
- [Newtonsoft.Json.dll(Newtonsoft.Json)](https://www.newtonsoft.com/json)
- [CBOR(PeterO.Cbor)](https://github.com/peteroupc/CBOR)
- [Numbers(PeterO.Numbers)](https://github.com/peteroupc/Numbers)
- [HidLibrary(hidlibrary)](https://github.com/mikeobrien/HidLibrary)

![Module.png](https://qiita-image-store.s3.amazonaws.com/0/252291/5820b1d6-f5bc-2dc1-3523-aab029f463df.png)

##プロジェクトへの組み込み
- Visual Studioでプロジェクトを作成します。
- [ここ](https://github.com/gebogebogebo/WebAuthnModokiDesktop/tree/master/bin)からとってきたバイナリ一式(binフォルダの中身)をexeと同じフォルダに置いてください。
- Visual Studioの参照から `WebAuthnModokiDesktop.dll` を参照に追加してください。

##メソッド
using gebo.CTAP2.WebAuthnModokiDesktop;
using gebo.CTAP2;

- Credentials.Create()
- Credentials.Get()
- Credentials.Info()
- Credentials.SetPin()
- Credentials.ChangePin()
- Credentials.HidCheck()
- Credentials.NfcCheck()
- Credentials.SerializeAttestationToFile()
- Credentials.DeSerializeAttestationFromFile()

###Credentials.Create()
####引数
- DevParam devParam : デバイスの検索パラメータ※
- string publickeyJson : WebAuthnで指定するOptionをJSON文字列形式で指定します。
- string pin="" : PIN

※devParamについて
Authenticatorデバイスを検索するパラメータです。
とりあえず、`gebo.CTAP2.DevParam.getDefaultParams()`をしておけばYubikeyとFEITIANのパラメータ設定されます。それ以外の製品は手動で追加してください。

####戻り値
- CreateCommandStatusクラス : 中にAttestationが入っています。

```C#:
var devParam = DevParam.GetDefaultParams();
byte[] challenge = System.Text.Encoding.ASCII.GetBytes("this is challenge");
string pin = "xxxx";
string json =
    "{" +
        "rp : {" +
            "id : 'demo.WebauthnMODOKI.gebogebo.com'," +
        "}," +
        "user : {" +
            "id : 'userid'," +
            "name :'name_name'," +
            "displayName :'my name is gebogebo'," +
        "}," +
        "pubKeyCredParams: [{type: 'public-key',alg: -7}]," +
        "timeout: 60000," +
        "authenticatorSelection : {" +
            "requireResidentKey : false," +
        "}," +
        string.Format($"challenge:[{string.Join(",", challenge)}],") +
     "}";
var response = await Credentials.Create(devParam,json, pin);
if (response.isSuccess == true) {
    // 成功
    // response.attestation ...
}
```

##Credentials.Get()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string publickeyJson : WebAuthnで指定するOptionをJSON文字列形式で指定します。
- string pin="" : PIN

####戻り値
- getcommandstatusクラス : 中にAssertionが入っています。
	- Assertionは複数取れる場合があるのでリストです。

```C#:
var devParam = DevParam.GetDefaultParams();
byte[] challenge = System.Text.Encoding.ASCII.GetBytes("this is challenge");
string pin = "xxxx";
byte[] CredentialId= // createでGETしたCredentialIdを指定してください(response.attestation.CredentialId);

string json =
   "{" +
        "timeout : 60000," +
        string.Format($"challenge:[{string.Join(",", challenge)}],") +
        "rpId : 'demo.WebauthnMODOKI.gebogebo.com'," +
        "allowCredentials : [{" +
           string.Format($"id : [{string.Join(",", CredentialId)}],") +
           "type : 'public-key'," +
       "}]," +
       "requireUserPresence : 'true'," +
       "userVerification : 'discouraged'," +
    "}";

var response = await Credentials.Get(devParam ,json, pin);
if (response.isSuccess == true) {
    // 成功
    // response.assertions[0] ...
}
```

##Credentials.SetPin()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string newpin : 設定するPIN

####戻り値
- commandstatusクラス : 処理結果です

PINを初めてセットするときに使うコマンドです。
PIN変更ではありません。

##Credentials.ChangePin()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string newpin : 設定するPIN
- string currentpin : 現在のPIN

####戻り値
- commandstatusクラス : 処理結果です

##Credentials.Info()
####引数
- List<hidparam> hidParams : HIDデバイスの検索パラメータ

####戻り値
- infocommandstatusクラス : Authenticatorの情報

##Credentials.HidCheck()
####引数
- List<HidParam> hidParams

USBにささっているAuthenticatorの情報を取ってきます。

##Credentials.NfcCheck()
####引数
- List<NfcParam> nfcParams

NFCリーダーにのっかっているAuthenticatorの情報を取ってきます。

##Credentials.SerializeAttestationToFile()
####引数
- CTAPResponseAttestation att : attestationクラス
- string pathname: シリアライズするファイルのパスとファイル名

####戻り値
- bool : true(成功)/false(失敗)
 
attestationをファイルに保存します。

##Credentials.DeSerializeAttestationFromFile()
####引数
- string pathname: デシリアライズするファイルのパスとファイル名

####戻り値
- CTAPResponseAttestation : Attestationクラス

attestationをファイルからクラスにデシリアライズします。

#おつかれさまでした
- 対応していない・未検証の部分
	- ~~応答値を検証する機能はありません。~~
	- ~~PINを変更する機能はありません。~~
	- ~~timeoutパラメータには対応していません。~~
	- ~~32bit環境では動作確認していません、多分動かないと思います。~~
	- ~~2って書いてある青いYubikey以外では動作確認していません。~~ 
- 夜 酒飲みながら書いているコードなので、怪しいところが多々あり。
- 参考にさせていただきました。
	- https://github.com/Yubico/libfido2
	- https://github.com/samcook/u2fhost.net