Windows Hello コンパニオン (IoT) デバイスを使った Windows のロック解除

Windows Hello コンパニオン デバイスは、Windows 10 デスクトップと連携して動作し、ユーザー認証エクスペリエンスを強化できるデバイスです。 Windows Hello コンパニオン デバイス フレームワークを使用すると、コンパニオン デバイスでは、生体認証を利用できない場合 (たとえば、Windows 10 のデスクトップが顔認証用のカメラや指紋リーダー デバイスを備えていない場合など) でも、Windows Hello のための優れたエクスペリエンスを利用することができます。

Note

Windows Hello コンパニオン デバイス フレームワーク用の API は、Windows 10 バージョン 2004 では非推奨とされています。

はじめに

コード サンプルについては、「Windows Hello コンパニオン デバイス フレームワークの Github リポジトリ」を参照してください。

ユース ケース

Windows Hello コンパニオン デバイス フレームワークを使用して、コンパニオン デバイスで優れた Windows ロック解除エクスペリエンスを築き上げるさまざまな方法があります。 たとえば、ユーザーは次のことができます。

  • コンパニオン デバイスを USB 経由で PC にアタッチし、コンパニオン デバイスのボタンをタッチして、PC のロックを自動的に解除します。
  • Bluetooth 経由で PC と既にペアリングされているスマートフォンをポケットに入れて携帯します。 PC の Space キーを押すと、スマートフォンに通知が届きます。 それを承認するだけで、PC のロックが解除されます。
  • コンパニオン デバイスを NFC リーダーにタップして、PC のロックをすばやく解除します。
  • 既に着用者が認証されているフィットネス バンドを着用します。 PC に近づき、特別なジェスチャ (拍手など) を実行すると、PC のロックは解除されます。

生体認証に対応している Windows Hello コンパニオン デバイス

コンパニオン デバイスが生体認証をサポートしている場合、Windows Hello コンパニオン デバイス フレームワークよりも Windows 生体認証フレームワークのほうが適したソリューションになる場合があります。

ソリューションのコンポーネント

次の図は、ソリューションのコンポーネントとそのビルドを担当するユーザーを示しています。

フレームワークの概要

Windows Hello コンパニオン デバイス フレームワークは、Windows で実行されているサービス (この記事では "コンパニオン認証サービス" と呼ばれます) として実装されています。 このサービスでは、Windows Hello コンパニオン デバイスに格納されている HMAC キーで保護する必要のあるロック解除トークンを生成します。 これにより、ロック解除トークンへのアクセスには Windows Hello コンパニオン デバイスのプレゼンスが必要になります。 (PC、Windows ユーザー) タプルごとに、一意のロック解除トークンが存在します。

Windows Hello コンパニオン デバイス フレームワークとの統合には、次のものが必要です。

  • Windows アプリ ストアからダウンロードした、コンパニオン デバイス用のユニバーサル Windows プラットフォーム (UWP) Windows Hello コンパニオン デバイス アプリ。
  • Windows Hello コンパニオン デバイスで 2 つの 256 ビット HMAC キーを作成し、それを使用して HMAC を生成する機能 (SHA-256 を使用)。
  • 正しく構成されている Windows 10 デスクトップのセキュリティ設定。 コンパニオン認証サービスでは、Windows Hello コンパニオン デバイスを接続する前に、この PIN を設定する必要があります。 ユーザーは、[設定]、[アカウント]、[サインイン オプション] の順に移動して、PIN を設定する必要があります。

上記の要件に加えて、Windows Hello コンパニオン デバイス アプリは以下を担います。

  • Windows Hello コンパニオン デバイスの初期登録とその後の登録解除に関わるユーザー エクスペリエンスとブランド化。
  • バックグラウンドでの実行、Windows Hello コンパニオン デバイスの検出、Windows Hello コンパニオン デバイスとの通信、およびコンパニオン認証サービス。
  • エラー処理

通常、コンパニオン デバイスには、初めてフィットネス バンドを設定するなどの初期セットアップ用のアプリが付属しています。 このドキュメントで説明する機能はそのアプリに含めることができ、別のアプリは必要ありません。

ユーザー シグナル

各 Windows Hello コンパニオン デバイスは、3 つのユーザー シグナルをサポートするアプリと組み合わせる必要があります。 これらのシグナルは、アクションまたはジェスチャの形式で指定できます。

  • 意図シグナル: ユーザーは、Windows Hello コンパニオン デバイスでボタンを押すなどして、ロック解除の意図を示すことができます。 意図シグナルは、Windows Hello コンパニオン デバイス側で収集する必要があります。
  • ユーザー プレゼンス シグナル: ユーザーのプレゼンスを証明します。 たとえば、Windows Hello コンパニオン デバイスでは、PC のロック解除に使用する前に PIN が必要になる場合や、ボタンを押すことが必要になる場合があります。
  • あいまいさ解消シグナル: Windows Hello コンパニオン デバイスで複数のオプションを使用できる場合に、ユーザーがロック解除を求める Windows 10 デスクトップを明確にします。

これらのユーザー シグナルは、いくつでも 1 つにまとめることができます。 ユーザー プレゼンス シグナルと意図シグナルは、使用のたびに要求される必要があります。

PC と Windows Hello コンパニオン デバイス間の登録と今後の通信

Windows Hello コンパニオン デバイスは、Windows Hello コンパニオン デバイス フレームワークへの接続前に、フレームワークに登録する必要があります。 登録のエクスペリエンスは、Windows Hello コンパニオン デバイス アプリによって完全に所有されています。

Windows Hello コンパニオン デバイスと Windows 10 デスクトップ デバイスは、1 対多の関係にできます (つまり、1 台のコンパニオン デバイスを、多数の Windows 10 デスクトップ デバイスで使用できます)。 ただし、各 Windows Hello コンパニオン デバイスは、Windows 10 デスクトップ デバイスごとに 1 人のユーザーのみが使用できます。

Windows Hello コンパニオン デバイスは PC と通信する前に、使用するトランスポートに同意する必要があります。 このような選択は、Windows Hello コンパニオン デバイス アプリに任されています。Windows Hello コンパニオン デバイス フレームワークでは、Windows Hello コンパニオン デバイスと、Windows 10 デスクトップ デバイス側の Windows Hello コンパニオン デバイス アプリとの間で使用されるトランスポートの種類 (USB、NFC、WiFi、BT、BLE など) やプロトコルに制限は設けられていません。 ただし、このドキュメントの「セキュリティ要件」セクションで説明されているように、トランスポート層のセキュリティに関する特定の考慮事項は提案されます。 これらの要件を提供するのは、デバイス プロバイダーの責任です。 フレームワークでは提供されません。

