# FIDOを用いたIntMediumアカウント認証 ### 結論から言うと、FIDOはプライバシー重視のため、鍵をFIDO認証以外に使うことができない仕様になっている。 FIDO認証のために生成したキーペアの公開鍵は、生成時にしか取り出すことができない。これを信頼するサーバーに保管してもらい、毎回の認証時の署名検証に使ってもらう方式。ブラウザ↔デバイス間のWebauthn APIもこうなっているし、デバイスもこうなっている。 したがって、同じサイト・ユーザーIDに対して同一の公開鍵が得られるのは一度きり。次回以降create()しても新しい公開鍵が得られるか、同一のユーザー名・サイトに対して複数のキーペアを生成しない指示があればエラーが返ってくる。以前の公開鍵は得られない。 また、get()によるメッセージ署名を使い、同一のメッセージに対して同一の署名が得られればそれを秘密鍵として使う作戦も考えられたが、Webauthnにおけるメッセージへの署名はたとえ同一のメッセージと鍵を用いても、デバイスから得られるエントロピーか署名カウンタによって毎回異なる仕組みであり、再現可能な署名は得られない。アルゴリズムにECDSAではなくRSAを指定してもである。 また、前述の通り署名単体から公開鍵を復元することもできない。 ブラウザからハードウェア認証機へのアクセスを標準化した功績がある一方で認証に非常に特化したWebauthnを拡張して汎用的な署名・鍵交換を可能にしてほしいという提案は、2021年5月のワーキンググループで却下されたようである。 https://github.com/w3c/webauthn/issues/1608#issuecomment-833009489 その後WebCryptoにも提案されたが、雰囲気的に導入される見込みはなさそう。 https://github.com/w3c/webcrypto/issues/263 したがって、FIDOを利用して鍵を生成するなら、生成時にその鍵をどこかに保管する必要がある。 脆弱性を無視して、パスワードなどで暗号化してlocalStorageに保管しても消えてしまう場合があるので、バックアップも必要。使うときはパスワード入力、もしlocalStorageになければバックアップから復元…となると、それMetamaskでは? FIDO認証の使用感にこだわるなら、サーバーが鍵を保管してくれるカストディアルなウォレットを作ることしかできなさそう。 ## IntMedium Identity ### 鍵生成フロー 1. username, rpIdからWebauthnでキーペアを作成する。 2. usename, credIdを認証サーバーに投げて登録する。 3. 登録が成功したら、公開鍵のx座標を秘密鍵として扱いlocalStorageに保存。 4. 以後、この秘密鍵を使う。 5. もし以後username認証が必要であれば認証サーバーとFIDO認証できる。 ### 秘密鍵でのログインフロー 1. localStorageに秘密鍵があるなら、ユーザーは確認ボタンを押してログインする。 2. なければリカバリーから復旧する。 3. コールバックするなどしてWeb3jsに託す。 ### usernameでのログインフロー (未実装) 1. 認証サーバーとFIDO認証する。 2. サーバー側からコールバックする。 ### 利用フロー 1. Web3jsでEVMサーバーに接続する。 2. トランザクションを準備する。 3. localStorageにcredentialIDとusernameがあれば、それを利用してFIDO認証をしてサーバーから送ってもらった秘密鍵でトランザクションに署名する。 4. localStorageにcredentialIDがなければ、認証サーバーと認証してusernameを指定してcredentialIDを送信してもらうか、バックアップから復元し、トランザクションに署名する。 5. Web3jsで署名済みトランザクションを送信する。 ## シーケンス図 ### 登録時 ```mermaid sequenceDiagram actor U as User participant I as IntMedium ID app participant S as ID Server autonumber I ->> S : submit desired username S ->> I : (if unused) send username, challenge U ->> I : (FIDO) auth(username, challenge) I ->> S : send auth, credId for storage S ->> I : ack I ->> I : add username, credId to localStorage I ->> I : (optional) backup credId, pk ``` ### credIdがlocalStorageにない場合の復旧(認証の場合) ```mermaid sequenceDiagram actor U as User participant I as IntMedium ID app participant S as ID Server autonumber I ->> S : (if credId ∉ localStorage) req credId for username S ->> I : send credId U ->> I : (FIDO) genPk(username, credId) I ->> I : add username, credentialId to localStorage I ->> I : (optional) backup credId, pk ``` ### credIdがlocalStorageにない場合の復旧(リカバリーの場合) ```mermaid sequenceDiagram actor U as User participant I as IntMedium ID app autonumber U ->> I : recover credId from backup U ->> I : (FIDO) genPk(username, credId) I ->> I : add username, credentialId to localStorage ``` ### TX署名時 ```mermaid sequenceDiagram actor U as User participant I as IntMedium ID app participant F as Dapp Frontend, web3.js participant S as ID Server autonumber F ->> I : transaction I ->> I : check localStorage for credId I ->> S : (if none) request credId for username S ->> I : credId U ->> I : (FIDO) genPk(username, credId) I ->> I : sign TX with pk I ->> F : signed TX to send ``` ## メモ:Internet Identity Dfinity上のIDを管理する[Internet Identity](identity.ic0.app)というサービスの使用感と仕組みを参考にする。詳細な仕様は[ここ](https://github.com/dfinity/internet-identity/blob/main/docs/internet-identity-spec.adoc)にある。 予めInternet Identity上でFIDO署名を使ったクレデンシャルをサーバーに登録しておく。このとき、バックアップ用のシードフレーズと、短いユーザーID(Anchor ID)が発行される。 Dappsを使うときにはDappsごとに異なるクレデンシャルが発行されるが、それらを1つのAnchor IDに紐付けることになる。1つのAnchor IDに複数台のFIDOデバイスをInternet IdentityのDB上で紐付けることもできる。 指紋認証+ユーザーIDというシンプルさが良い。短所として、サービスに依存している。