FIDO2セキュリティキーでリモートデスクトップをする試み

#はじめに
FIDOといえばWebなんですけど、どうしてもWeb以外でやりたい、ということで、前回 [FIDO2セキュリティキーで電子署名をする試み](https://qiita.com/gebo/items/0a08bc0e1aea6bc12c96) をやりましたが、今回はリモートデスクトップをする試みです。

Windows作業PCからサーバーPCなどにRDPして作業するなんていうのは、よくあることだと思いますが、パスワードを毎回打ち込むのは面倒だし、作業PCにパスワードを保存してしまうのはセキュリティ的にまずいですよね。
そこで、FIDOキーからRDP接続する方法を考えてみました。


#環境
WebAuthnModokiDesktop と PowerShell でシンプルにやります。

- FIDO2セキュリティキー
	- [Yubikey](https://www.yubico.com/product/security-key-by-yubico/#security-key)とか[BioPass](https://www.ftsafe.com/Products/FIDO2)とかFIDO2であればOK。UAF,U2Fしか対応していないものはNG
- Windows10 1809
- [WebAuthnModokiDesktop](https://github.com/gebogebogebo/WebAuthnModokiDesktop)
- PowerShell
- ソース [GitHub](https://github.com/gebogebogebo/FidoKeyRDP)


####注意事項
- お遊びレベルの品質なので重要なクレデンシャル情報が入っているFIDOキーは使わないことをお勧めします。
- 本ライブラリ・デモプログラムを利用することによって生じるいかなる問題についても、その責任を負いません。


#登録
FIDOキーに接続情報を書き込みます。
- register.bat
- register.ps1
- WebAuthnModokiDesktop.dll
の3つのファイルを同じ場所に置いて register.bat を実行すればOK

####手順

- `キーをさしてください` と出たらFIDOキーをUSBにさします。NFCのFIDOキーも対応しています。
- `RDP接続情報を入力してください` と出たら `ホスト名(IPアドレス)` `接続ユーザー/パスワード` を入力をします。(Enterで確定)
- `登録情報を入力してください` と出たら `RPID（任意の文字列）` `PIN` を入力します。
- 書き込みがはじまります、`Writing...` となってFIDOキーが光ったらタッチしてあげてください。3回ほどタッチが必要です。
- 無事書き込みが終了すると `Success` となります。

```bat:
C:\work\aaa>echo off
キーをさしてください
Manufacturer : Yubico , Product : Security Key by Yubico
PIN Retry Count = 8
RDP接続情報を入力してください
RDP Host: 192.168.xxx.xxx
RDP User: gebo
RDP Password: boge
登録情報を入力してください
Authenticator RPID: test
Authenticator PIN: 1234
登録ブロック数 = 3
Writing...
Writing...
Writing...
Success
```

#RDP接続
FIDOキーからRDP接続します。
- rdp.bat
- rdp.ps1
- WebAuthnModokiDesktop.dll
の3つのファイルを同じ場所に置いて rdp.bat を実行すればOK

####手順
- 先ほど登録した `RPDI` `PIN` を入力するとRDP接続します。
- PIN省略(未入力)も可能です、その場合は生体(BioPassなら指紋)の認証が必要です。
- RPIDを変えれば複数の接続情報を登録できるので、接続先＝RPIDというネーミングで色々登録しておく使い方がいいと思います。

```bat:
キーをさしてください
Manufacturer : Yubico , Product : Security Key by Yubico
PIN Retry Count = 8
登録情報を入力してください
Authenticator RPID: test
Authenticator PIN: 1234
Success
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/54a7ad22-8ea7-46bf-0ba3-9d3964199d11.png" width=50%>

<img src="https://qiita-image-store.s3.amazonaws.com/0/252291/f6b2dd08-580a-134a-83a8-8dad9c406e5a.png" width=70%>

#解説
##register.bat、rdp.bat
powershellを実行しているだけです

```bat:register.bat、rdp.bat
echo off
powershell -NoProfile -ExecutionPolicy Unrestricted .\%~n0.ps1
Timeout 3
```

##register.ps1
WebAuthnModokiDesktop.dlのAPIを使っています。
Polling()でFIDOキーの接続を待って、RegisterCmd()でFIDOキーにコマンドを書き込んでいます。
APIの説明は後ほど。

```ps1:register.ps1
Set-Location (Split-Path ( & { $myInvocation.ScriptName } ) -parent)
Add-Type -Path ".\WebAuthnModokiDesktop.dll";

Write-Host "キーをさしてください"
$poll = [gebo.CTAP2.Util.CmdExecuter]::Polling(5000).GetAwaiter().GetResult()
if( $poll -eq $false ) {
    Write-Host "Timeout"
    return
}

Write-Host "RDP接続情報を入力してください"
$ip=Read-Host "RDP Host"
$user= Read-Host "RDP User"
$pass= Read-Host "RDP Password"

Write-Host "登録情報を入力してください"
$rpid= Read-Host "Authenticator RPID"
$pin= Read-Host "Authenticator PIN"

$cmd = "Cmdkey /generic:TERMSRV/${ip} /user:${user} /pass:${pass} & Start mstsc /v:${ip} & Timeout 2 & Cmdkey /delete:TERMSRV/${ip}";

$blockcount = [gebo.CTAP2.Util.CmdExecuter]::CheckWriteBlockCount($cmd)
Write-Host "登録ブロック数 = ${blockcount}"

$result = [gebo.CTAP2.Util.CmdExecuter]::RegisterCmd($rpid,$pin,$cmd).GetAwaiter().GetResult()
Write-Host "${result}"
```

書き込むコマンドですが、定番のRDP接続コマンドをワンライナーで指定します。

```
Cmdkey /generic:TERMSRV/${ip} /user:${user} /pass:${pass} & Start mstsc /v:${ip} & Timeout 2 & Cmdkey /delete:TERMSRV/${ip}
```


##rdp.ps1
WebAuthnModokiDesktop.dlのAPIをCallしているだけです。APIの説明は後ほど。

```ps1:rdp.ps1
Set-Location (Split-Path ( & { $myInvocation.ScriptName } ) -parent)
Add-Type -Path ".\WebAuthnModokiDesktop.dll";

Write-Host "キーをさしてください"
$poll = [gebo.CTAP2.Util.CmdExecuter]::Polling(5000).GetAwaiter().GetResult()
if( $poll -eq $false ) {
    Write-Host "Timeout"
    return
}
Write-Host "登録情報を入力してください"
$rpid= Read-Host "Authenticator RPID"
$pin= Read-Host "Authenticator PIN"
$result = [gebo.CTAP2.Util.CmdExecuter]::Execute($rpid,$pin).GetAwaiter().GetResult()
return $result
```

##WebAuthnModokiDesktop.dll
FIDOキーを制御するDLLです。
ソースはこちら
https://github.com/gebogebogebo/WebAuthnModokiDesktop

この中から
gebo.CTAP2.Util.CmdExecuterクラスを使っています。

###static async Task\<bool> Polling(int timeoutms=10000)
FIDOキーの接続を待ちます。

- 引数
	- int timeoutms : 接続待ちタイムアウトをミリ秒単位で指定してください。
- 戻り値
	- true(成功)/false(失敗)

###static int CheckWriteBlockCount(string cmd)
FIDOキーに書き込むコマンド文字列の必要ブロック数を調査します。

- 引数
	- string cmd : コマンド文字
- 戻り値
	- 必要ブロック数

###static async Task\<string> RegisterCmd(string rpid, string pin, string cmd)
FIDOキーにコマンドを書き込みます。
USBタイプのFIDOキーの場合、必要ブロック数だけUP(キーをタッチする操作)が必要です。

- 引数
	- string rpid : rpid
	- string pin : pin
	- string cmd : 書き込むコマンド
- 戻り値
	- 実行結果

###static async Task\<string> Execute(string rpid, string pin)
FIDOキーに書き込まれているコマンドを実行します。

- 引数
	- string rpid : rpid
	- string pin : pin
- 戻り値
	- 実行結果

#おつかれさまでした
RDPに限らずワンライナーで実行できるコマンドだったらなんでもいけるかと思います。