ユーザー操作モデル

Windows Hello コンパニオン デバイス アプリの検出、インストール、初回登録

一般的なユーザー ワークフローは次のとおりです。

  • ユーザーは、その Windows Hello コンパニオン デバイスでのロック解除が必要な Windows 10 デスクトップ デバイスごとに PIN を設定します。
  • ユーザーは、Windows 10 デスクトップ デバイスで Windows Hello コンパニオン デバイス アプリを実行して、自身の Windows Hello コンパニオン デバイスを Windows 10 デスクトップに登録します。

注:

  • Windows Hello コンパニオン デバイス アプリの検出、ダウンロード、および起動は効率化し、可能な場合は自動化することをお勧めします (たとえば、Windows Hello コンパニオン デバイスを Windows 10 デスクトップ デバイス側の NFC リーダーでタップしたときに、アプリを自動的にダウンロードできるようにします)。 ただし、これは Windows Hello コンパニオン デバイスと Windows Hello コンパニオン デバイス アプリの責任です。
  • エンタープライズ環境では、Windows Hello コンパニオン デバイス アプリを MDM 経由で展開できます。
  • Windows Hello コンパニオン デバイス アプリは、登録の一環として発生したエラー メッセージをユーザーに表示する役割を担います。

登録と登録解除のプロトコル

次の図は、登録時に Windows Hello コンパニオン デバイスがコンパニオン認証サービスとどのように対話するかを示しています。

登録フローの図。

このプロトコルでは、次の 2 つのキーが使用されます。

  • デバイス キー (devicekey): PC で Windows のロックを解除するために必要なロック解除トークンを保護するために使用されます。
  • 認証キー (authkey): Windows Hello コンパニオン デバイスとコンパニオン認証サービスを相互に認証するために使用されます。

デバイス キーと認証キーは、登録時に Windows Hello コンパニオン デバイス アプリと Windows Hello コンパニオン デバイスの間で交換されます。 その結果、Windows Hello コンパニオン デバイス アプリと Windows Hello コンパニオン デバイスは、セキュリティで保護されたトランスポートを使用してキーを保護する必要があります。

また、上の図では Windows Hello コンパニオン デバイスで 2 つの HMAC キーを生成する方法が示されていますが、アプリで HMAC キーを生成し、Windows Hello コンパニオン デバイスに送信して格納することも可能です。

認証フローの開始

ユーザーが Windows Hello コンパニオン デバイス フレームワークを使用して Windows 10 デスクトップへのサインイン フローを開始するには、次の 2 つの方法があります (つまり、意図シグナルを提供します)。

  • ノート PC の蓋を開くか、スペース バーを押すか、PC で上にスワイプします。
  • Windows Hello コンパニオン デバイス側でジェスチャまたはアクションを実行します。

どちらを開始点に選択するかは、Windows Hello コンパニオン デバイスが決めます。 Windows Hello コンパニオン デバイス フレームワークでは、オプション 1 が発生するとコンパニオン デバイス アプリに通知されます。 オプション 2 の場合、Windows Hello コンパニオン デバイス アプリでコンパニオン デバイスへのクエリを実行し、そのイベントがキャプチャされたかどうかを確認する必要があります。 これにより、Windows Hello コンパニオン デバイスは、ロック解除が成功する前に意図シグナルを収集します。

Windows Hello コンパニオン デバイスの資格情報プロバイダー

Windows 10 には、すべての Windows Hello コンパニオン デバイスを処理する新しい資格情報プロバイダーがあります。

Windows Hello コンパニオン デバイスの資格情報プロバイダーは、トリガーをアクティブ化してコンパニオン デバイスのバックグラウンド タスクを起動する役割を担います。 トリガーが初めて設定されるのは、PC が起動し、ロック画面が表示されたときです。 2 回目は、PC がログオン UI に入り、Windows Hello コンパニオン デバイスの資格情報プロバイダーが選択されたタイルである場合です。

Windows Hello コンパニオン デバイス アプリのヘルパー ライブラリは、ロック画面の状態の変更をリッスンし、Windows Hello コンパニオン デバイスのバックグラウンド タスクに対応するイベントを送信します。

Windows Hello コンパニオン デバイスのバックグラウンド タスクが複数ある場合は、認証プロセスが終了した最初のバックグラウンド タスクによって PC のロックが解除されます。 コンパニオン デバイス認証サービスでは、残りの認証呼び出しはすべて無視されます。

Windows Hello コンパニオン デバイス側のエクスペリエンスは、Windows Hello コンパニオン デバイス アプリによって所有され管理されます。 Windows Hello コンパニオン デバイス フレームワークでは、ユーザー エクスペリエンスのこの部分を制御できません。 具体的には、コンパニオン認証プロバイダーは、ログオン UI の状態の変化 (ロック画面が今表示されたことや、ユーザーが Space キーを押してロック画面を消したばかりであることなど) を Windows Hello コンパニオン デバイス アプリに (そのバックグラウンド アプリ経由で) 通知します。その周辺のエクスペリエンス (ユーザーが Space キーを押したときのロック画面の消去や、USB 上のデバイス検索の開始など) の構築は、Windows Hello コンパニオン デバイス アプリが担当します。

Windows Hello コンパニオン デバイス フレームワークには、Windows Hello コンパニオン デバイス アプリが選択できる (ローカライズされた) テキスト メッセージとエラー メッセージのストックが用意されています。 これらはロック画面の上部 (またはログオン UI) に表示されます。 詳細については、「メッセージとエラーの処理」セクションを参照してください。

認証プロトコル

Windows Hello コンパニオン デバイス アプリに関連付けられているバックグラウンド タスクは起動すると、コンパニオン認証サービスによって計算された HMAC 値を検証して 2 つの HMAC 値の計算を助けるよう Windows Hello コンパニオン デバイスに要求する役割を担います。

  • サービス HMAC = HMAC(authentication key, service nonce || device nonce || session nonce) を検証します。
  • nonce を使用してデバイス キーの HMAC を計算します。
  • コンパニオン認証サービスによって生成された nonce と連結されている最初の HMAC 値を使用して、認証キーの HMAC を計算します。

2 番目の計算値は、サービスでデバイスを認証し、トランスポート チャネルでのリプレイ攻撃も防ぐために使用されます。

更新された登録フローの図。

ライフサイクル管理

一度登録すれば、どこでも使用可能

バックエンド サーバーがない場合、ユーザーは Windows Hello コンパニオン デバイスを Windows 10 デスクトップ デバイスごとに個別に登録する必要があります。

コンパニオン デバイスのベンダーまたは OEM は、ユーザーの Windows 10 デスクトップまたはモバイル デバイス間で登録状態をローミングする Web サービスを実装できます。 詳細については、「ローミング、失効、およびフィルター サービス」セクションを参照してください。

PIN の管理

コンパニオン デバイスを使用する前に、Windows 10 デスクトップ デバイスで PIN を設定する必要があります。 これにより、ユーザーは Windows Hello コンパニオン デバイスが動作しない場合に備えて、バックアップを作成できるようになります。 PIN は Windows が管理するもので、アプリでは絶対に確認できません。 これを変更するには、ユーザーは、[設定]、[アカウント]、[サインイン オプション] の順に移動します。

管理とポリシー

ユーザーは、そのデスクトップ デバイスで Windows Hello コンパニオン デバイス アプリを実行すると、Windows 10 デスクトップから Windows Hello コンパニオン デバイスを削除できます。

企業には、Windows Hello コンパニオン デバイス フレームワークを制御するための 2 つのオプションがあります。

  • 機能のオン/オフを切り替える
  • Windows AppLocker を使用して、許可される Windows Hello コンパニオン デバイスの許可リストを定義する

Windows Hello コンパニオン デバイス フレームワークでは、使用可能なコンパニオン デバイスのインベントリを保持するための一元的な方法や、許可する Windows Hello コンパニオン デバイスの種類のインスタンス (例: X と Y の間のシリアル番号を持つコンパニオン デバイスのみを許可) をさらにフィルター処理する方法は、サポートされていません。 ただし、アプリ開発者は、このような機能を提供するサービスを構築できます。 詳細については、「ローミング、失効、およびフィルター サービス」セクションを参照してください。

無効化

Windows Hello コンパニオン デバイス フレームワークでは、特定の Windows 10 デスクトップ デバイスからリモートでコンパニオン デバイスを削除することはサポートされていません。 代わりに、ユーザーは、その Windows 10 デスクトップで実行されている Windows Hello コンパニオン デバイス アプリを使用して、Windows Hello コンパニオン デバイスを削除できます。

ただし、コンパニオン デバイス ベンダーは、リモート失効機能を提供するサービスを構築できます。 詳細については、「ローミング、失効、およびフィルター サービス」セクションを参照してください。

ローミングとフィルター サービス

コンパニオン デバイス ベンダーは、次のシナリオで使用できる Web サービスを実装できます。

  • 企業向けのフィルター サービス: 企業は、環境内で動作可能な Windows Hello コンパニオン デバイスのセットを、特定のベンダーの一部デバイスに制限できます。 たとえば、Contoso はベンダー X から 10,000 台のモデル Y コンパニオン デバイスを注文し、それらのデバイスのみが Contoso ドメインで動作するように (し、ベンダー X の他のデバイス モデルは動作しないように) することができます。
  • インベントリ: 企業は、エンタープライズ環境で使用される既存のコンパニオン デバイスの一覧を確認できます。
  • リアルタイム失効: 従業員からコンパニオン デバイスの紛失または盗難が報告された場合、Web サービスを使用してそのデバイスを失効させることができます。
  • ローミング: ユーザーは自身のコンパニオン デバイスを一度登録するだけで、そのデバイスを Windows 10 デスクトップと Windows Mobile で使用することができます。

これらの機能を実装するには、登録時と使用時に Windows Hello コンパニオン デバイス アプリで Web サービスと照合する必要があります。 Windows Hello コンパニオン デバイス アプリでは、1 日に 1 回のみ Web サービスとの照合を要求する (代償として、失効時間が最大 1 日まで延長される) など、キャッシュされたログオン シナリオに合わせて最適化できます。

Windows Helloコンパニオン デバイス フレームワークの API モデル

概要

Windows Hello コンパニオン デバイス アプリには、デバイスの登録と登録解除を担う UI を備えたフォアグラウンド アプリと、認証を処理するバックグラウンド タスクという 2 つのコンポーネントが含まれている必要があります。

全体的な API フローは次のとおりです。

  1. Windows Hello コンパニオン デバイスを登録する
    • デバイスが近くにあることを確認し、その機能に対してクエリを実行する (必要な場合)
    • (コンパニオン デバイス側かアプリ側のどちらかで) 2 つの HMAC キーを生成する
    • RequestStartRegisteringDeviceAsync を呼び出す
    • FinishRegisteringDeviceAsync を呼び出す
    • Windows Hello コンパニオン デバイス アプリで HMAC キーを格納し (サポートされている場合)、Windows Hello コンパニオン デバイス アプリでそのコピーを破棄する
  2. バックグラウンド タスクを登録する
  3. バックグラウンド タスクで適切なイベントを待機する
    • WaitingForUserConfirmation: 認証フローを開始するために Windows Hello コンパニオン デバイス側のユーザー アクション/ジェスチャが必要な場合は、このイベントを待機する
    • CollectingCredential: Windows Hello コンパニオン デバイスが、PC 側でのユーザーの操作/ジェスチャー (Space キーを押すことなど) に依存して認証フローを開始する場合は、このイベントを待つ
    • その他のトリガー (スマートカードなど): 適切な API を呼び出すために、現在の認証状態についてクエリを必ず実行する。
  4. ShowNotificationMessageAsync を呼び出して、エラー メッセージや必要な次の手順についてユーザーに逐次通知する。 意図シグナルが収集された後にのみ、この API を呼び出す
  5. ロックを解除する
    • 意図シグナルとユーザー プレゼンス シグナルが収集されたことを確認する
    • StartAuthenticationAsync を呼び出す
    • 必要な HMAC 操作を実行するためにコンパニオン デバイスと通信する
    • FinishAuthenticationAsync を呼び出す
  6. ユーザーが要求したときに Windows Hello コンパニオン デバイスの登録を解除する (コンパニオン デバイスを紛失した場合など)
    • FindAllRegisteredDeviceInfoAsync を使用してログインしているユーザーの Windows Hello コンパニオン デバイスを列挙する
    • UnregisterDeviceAsync を使用して登録を解除する

登録と登録解除

登録には、コンパニオン認証サービスに対する 2 つの API 呼び出し (RequestStartRegisteringDeviceAsync と FinishRegisteringDeviceAsync) が必要です。

これらの呼び出しを行う前に、Windows Hello コンパニオン デバイス アプリで Windows Hello コンパニオン デバイスが使用可能であることを確認する必要があります。 Windows Hello コンパニオン デバイスが HMAC キー (認証キーとデバイス キー) の生成を担っている場合は、Windows Hello コンパニオン デバイス アプリは、上記の 2 つの呼び出しのいずれかを行う前に、コンパニオン デバイスに生成を要求する必要もあります。 Windows Hello コンパニオン デバイス アプリが HMAC キーの生成を担っている場合は、上記の 2 つの呼び出しを呼び出す前に、それを行う必要があります。

さらに、最初の API 呼び出し (RequestStartRegisteringDeviceAsync) の一環として、Windows Hello コンパニオン デバイス アプリはデバイスの機能を決定し、API 呼び出しの一部としてその機能を渡す準備をする必要があります (Windows Hello コンパニオン デバイスが HMAC キー用のセキュリティで保護されたストレージをサポートするかどうかなど)。 同じ Windows Hello コンパニオン デバイス アプリを使用して、複数のバージョンの同じコンパニオン デバイスを管理しており、それらの機能が変更される (そして、デバイス クエリで決定する必要がある) 場合は、最初の API 呼び出しが行われる前にこのクエリを実行することをお勧めします。

最初の API (RequestStartRegisteringDeviceAsync) は、2 番目の API (FinishRegisteringDeviceAsync) で使用されるハンドルを返します。 登録の最初の呼び出しは、ユーザーが存在することを確認する PIN プロンプトを起動します。 PIN が設定されていない場合、この呼び出しは失敗します。 Windows Hello コンパニオン デバイス アプリは、KeyCredentialManager.IsSupportedAsync 呼び出しを使用して PIN が設定されているかどうかのクエリを実行することもできます。 ポリシーによって Windows Hello コンパニオン デバイスの使用が無効になっている場合、RequestStartRegisteringDeviceAsync 呼び出しが失敗する可能性もあります。

最初の呼び出しの結果は、SecondaryAuthenticationFactorRegistrationStatus 列挙型を介して返されます。

{
	Failed = 0, 		// Something went wrong in the underlying components
	Started,     		// First call succeeded
	CanceledByUser,  	// User cancelled PIN prompt
	PinSetupRequired,	// PIN is not set up
	DisabledByPolicy,	// Companion device framework or this app is disabled
}

2 番目の呼び出し (FinishRegisteringDeviceAsync) で登録は完了します。 登録プロセスの一環として、Windows Hello コンパニオン デバイス アプリはコンパニオン認証サービスを使用してコンパニオン デバイス構成データを格納できます。 このデータには 4K のサイズ制限があります。 このデータは、認証時に Windows Hello コンパニオン デバイス アプリで使用できるようになります。 このデータは、たとえば、MAC アドレスのように Windows Hello コンパニオン デバイスに接続するために使用できます。または、Windows Hello コンパニオン デバイスにストレージが存在せず、コンパニオン デバイスで PC を格納用に使用する必要がある場合は、構成データを使用できます。 構成データの一部として格納される機密データは、Windows Hello コンパニオン デバイスでのみ認識されるキーを使用して暗号化する必要があることにご注意ください。 また、構成データは Windows サービスによって格納されるため、ユーザー プロファイルを問わず Windows Hello コンパニオン デバイス アプリで使用可能です。

Windows Hello コンパニオン デバイス アプリは、AbortRegisteringDeviceAsync を呼び出して登録をキャンセルし、エラー コードを渡すことができます。 コンパニオン認証サービスは、テレメトリ データにエラーのログを記録します。 この呼び出しが適切な例として、Windows Hello コンパニオン デバイスで問題が発生し、登録を終了できなかった場合があります (HMAC キーが保存できなかった、BT 接続が失われた場合など)。

Windows Hello コンパニオン デバイス アプリには、ユーザーが (Windows Hello コンパニオン デバイスを紛失した場合や、新しいバージョンを購入した場合などに) Windows 10 デスクトップから Windows Hello コンパニオン デバイスの登録を解除するためのオプションが必要です。 ユーザーがそのオプションを選択した場合は、Windows Hello コンパニオン デバイス アプリで UnregisterDeviceAsync を呼び出す必要があります。 Windows Hello コンパニオン デバイス アプリでこの呼び出しが実行されると、コンパニオン デバイス認証サービスがトリガーされ、PC 側から呼び出し元アプリの特定のデバイス ID と AppId に対応するすべてのデータ (HMAC キーを含む) が削除されます。 この API 呼び出しでは、Windows Hello コンパニオン デバイス アプリまたはコンパニオン デバイス側から HMAC キーの削除を試みることはありません。 これは、Windows Hello コンパニオン デバイス アプリで実装するために残されています。

Windows Hello コンパニオン デバイス アプリは、登録と登録解除のフェーズで発生するエラー メッセージを表示する役割を担います。

using System;
using Windows.Security.Authentication.Identity.Provider;
using Windows.Storage.Streams;
using Windows.Security.Cryptography;
using Windows.UI.Popups;

namespace SecondaryAuthFactorSample
{
	public class DeviceRegistration
	{

		public void async OnRegisterButtonClick()
		{
			//
			// Pseudo function, the deviceId should be retrieved by the application from the device
			//
			string deviceId = await ReadSerialNumberFromDevice();

			IBuffer deviceKey = CryptographicBuffer.GenerateRandom(256/8);
			IBuffer mutualAuthenticationKey = CryptographicBuffer.GenerateRandom(256/8);

			SecondaryAuthenticationFactorRegistration registrationResult =
				await SecondaryAuthenticationFactorRegistration.RequestStartRegisteringDeviceAsync(
					deviceId,  // deviceId: max 40 wide characters. For example, serial number of the device
					SecondaryAuthenticationFactorDeviceCapabilities.SecureStorage |
						SecondaryAuthenticationFactorDeviceCapabilities.HMacSha256 |
						SecondaryAuthenticationFactorDeviceCapabilities.StoreKeys,
					"My test device 1", // deviceFriendlyName: max 64 wide characters. For example: John's card
					"SAMPLE-001", // deviceModelNumber: max 32 wide characters. The app should read the model number from device.
					deviceKey,
					mutualAuthenticationKey);

			switch(registerResult.Status)
			{
			case SecondaryAuthenticationFactorRegistrationStatus.Started:
				//
				// Pseudo function:
				// The app needs to retrieve the value from device and set into opaqueBlob
				//
				IBuffer deviceConfigData = ReadConfigurationDataFromDevice();

				if (deviceConfigData != null)
				{
					await registrationResult.Registration.FinishRegisteringDeviceAsync(deviceConfigData); //config data limited to 4096 bytes
					MessageDialog dialog = new MessageDialog("The device is registered correctly.");
					await dialog.ShowAsync();
				}
				else
				{
					await registrationResult.Registration.AbortRegisteringDeviceAsync("Failed to connect to the device");
					MessageDialog dialog = new MessageDialog("Failed to connect to the device.");
					await dialog.ShowAsync();
				}
				break;

			case SecondaryAuthenticationFactorRegistrationStatus.CanceledByUser:
				MessageDialog dialog = new MessageDialog("You didn't enter your PIN.");
				await dialog.ShowAsync();
				break;

			case SecondaryAuthenticationFactorRegistrationStatus.PinSetupRequired:
				MessageDialog dialog = new MessageDialog("Please setup PIN in settings.");
				await dialog.ShowAsync();
				break;

			case SecondaryAuthenticationFactorRegistrationStatus.DisabledByPolicy:
				MessageDialog dialog = new MessageDialog("Your enterprise prevents using this device to sign in.");
				await dialog.ShowAsync();
				break;
			}
		}

		public void async UpdateDeviceList()
		{
			IReadOnlyList<SecondaryAuthenticationFactorInfo> deviceInfoList =
				await SecondaryAuthenticationFactorRegistration.FindAllRegisteredDeviceInfoAsync(
					SecondaryAuthenticationFactorDeviceFindScope.User);

			if (deviceInfoList.Count > 0)
			{
				foreach (SecondaryAuthenticationFactorInfo deviceInfo in deviceInfoList)
				{
					//
					// Add deviceInfo.FriendlyName and deviceInfo.DeviceId into a combo box
					//
				}
			}
		}

		public void async OnUnregisterButtonClick()
		{
			string deviceId;
			//
			// Read the deviceId from the selected item in the combo box
			//
			await SecondaryAuthenticationFactorRegistration.UnregisterDeviceAsync(deviceId);
		}
	}
}

認証

認証には、コンパニオン認証サービスに対する 2 つの API 呼び出し (StartAuthenticationAsync と FinishAuthencationAsync) が必要です。

最初の開始 API は、2 番目の API で使用されるハンドルを返します。 最初の呼び出しでは、特に (一度他のものと連結された後で) Windows Hello コンパニオン デバイスに格納されているデバイス キーを使用して HMAC を行う必要のある nonce を返します。 2 回目の呼び出しでは、デバイス キーを使用した HMAC の結果が返され、認証が正常に終わる (つまり、ユーザーにデスクトップが表示される) 可能性があります。

最初の開始 API (StartAuthenticationAsync) は、初期登録後にポリシーによってその Windows Hello コンパニオン デバイスが無効にされると失敗する可能性があります。 また、API 呼び出しが WaitingForUserConfirmation 状態や CollectingCredential 状態以外で行われた場合にも失敗する可能性があります (詳細については、このセクションの後半で説明します)。 未登録のコンパニオン デバイス アプリで呼び出された場合にも、失敗する可能性があります。 SecondaryAuthenticationFactorAuthenticationStatus Enum は、考えられる結果を要約します。

{
	Failed = 0, 					// Something went wrong in the underlying components
	Started,
	UnknownDevice,    				// Companion device app is not registered with framework
	DisabledByPolicy, 				// Policy disabled this device after registration
	InvalidAuthenticationStage,		// Companion device framework is not currently accepting
									// incoming authentication requests
}

2 番目の API 呼び出し (FinishAuthencationAsync) は、最初の呼び出しで指定された nonce の期限 (20 秒) が切れると、失敗する可能性があります。 SecondaryAuthenticationFactorFinishAuthenticationStatus 列挙型は、考えられる結果をキャプチャします。

{
	Failed = 0, 	// Something went wrong in the underlying components
	Completed,   	// Success
	NonceExpired,   // Nonce is expired
}

2 つの API 呼び出し (StartAuthenticationAsync と FinishAuthencationAsync) のタイミングは、Windows Hello コンパニオン デバイスが意図シグナル、ユーザー プレゼンス シグナル、あいまいさ解消シグナルを収集する方法と一致している必要があります (詳細については、「ユーザー シグナル」を参照してください)。 たとえば、2 番目の呼び出しは、意図シグナルが使用可能になるまで送信できません。 つまり、ユーザーがその意図を表明していない場合、PC のロックを解除すべきではありません。 これをより明確にするために、PC のロック解除に Bluetooth 近接通信が使用されていると仮定すると、明示的な意図シグナルを収集する必要があります。そうしないと、ユーザーがキッチンに向かう途中で PC の側を通った途端に、PC のロックが解除されます。 また、最初の呼び出しから返される nonce は時間制限 (20 秒) があり、一定期間が過ぎると期限切れになります。 このため、最初の呼び出しは、Windows Hello コンパニオン デバイスが確実に存在することをコンパニオン デバイス アプリが認識した (たとえば、コンパニオン デバイスが USB ポートに挿入されたり、NFC リーダーにタップされた) ときにのみ実行する必要があります。 Bluetooth を使用する場合は、Windows Hello コンパニオン デバイスのプレゼンスを確認するときに、PC 側のバッテリーに影響を与えたり、その時点で実行中の他の Bluetooth アクティビティに影響を与えたりしないように注意する必要があります。 さらに、(たとえば PIN の入力による) ユーザー プレゼンス シグナルを提供する必要がある場合は、最初の認証呼び出しは、そのシグナルが収集された後でのみ実行することをお勧めします。

Windows Hello コンパニオン デバイス フレームワークは、ユーザーが認証フローのどの段階にいるかを完全に把握して、Windows Hello コンパニオン デバイス アプリが上記 2 つの呼び出しを行うタイミングを情報に基づいて判断できるよう支援します。 Windows Hello コンパニオン デバイス フレームワークでは、ロック状態の変更通知をアプリのバックグラウンド タスクに提供して、この機能を提供します。

コンパニオン デバイス フロー

これらの各状態の詳細は次のとおりです。

State 説明
WaitingForUserConfirmation この状態変化通知イベントは、ロック画面から移動した場合に発生します (例: ユーザーが Windows + L キーを押下)。 この状態にあるデバイスを見つけることが困難な場合に関連するエラー メッセージを要求しないことをお勧めします。 一般に、意図シグナルが使用可能な場合のみ、メッセージを表示することをお勧めします。 コンパニオン デバイスがインテント シグナル (NFC リーダーのタップ、コンパニオン デバイスのボタンの押下、拍手などの特定のジェスチャなど) を収集する場合、Windows Hello コンパニオン デバイス アプリは、認証するための最初の API 呼び出しを、この状態のときに実行する必要があり、Windows Hello コンパニオン デバイス アプリのバックグラウンド タスクは、インテント シグナルが検出されたことをコンパニオン デバイスから受信します。 それ以外では、Windows Hello コンパニオン デバイス アプリが PC に依存して認証フローを開始する場合 (ユーザーがロック解除画面を上にスワイプするか、スペース バーを押すことによる)、Windows Hello コンパニオン デバイス アプリは、次の状態 (CollectingCredential) を待機する必要があります。
CollectingCredential この状態変更通知イベントは、ユーザーがノート PC の蓋を開くか、キーボードの任意のキーを押すか、ロック解除画面までスワイプしたときに発生します。 Windows Hello コンパニオン デバイスが上記のアクションに依存してインテント シグナルの収集を開始する場合、Windows Hello コンパニオン デバイス アプリは (ユーザーが PC のロック解除を望んでいるかどうかを確認するコンパニオン デバイス上のポップアップなどで) その収集を開始する必要があります。 これは、Windows Hello コンパニオン デバイス アプリで、ユーザーがコンパニオン デバイスにユーザー プレゼンス シグナルを提供する必要がある場合 (Windows Hello コンパニオン デバイスで PIN を入力するような場合)、エラー ケースを提供するのに適したタイミングです。
SuspendingAuthentication Windows Hello コンパニオン デバイス アプリがこの状態を受け取った場合は、コンパニオン認証サービスが認証要求の受け入れを停止したことを意味します。
CredentialCollected これは、別の Windows Hello コンパニオン デバイス アプリが 2 つ目の API を呼び出し、コンパニオン認証サービスが、送信された内容を検証していることを意味します。 この時点で、コンパニオン認証サービスは他の認証要求を受け入れません。ただし、現在送信されている認証要求が検証に合格した場合は除きます。 Windows Hello コンパニオン デバイス アプリは、次の状態に達するまで調整を続ける必要があります。
CredentialAuthenticated これは、送信された資格情報が機能したことを意味します。 credentialAuthenticated には、成功した Windows Hello コンパニオン デバイスのデバイス ID があります。 Windows Hello コンパニオン デバイス アプリでは、それを確認し、関連付けられているデバイスが勝者かどうかを確認する必要があります。 そうでない場合は、Windows Hello コンパニオン デバイス アプリで、認証後のフロー (コンパニオン デバイスでの成功メッセージや、そのデバイスでの振動など) を表示しないようにする必要があります。 送信された資格情報が機能しなかった場合、状態は CollectingCredential 状態に変わることにご注意ください。
StoppingAuthentication 認証が成功し、ユーザーにデスクトップが表示されました。 バックグラウンド タスクを終了するタイミング。 バックグラウンド タスクを終了する前に、StageEvent ハンドラーの登録を明示的に解除します。 これにより、バックグラウンド タスクをすばやく終了できます。

Windows Hello コンパニオン デバイス アプリでは、最初の 2 つの状態で 2 つの認証 API のみを呼び出す必要があります。 Windows Hello コンパニオン デバイス アプリでは、このイベントが発生するシナリオについて確認する必要があります。 ロック解除とロック解除後の 2 つの可能性があります。 現時点では、ロック解除のみがサポートされています。 今後のリリースでは、ロック解除後のシナリオがサポートされる可能性があります。 SecondaryAuthenticationFactorAuthenticationScenario 列挙型は、次の 2 つのオプションをキャプチャします。

{
	SignIn = 0,      	// Running under lock screen mode
	CredentialPrompt, 	// Running post unlock
}

完全なコードのサンプル:

using System;
using Windows.Security.Authentication.Identity.Provider;
using Windows.Storage.Streams;
using Windows.Security.Cryptography;
using System.Threading;
using Windows.ApplicationModel.Background;

namespace SecondaryAuthFactorSample
{
	public sealed class AuthenticationTask : IBackgroundTask
	{
		private string _deviceId;
		private static AutoResetEvent _exitTaskEvent = new AutoResetEvent(false);
		private static IBackgroundTaskInstance _taskInstance;
		private BackgroundTaskDeferral _deferral;

		private void Authenticate()
		{
			int retryCount = 0;

			while (retryCount < 3)
			{
				//
				// Pseudo code, the svcAuthNonce should be passed to device or generated from device
				//
				IBuffer svcAuthNonce = CryptographicBuffer.GenerateRandom(256/8);

				SecondaryAuthenticationFactorAuthenticationResult authResult = await
					SecondaryAuthenticationFactorAuthentication.StartAuthenticationAsync(
						_deviceId,
						svcAuthNonce);
				if (authResult.Status != SecondaryAuthenticationFactorAuthenticationStatus.Started)
				{
					SecondaryAuthenticationFactorAuthenticationMessage message;
					switch (authResult.Status)
					{
						case SecondaryAuthenticationFactorAuthenticationStatus.DisabledByPolicy:
							message = SecondaryAuthenticationFactorAuthenticationMessage.DisabledByPolicy;
							break;
						case SecondaryAuthenticationFactorAuthenticationStatus.InvalidAuthenticationStage:
							// The task might need to wait for a SecondaryAuthenticationFactorAuthenticationStageChangedEvent
							break;
						default:
							return;
					}

					// Show error message. Limited to 512 characters wide
					await SecondaryAuthenticationFactorAuthentication.ShowNotificationMessageAsync(null, message);
					return;
				}

				//
				// Pseudo function:
				// The device calculates and returns sessionHmac and deviceHmac
				//
				await GetHmacsFromDevice(
					authResult.Authentication.ServiceAuthenticationHmac,
					authResult.Authentication.DeviceNonce,
					authResult.Authentication.SessionNonce,
					out deviceHmac,
					out sessionHmac);
				if (sessionHmac == null ||
					deviceHmac == null)
				{
					await authResult.Authentication.AbortAuthenticationAsync(
						"Failed to read data from device");
					return;
				}

				SecondaryAuthenticationFactorFinishAuthenticationStatus status =
					await authResult.Authentication.FinishAuthencationAsync(deviceHmac, sessionHmac);
				if (status == SecondaryAuthenticationFactorFinishAuthenticationStatus.NonceExpired)
				{
					retryCount++;
					continue;
				}
				else if (status == SecondaryAuthenticationFactorFinishAuthenticationStatus.Completed)
				{
					// The credential data is collected and ready for unlock
					return;
				}
			}
		}

		public void OnAuthenticationStageChanged(
			object sender,
			SecondaryAuthenticationFactorAuthenticationStageChangedEventArgs args)
		{
			// The application should check the args.StageInfo.Stage to determine what to do in next. Note that args.StageInfo.Scenario will have the scenario information (SignIn vs CredentialPrompt).

			switch(args.StageInfo.Stage)
			{
			case SecondaryAuthenticationFactorAuthenticationStage.WaitingForUserConfirmation:
				// Show welcome message
				await SecondaryAuthenticationFactorAuthentication.ShowNotificationMessageAsync(
					null,
					SecondaryAuthenticationFactorAuthenticationMessage.WelcomeMessageSwipeUp);
				break;

			case SecondaryAuthenticationFactorAuthenticationStage.CollectingCredential:
				// Authenticate device
				Authenticate();
				break;

			case SecondaryAuthenticationFactorAuthenticationStage.CredentialAuthenticated:
				if (args.StageInfo.DeviceId = _deviceId)
				{
					// Show notification on device about PC unlock
				}
				break;

			case SecondaryAuthenticationFactorAuthenticationStage.StoppingAuthentication:
				// Quit from background task
				_exitTaskEvent.Set();
				break;
			}

			Debug.WriteLine("Authentication Stage = " + args.StageInfo.AuthenticationStage.ToString());
		}

		//
		// The Run method is the entry point of a background task.
		//
		public void Run(IBackgroundTaskInstance taskInstance)
		{
			_taskInstance = taskInstance;
			_deferral = taskInstance.GetDeferral();

			// Register canceled event for this task
			taskInstance.Canceled += TaskInstanceCanceled;

			// Find all device registred by this application
			IReadOnlyList<SecondaryAuthenticationFactorInfo> deviceInfoList =
				await SecondaryAuthenticationFactorRegistration.FindAllRegisteredDeviceInfoAsync(
					SecondaryAuthenticationFactorDeviceFindScope.AllUsers);

			if (deviceInfoList.Count == 0)
			{
				// Quit the task silently
				return;
			}
			_deviceId = deviceInfoList[0].DeviceId;
			Debug.WriteLine("Use first device '" + _deviceId + "' in the list to signin");

			// Register AuthenticationStageChanged event
			SecondaryAuthenticationFactorRegistration.AuthenticationStageChanged += OnAuthenticationStageChanged;

			// Wait the task exit event
			_exitTaskEvent.WaitOne();

			_deferral.Complete();
		}

		void TaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
		{
			_exitTaskEvent.Set();
		}
	}
}

バックグラウンド タスクの登録

Windows Hello コンパニオン デバイス アプリでは、最初のコンパニオン デバイスを登録するときに、デバイスとコンパニオン デバイス認証サービスの間で認証情報を渡すバックグラウンド タスク コンポーネントも登録する必要があります。

using System;
using Windows.Security.Authentication.Identity.Provider;
using Windows.Storage.Streams;
using Windows.ApplicationModel.Background;

namespace SecondaryAuthFactorSample
{
	public class BackgroundTaskManager
	{
		// Register background task
		public static async Task<IBackgroundTaskRegistration> GetOrRegisterBackgroundTaskAsync(
			string bgTaskName,
			string taskEntryPoint)
		{
			// Check if there's an existing background task already registered
			var bgTask = (from t in BackgroundTaskRegistration.AllTasks
						  where t.Value.Name.Equals(bgTaskName)
						  select t.Value).SingleOrDefault();
			if (bgTask == null)
			{
				BackgroundAccessStatus status =
					BackgroundExecutionManager.RequestAccessAsync().AsTask().GetAwaiter().GetResult();

				if (status == BackgroundAccessStatus.Denied)
				{
					Debug.WriteLine("Background Execution is denied.");
					return null;
				}

				var taskBuilder = new BackgroundTaskBuilder();
				taskBuilder.Name = bgTaskName;
				taskBuilder.TaskEntryPoint = taskEntryPoint;
				taskBuilder.SetTrigger(new SecondaryAuthenticationFactorAuthenticationTrigger());
				bgTask = taskBuilder.Register();
				// Background task is registered
			}

			bgTask.Completed += BgTask_Completed;
			bgTask.Progress += BgTask_Progress;

			return bgTask;
		}
	}
}

エラーとメッセージ

Windows Hello コンパニオン デバイス フレームワークは、サインインの成功または失敗に関するフィードバックをユーザーに提供する役割を担います。 Windows Hello コンパニオン デバイス フレームワークには、Windows Hello コンパニオン デバイス アプリが選択できる (ローカライズされた) テキスト メッセージとエラー メッセージのストックが用意されています。 これらはログオン UI に表示されます。

コンパニオン デバイス エラー

Windows Hello コンパニオン デバイス アプリでは、ShowNotificationMessageAsync を使用して、ログオン UI の一部としてユーザーにメッセージを表示できます。 意図シグナルが使用可能な場合は、この API を呼び出します。 意図シグナルは、常に Windows Hello コンパニオン デバイス側で収集する必要があることにご注意ください。

メッセージには、ガイダンスとエラーの 2 種類があります。

ガイダンス メッセージは、ロック解除プロセスを開始する方法をユーザーに示すように設計されています。 これらのメッセージは、最初のデバイス登録時にロック画面でユーザーに一度だけ表示され、再び表示されることはありません。 これらのメッセージはロック画面に引き続き表示されます。

エラー メッセージは常に表示され、意図シグナルが提供された後に表示されます。 ユーザーにメッセージを表示する前に意図シグナルを収集する必要があること、およびユーザーが Windows Hello コンパニオン デバイスのいずれかを使用してのみ、その意図を示すことを考慮すると、複数の Windows Hello コンパニオン デバイスがエラー メッセージの表示で競合する状況があってはなりません。 結果的に、Windows Hello コンパニオン デバイス フレームワークではキューを保持しません。 呼び出し元がエラー メッセージを要求すると 5 秒間表示され、その 5 秒間エラは、エラー メッセージの表示に関する他の要求はすべて削除されます。 5 秒が経過したら、別の呼び出し元がエラー メッセージを表示する機会が生じます。 呼び出し元がエラー チャネルを妨害することは禁止されています。

ガイダンスとエラー メッセージは次のとおりです。 デバイス名は、ShowNotificationMessageAsync の一部としてコンパニオン デバイス アプリによって渡されるパラメーターです。

ガイダンス

  • "上にスワイプするか、スペース バーを押して、デバイス名でサインインします。"
  • "コンパニオン デバイスの設定。 待機するか、別のサインイン オプションを使用してください。"
  • "デバイス名を NFC リーダーにタップしてサインインします。"
  • "デバイス名を探しています..."
  • "デバイス名を USB ポートに接続してサインインします。"

エラー

  • "サインイン手順については、デバイス名を参照してください。"
  • "デバイス名を使用してサインインするには、Bluetooth をオンにします。"
  • "デバイス名を使用してサインインするには、NFC をオンにします。"
  • "Wi-Fi ネットワークに接続し、デバイス名を使用してサインインします。"
  • "デバイス名をもう一度タップします。"
  • "お客様の企業では、デバイス名を使用したサインインが禁止されています。 別のサインイン オプションをご使用ください。"
  • "デバイス名をタップしてサインインします。"
  • "サインインするには、デバイス名に指を置いてください。"
  • "サインインするには、デバイス名を指でスワイプしてください。"
  • "デバイス名でサインインできませんでした。 別のサインイン オプションをご使用ください。"
  • "エラーが発生しました。 別のサインイン オプションを使用して、デバイス名をもう一度設定してください。"
  • "やり直してください。"
  • "音声パスフレーズをデバイス名に入力してください。"
  • "デバイス名でサインインする準備ができました。"
  • "最初に別のサインイン オプションを使用した後で、デバイス名を使用してサインインできます。"

登録済みデバイスの列挙

Windows Hello コンパニオン デバイス アプリでは、FindAllRegisteredDeviceInfoAsync 呼び出しを使用して、登録済みのコンパニオン デバイスの一覧を列挙できます。 この API は、SecondaryAuthenticationFactorDeviceFindScope 列挙型を使用して定義された 2 つのクエリの種類をサポートしています。

{
	User = 0,
	AllUsers,
}

最初のスコープは、ログオンしているユーザーのデバイスの一覧を返します。 2 番目のスコープは、その PC 上のすべてのユーザーの一覧を返します。 登録解除時は、別のユーザーの Windows Hello コンパニオン デバイスの登録を解除しないよう、最初のスコープを使用する必要があります。 認証時または登録時には 2 番目のスコープを使用する必要があります。この列挙型は、アプリが登録時に、同じ Windows Hello コンパニオン デバイスを 2 回登録するのを回避するのに役立ちます。

なお、アプリがこの確認を実行しない場合でも、PC は、同じ Windows Hello コンパニオン デバイスが複数回登録されないように拒否します。 認証時、AllUsersスコープを使用すると、Windows Hello コンパニオン デバイス アプリは、ユーザー B がログインしている時にユーザー A をログオンするという、ユーザー フローの切り替えをサポートします (これには、両方のユーザーが Windows Hello コンパニオン デバイス アプリをインストールしている必要があり、ユーザー A がコンパニオン デバイスを PC に登録済みで、PC にロック画面 (またはログオン画面) が表示されている必要があります)。

セキュリティ要件

コンパニオン認証サービスでは、次のセキュリティ保護が提供されます。

  • 中規模のユーザー コンテナーまたはアプリ コンテナーとして実行されている Windows 10 デスクトップ デバイス上のマルウェアでは、Windows Hello コンパニオン デバイスを使用して、PC 上のユーザー資格情報キー (Windows Hello の一部として格納) にサイレント アクセスすることはできません。
  • Windows 10 デスクトップ デバイス上の悪意のあるユーザーは、その Windows 10 デスクトップ デバイス上の別のユーザーに属している Windows Hello コンパニオン デバイスを使用して、(同じ Windows 10 デスクトップ デバイス上で) そのユーザーの資格情報キーへのサイレント アクセスを取得できません。
  • Windows Hello コンパニオン デバイス上のマルウェアでは、Windows 10 デスクトップ デバイス上のユーザー資格情報キーにサイレント アクセスできません。これには、Windows Hello コンパニオン デバイス フレームワーク専用に開発された機能やコードの活用も含まれます。
  • 悪意のあるユーザーは、Windows Hello コンパニオン デバイスと Windows 10 デスクトップ デバイスの間のトラフィックをキャプチャし、後で再生するという方法で、Windows 10 デスクトップ デバイスのロックを解除できません。 Microsoft のプロトコルで nonce、authkey、HMAC を使用すると、リプレイ攻撃に対する保護が保証されます。
  • マルウェアや不正な PC 上の悪意のあるユーザーは、Windows Hello コンパニオン デバイスを使用して正直なユーザーの PC にアクセスすることはできません。 これは、コンパニオン認証サービスと Windows Hello コンパニオン デバイス間の相互認証を通じて、プロトコルでの認証キーと HMAC の使用によって実現されます。

上に列挙したセキュリティ保護を実現する鍵は、承認されていないアクセスから HMAC キーを保護し、ユーザー プレゼンスも確認することです。 具体的には、次の要件を満たす必要があります。

  • Windows Hello コンパニオン デバイスの複製から保護する
  • 登録時に HMAC キーを PC に送信する際の傍受から保護する
  • ユーザー プレゼンス シグナルが使用可能であることを確認する