=kthrtty/(+blog)

最近旅してないフルスタックサラリーマンジャーマネの旅行の記録

思わず天を仰いでしまうID関連システムトラブル

こんにちは。アドカレ12/24の記事を簡単にではありますが書かせていただきました。(25日のポストで遅刻ですが)

Digital Identity技術勉強会 #iddanceのカレンダー | Advent Calendar 2023 - Qiita

はじめに

本日のテーマ:思わず天を仰いでしまうID関連システムトラブル

本日のテーマは、みんな大好き「トラブル」の話です。CIAM(Consumer Identity and Access Management)領域のさまざまなシステムにさまざまな立場で関わり、さまざまなトラブルに遭遇してきた経験を踏まえて、クリスマスの合間の気楽な読み物として記載しましたので、一息ついていただければ幸いです。

今回はトラブルの中でも思わず「天を仰いでしまう」激ヤバトラブルにフォーカスして、私的ランキング形式でお届けしたいと思います。

 

天を仰ぐトラブルとは?

私の経験の範疇ですが、事象の初報を聞いた、あるいはトラブルメールが届いた際、その瞬間に「あぁ最悪だ終わった」という気持ちで天を仰いでしまうようなトラブルです。オフィスチェアに座っている場合は、背もたれでのけぞって精一杯「終わった」感を表現します。

緊急召集をかけ、対策室として他チームの予定でぎっしりの会議室を強引に確保させてもらい、全体統括、お客様との渉外担当、時系列の記録係、本番アクセスログ分析、アプリ分析担当など分担を決めて対処していきます。

全社のトラブル経営連絡MLにも投げ込みます。しばらくするとどこかの部門から突如横槍がはいります。予想通りです。関係性が良くない役員がいたりした場合「ほらみたことか、あのシステムはやばいから注意して見ておけ」などというバイアスまみれの事前吹聴により、本社セキュリティ部門から謎の連絡要員がやってくることもあるでしょう。別にいいです受け入れます。バイアスなく冷静にみてくれるのであれば。

お客様が「30分に1回」進捗を報告せよ、と仰る気持ちは理解しつつも、対策室には「短すぎて進捗ないよ・・・」と暗い雰囲気が立ち込めます。数時間のうちにお客様側では基幹システム停止時などに開催される緊急役員会議が開催され、報告できるほど状況が整理しきれていなくても報告をしに行きます。トラブル耐性の強い社長あるいは担当役員であれば自ら「人質」を買って出てくれることでしょう。短期的なトラブル解決に直接作用するわけではありませんが、アイコニックな存在としてお客様の経営に「本気」を伝えつつ、強力な緩衝帯として機能してくれます。こうありたいと思います。時にはテンパリ過ぎて恫喝傾向が出る人がいたりと、強いプレッシャーに曝されることによる人間の本質が垣間見えることもあります。こうありたくはないと思います。

数日は寝れない&帰れないかもしれません。クリスマスのデートや長期休暇の旅行をキャンセルする人もいるかもしれません。さすがに休暇の特別な予定を返上させるようなことのない組織と体制であれと常々思います。

 

長く仕事をしていれば、そういうトラブルに対処しなければならないことありますよね。

 

注意事項

なお、今回掲載しているトラブルは、一部の明らかな事実への参照を除いて全てフィクションです。現実の出来事、人物、所属する組織とは一切関係ありません。バグは存在する、トラブルは起きることを前提としています。

 

どいういうバックグランドで書いているか

主にCIAM領域で10年超の経験を持ちます。内容は営業・企画・コンサル・開発・運用まで様々でした。およそ15年前にOpenID 2.0のDraft版に関わってからの縁で、OpenID Foundation Japanの法人設立を進めたり、OP/IdPのSIをしたり、IDビジネスコンサルをしたり、Identity関連のデジタル犯罪のインシデント対応などをしていました。

その後は、がらり変わってWebセキュリティ診断、重要インフラや自動車セキュリティ事業を率いたのち、現在はクラウドのセキュリティ課題解決に日々勤しんでいます。

ご縁もあり、去年から紀尾井町方面のIdentity関連の会議に呼んで頂いており、世の中が少し良くなるようにと取り組んでいます。重鎮が多く視点も充実しているので寧ろ楽しんでいるきらいもあります。

トラブル

早速、勝手ながらランキング形式でお届けします。

 

第3位 任意のユーザーへのなりすましが可能

第3位はOWASP Top 10でも登場する、アクセス制御不備(Broken Access Control)を原因とした「任意のユーザーへのなりすまし」です。これ1位じゃないのと思う方がいるかもしれませんが、私が勝手に決めたランキングなので気にしないでください。

 

「大変です!多くの一般ユーザーの方から意図しない取引履歴があるというお問い合わせが殺到しているみたいです!」と一報がはいりました。天を仰ぎます。つらい。

 

この事象は、特にID関連システムの文脈で原因を考えると、以下が定番でしょう。

  • 認可機構と業務ロジックとの対象ユーザ不整合
    • 例えばOAuth/OIDCベースのAPI認可を採用しているシステムにおいて、アクセストークンを利用してAPI認可しているのに、業務ロジックではペイロードで指定されているID識別子だけをみて動作してしまい、誰でもいいからAPI認可さえ通過すればペイロードで指定した任意のユーザーに干渉できるというものです。よくIDOR(Insecure Direct Object Reference)脆弱性と言われたりするのを見かけたことがある方いるかもしれません。
  • 認証アサーションの検証不備
    • 認証アサーション(OIDCだとID Token)の署名チェックに不備があり、例えば自由に組み立てたID Token(JWT)に埋め込まれたユーザーのID識別子だけを見て動作してしまうので、任意のユーザーの認証アサーションを作成し、なりすませてしまうというものです。7payのケースでは似たような事例が報告されていますが、報道ではこの問題が原因ではないとされています。

 

内容自体はシンプルなのですが、いずれも根深い不具合です。

認可機構と業務ロジックとの対象ユーザ不整合

極めて単純で、共通の認可処理で特定できるユーザーと、ペイロードで指定された(おそらくユーザーの所有する)何らかのオブジェクトの識別子との関連性のビジネスルールをチェックすべきところをしていない、というものです。ただ、そうなってしまう気持ちはわかります。共通の認可処理と、個々のAPIの業務ロジックを作っている人が分業・あるいは認可処理がAPI Gatewayに分離されていたりすると、システムを組み上げた際の視点が乏しいコードレビューやテストケースでは抜けることもあるかもしれません。

勝手ながら私の好みでいうと、ユーザーの権限で動作するAPIについてはアクセストークンからユーザーを特定せざるを得ないようにペイロードにユーザーIDを入れないようにして、APIロジックの作成者にアクセストークンから特定されるユーザーを意識せざるを得なくする、あるいはAPIの操作対象のマイカーIDみたいなものをペイロードに入れたとしても、データベースのテーブル定義で最初から(ユーザーID, マイカーID)のタプルでなければ検索できないようにしてIDORが起こりにくいような"Fail Safe"なAPI設計は良いと思っています。

ただし、作り手への制約の付け方はチームの構成や開発スタイルに大きく依存するので、一例として記載するに留めておきます。制約を課す設計が大嫌いな先輩と口論になったことがあり、後輩には冷ややかな目で「またあいつらやってるよ」と思われてしまったようで反省しています。

冒頭に書いた通り、ちょっと前に自動車セキュリティをやっていたのですが、コネクテッドサービスのアプリでもIDORの脆弱性が話題になることを複数回見かけました。サイバーフィジカルなシステムにおいてはアクセスコントロールの不備は物理世界への干渉につながるので、下り(車両側)方向の制御というのは慎重に慎重を期して開発していくものだと常々考えています。IoTにアイデンティティレイヤー導入するサイバーフィジカルなコネクテッドサービスは本当に楽しいので、利用者の誰もがセキュリティを気にせず楽しいところだけ味わってもらえると良いですよね。

認証アサーションの検証不備

こちらもフェデレーションにおいては伝統的かつ影響の大きな問題です。SAMLでもOIDCでも時々見かけます。以下のようなイメージです。

  • アサーションが署名されておらず、チェックもしておらず、なりすましにつながる
  • アサーションが署名されているが、チェックしておらず、なりすましにつながる
  • アサーションが署名されており、チェックもしているが、署名範囲が誤っており、結局なりすましにつながる

 

今回は特にOIDCのID Tokenを取り上げます。今でこそセキュリティ診断を生業としている方達の中でも常識となりましたが、数年前はJWTのヘッダに "alg":"none" として署名なしを選択したJWTを作成し、ID Tokenを受け付けるエンドポイントに投げつけたりすると、署名なしを受け入れて、ペイロードに記載されたユーザーID識別子を署名未検証で信じて動く、なんてものもありました。

サービス仕様で「署名なしは選べない」 と明記しつつ、テストをしておけば良い話ではあるのですが、ライブラリに任せてよしなにやってもらうことの多い処理だったり、そもそもJWT/JWSの署名なしが可能ということ自体に知見がないなどの理由で、うっかり作り込んでしまうこともあるでしょう。

IdPを開発するチームであれば、当然落としたくない観点ですよね。

 

第2位 ユーザーの入れ替わりが発生

悩んだのですがこちらを2位にします。「ユーザーの入れ替わり」が発生してしまった話です。リクエスト毎のスコープで同期的に物事を処理していけば、そんなことが起きる実装には普通ならないと思うのですが、OAuth2/OIDCの認可コードフローなどリクエスト元がブラウザとサーバーなど混在したりする場合や、非同期処理が出てくるとやらかし事例が出てくることはあるかもしれません。

 

「大変です!キャンペーンメールの一斉配信でアクセス集中したころから、ユーザーの入れ替わりが起きてるとお客様から連絡がありました!」と一報がはいります。天を仰ぎます。つらい。

 

典型的な原因はレースコンディション(Race Condition)です。並行度の高いアクセス状態が継続すれば、影響範囲がどんどん拡大していきます。「ちょっとログを見たぐらいでは分からない」類の障害のため、判断に時間をかけている間に、ダメージが蓄積していくのでつらいです。自主事業であれば止める判断が比較的素早くできるかもしれませんが、お客様事業だと止める判断を(あとから内部で刺されないように段取りアレンジと正当化)するため「経緯と影響範囲をまとめろ」みたいなことを言われます。事象認知の初期段階でこれが正確にできる人・会社はおそらく存在しません。ふわっとした速報しか出せません。少ない情報での判断を躊躇している間に影響範囲は広がります。サービスを止める判断基準は、平時のうちに議論しておきたいですね。

レースコンディションは細かく見るとさまざまな実装上の不具合が根本原因ですが、私がパッと心当たりがある範囲では以下のようなイメージです。

  • スレッドセーフでないコンポーネントクリティカルセクションを設けずに利用する、あるいは排他制御にバグがあるライブラリ利用により、入れ替わりが発生する
  • 精度の低いタイムスタンプを用いた導出ロジックで、ノード内・ノード間でユーザーに関する識別子が競合する
  • 乱数生成器が適切なものではなかなったり、乱数のシードがリクエストの都度同じ値で初期化されて同じ疑似乱数が生成されるなどの利用方法の不備で、ノード内・ノード間でユーザーに関する識別子が競合する
  • 特定条件でユーザーに関する識別子の元ネタ変数がNullのまま動き、ユーザーに関連するID「0」と評価されて競合する
  • TOCTOU(Time of Check, Time of Use)の問題でユーザーに関する何らかの値が競合する


この手の問題の検出にはまず設計とコードレビュー等前段で打ち取れるのが望ましいとして、私は負荷テストとの合わせ技で打ち取るのが大好きです。後から慌てないようにWBSには負荷テスト入れておきましょう。その際、データを大量に積み込んで認証周りの負荷テストするのは当たり前に実施されていると思いますが、ユーザーの入れ替わりも検出できるようにしておくことをお勧めします。例えばOP/IdPであれば、認可コードフローを回しつつ、UserInfoを叩いてレスポンスをみてユーザー入れ替わりを検知するAssertを入れておくことで効果があるでしょう。

最近では、マイナンバーカードを利用したコンビニで他人の住民票を発行できてしまうケースが報道されていました。件数の大小に関わらずステークホルダーのボルテージが上がりやすいID関連のトラブルですので、気をつけたいものです。

 

第1位 Pay系サービスと金融機関との連携で不正に金銭が窃取された

これも独断と偏見ですが、「Pay系サービスと金融機関との連携で不正に金銭が窃取」を第1位にしました。有名すぎるが故に名前が上がってしまう象徴的なものとしてはドコモ口座事件が記憶にある方も多いのではないでしょうか。これは、Pay系アプリで決済をするために資金をチャージする手段の1つとして、自身の銀行口座を連携させて紐づけるような機能を有するサービスでした。この連携・紐付け部分の弱さを突かれ、一般ユーザーの資金が不正に窃取されたとされる事案です。なお、ドコモに限らず、その周辺では似たような事象が風の噂で色々と聞こえてきています。事前か事後かは知るところではありませんが、いくつかの類似サービスで銀行との連携を停止する、といった対応がなされていたのを思い出します。

 

「大変です!一般利用者からうちのアプリの名前で銀行から身に覚えのない金銭の引き出しが行われた問い合わせが殺到しているみたいです!」と一報がはいりました。天を仰ぎます。つらい。

 

細かく見るとさまざまな原因がありますが、典型的なものはチャージ(入金)元の銀行と連携する際に、口座番号とキャッシュカードの4桁PINで認証が成功し紐付けができてしまい、更に銀行とPay系サービスの間で業務取引(チャージ操作に伴う資金移動など)ができるようになる、という点です。通常お金を引き出す際にはキャッシュカード(Something you have:所持)と4桁PIN(Something you know:記憶)の2つの要素が必要ですが、事象が発生するPay系サービスの連携ではいくつかの金融機関では「お客様番号/口座番号」と4桁PINがあれば資金移動ができる、つまりキャッシュカード(≒カード所有者)不在かつインターネット経由で経由で大量に、あるいはすでに漏洩している他の情報と組み合わせて戦略的に試行を繰り返しやすい仕組みになっていた、というわけですね。この時、口座番号+4桁PIN認証(+名義)ではなく、ある程度実績があり日々の不正な試行に対する何らかの取り組みがなされているインターネットバンキングのユーザー認証への導線を提供していた金融機関では被害が発生していない、あるいは発生していたとしても話題になっていないと記憶しています。

では、そもそもなぜこのような連携・紐付けをするサービス設計になるのかですが、その根拠は犯収法における「依拠」が関係します。ざっくりいうと依拠というのは「すでに銀行などの金融機関が法律に則って身元確認(Identity Proofing)を実施してくれているので、銀行で当人認証(Authentication)が成功すれば、自分たちのサービスでも推移的に Identity Proofing したとみなしても良いだろう」ということです。しかし現実には「依拠」という定義が曖昧なものを曖昧なまま取り扱っては考慮不足が出てしまい、実はきちんとパターン分けをして、リスク分析をしていなければ望ましくないことが発生するものである、ということをシステム関係者は認識するきっかけになった出来事でした。

なお、ここで「依拠するための」銀行側の認証が強化されればそれで万事OKということではありません。手前味噌ですが参考に以前作った資料からの抜粋です。

身元確認(Identity Proofing)のレベル、当人認証(Authentication)のレベルがそれぞれPay系サービス側、連携する金融機関側の組み合わせを一通り見て、もし〜だったら、自分のサービスには何が起きるのだろう、と考えていただくのが検討の良いスタート地点です。

 

例えば、分かりやすい例だと、ポイント集約が挙げられます。好きなだけPay系サービスのアカウントを作成でき、新規登録ポイントキャンペーンをやっていると、万が一ポイントを知り合いに送る機能があったりすると、ポイント無限集約という経済的なダメージが生じるわけです。バーチャルクレジットカードのチャージをカスケードさせてポイント多重取りしようとする話も似たような類の話ですね。

 

この整理で出てくるIdentity Proofingのレベル(IAL)、あるいはAuthenticationのレベル(AAL)は、認証にまつわる米国政府機関向けのガイドラインであるNIST SP800-63 Digital Identity Guidelineを参照いただくと概念を理解するのに良いのですが、結構ボリュームがあってわかりづらいという声もよく聞きます。

そういった場合は、これまた手前味噌ですが以下の「認証にまつわるセキュリティの新常識 rev.3」も併せて見ていただけると理解が捗りやすいと思います。

speakerdeck.com

 

またIdentity Proofingの深淵を覗くべくアマゾンの奥地へ旅立っていきたい方は、これまた手前味噌ですが私の「NIST SP800-63-4 IPD 63A: Identity Proofing 概要紹介 - Speaker Deck」も参考にしてみてください。

speakerdeck.com

崎村御大の「本人確認のニューノーマル」も外せませんね。

 

あるいはOpenID Foundation Japan KYC WGのガイドラインも参考になります。

www.openid.or.jp

 

NIST SP800-63は現在Revision4の改訂作業中で、現行版であるRev.3ではサービス同士の連携プロトコルのセキュリティレベルとしてFederation Assuarance Level(FAL)というものも定義されるようになりました。第1位の論点については「これを見ればよいじゃん」と思われるかもしれませんが、業務的な観点はほぼなく、暗号化や署名をするしないの話が中心です。サービス同士の連携を考える場合は、FALは足回りのセキュリティの参考にしつつ、その上位レイヤにサービス同士の連携に伴う業務上のリスクを考慮した対処を載せていくと良いでしょう。

 

ここまで私的に好みの「天を仰いでしまう」トラブルを3つご紹介してきました。
サービスの実装をする人も、サービス仕様の設計をする人も、IDに関する考慮事項やアンチパターンは多々あります。今回のフィクションを参考に、皆様が面倒ごとに巻き込まれずに平和に過ごすことができれば幸いです。

 

おまけ 「さとみ1224事件」

石原さとみさんの誕生日は12月24日です。メリークリスマス。

 

あるセンシティブな業界向けのお客様のシステムで、「大変です!お客様からコールセンターに連絡があり、ログインしたところ別の人の情報が見えたみたいです!」と一報がはいったとします。天を仰ぎます。つらい。

 

他人に見られてしまったお客様のアカウント名は "satomi1224" というものだという情報が寄せられます。パスワードは平文で保存していませんので分かりません。ログを見ますが何もおかしいところはありません。エラーも出ていません。並行でログイン回りのマルチスレッド処理に問題がないかをチェックする人がアサインされ、あるのか分からないバグを探し始めます。原因が分からないことへのお客様の苛立ちはつのり、ただ再現もせず暗中模索で悶々とした時間が過ぎていきます。大きな取引先ですので、頼んでもいないのによその部門からプレッシャーをかけにくる連絡要員が対策室にくることがあるかもしれません。

 

事象をご報告いただいたエンドユーザーの方と連絡がとれ、コールセンター部門から追加の情報が入ります。見てしまったお客様のお名前は「さとみ」さんで誕生日は「12月24日」だというではありませんか。

 

もうお分かりですね。同名かつ同じ誕生日の別の2人の方が、間違えて別の方のID/PWに一発で直撃させてログインしてしまうことがある、という話です。パスワードにIDと同じ文字列や誕生日などが含まれないようにチェックする、というのは悪意のない方が引き起こす偶発的なトラブルの防止にも役立つ訳ですね。

 

原因がわかるとプレッシャーをかけ続けてきた人たちはそそくさと帰っていくでしょう。憤りと怒りはそっと胸にしまいこんで、心の中の⚪︎⚪︎リストに加えておくにとどめます。何事もなかったのであればそれでいいのです。

 

メリークリスマス。

 

AWS IoT GreenGrassのLocal Debug Consoleを使ってみる(Cloud9編)

はじめに

前提

  • IoT GreenGrass v2がCloud9環境に導入済み
  • 自分のSSH鍵生成済み

結論

  • GreenGrass v2のLocal Debug Consoleでできること
  • コンポーネント間の依存関係をグラフで表示してくれる(yamlを呼んで頭で想像しているのとそんなに変わらない)
  • コンポーネントの設定をすぐ見れる(Cloud9のシェル操作して1個1個みるのでも良い)
  • コンポーネントのStop/Startがダッシュボード的にできる(Cloud9のシェル操作して落とし上げしても良い)
  • ローカルのログをグリグリ見れたりはしないので、そこまでデバッグに役立つわけではない。今後に期待。

参考

docs.aws.amazon.com

日本語訳が平常運転なので必要に応じて英語に切り替えて参照。

Cloud9のインスタンスのSecurity Groupで必要な穴あけをする

マネジメントコンソールで環境を選択して、"View details"。 f:id:kthrtty:20210811115947p:plain

"Security Groups"からSG設定へ行き必要なインバウンドルールを追加してください。穴あけすぎ注意。 f:id:kthrtty:20210811120205p:plain

Cloud9にSSHキーを設定する

Cloud9のターミナルから普通に自分の公開鍵を追加します。 先頭にCloud9サービス提供のために使う公開鍵があるので消さないこと。

$ vi .ssh/authorized_keys

SSH接続しつつローカルポートフォワード

前述画面で"EC2 Instance"でインスタンス設定へ飛びます。 パブリックIPを把握してssh接続するさい、ローカルデバッグコンソールがサービスしているポートをLocal Fowardしておきます。

~ % ssh -L 1441:localhost:1441 -L 1442:localhost:1442 {user}@{ip} -i {秘密鍵}

その際、Cloud9のシェルに表示されているユーザ名(Admin)が実はwhoamiすると”ubuntu”だったりして、接続時のユーザー名誤りで地味にハマるので注意するとよいです。

ブラウザでアクセスしてみる

最近はオレオレ証明書のWebサーバへのアクセスが著しく制限されるので、いくつかブラウザを変えながらやりました。Edgeには選択権すら与えられませんでしたが、Safariでは選べばアクセスできました。

f:id:kthrtty:20210811115226p:plain

コマンドの備忘録

$ sudo /greengrass/v2/bin/greengrass-cli component list
$ sudo /greengrass/v2/bin/greengrass-cli component (start|stop|restart) --names "{コンポーネント名}"

Yubikey4に秘密鍵を保存してPuttyでSSHログインする

はじめに

  • Yubikey4をたまたまソフト技研のスポンサードするイベントか何かでもらっていた
  • 職務上の変化でSSHログインする機会が増えそうだった
  • そのためYubikey4に手元のキーペアをインポートして、USBドングルでログインできる環境を作ろうと思った

なお、

  • OneDriveのPersonal Vaultに鍵は保存するのが好みなのだが、保存には適していても利用には適していない(面倒)
  • FIDO対応なOpenSSHバージョンが登場したが、まだまだマジョリティではない

ということも補足的な理由です。

結論を先に

  • Yubikeyのセットアップの面倒さに加え、クライアント環境を構築しないといけないので、結局のところ秘密鍵の置き場をUSBセキュリティキーにできる、という以外はあまり効率的な印象はありませんでした。
  • Multi-factor Cryptographic Deviceを強制するような物理的な保護要件がなければ、クライアント証明書をPIN保護してMulti-factor Cryptographic Softwareでもいいなぁ、というところです。
  • あまり流行らないうちにネイティブ対応のOpenSSHが普及しそう。Ubuntu 20.04LTSから使えるらしいので。

参考

細かいことはいろいろ書いてくださっているサイトが複数あるのでそちらを合わせて参照。 主に眺めたのは以下です。

設定

肝心なところだけ備忘で残しておきます。

YubiKey ManagerのPIV設定画面から"PIN Management"でPIN, PUK, Management Keyの登録を行う。

f:id:kthrtty:20210520213109p:plain
Yubikey4のPIV機能の設定画面
初期状態のYubikeyの場合は、Current値を入れる部分にデフォルト値を入れてくれるチェックボックスがあるので、それにチェックしておけば、好きなPIN, PUKを登録できる。 f:id:kthrtty:20210520213823p:plain Management Keyも同様。あとで自己署名証明書(と秘密鍵)を作る際に必要になるようなので、メモっておく(と参考リンク先にもある)。 f:id:kthrtty:20210520213859p:plain

Certificates設定画面から秘密鍵(pem)をセキュリティキーにインポート

"Import"ボタンを押してpemファイルを指定すればよし。秘密鍵自体の保護PINと、YubikeyのPINを聞かれる。 f:id:kthrtty:20210520214405p:plain Management Keyの値が聞かれなかったがとりあえずこれで良いみたい。参考リンクが記載された時よりもGUIアプリが進化して省力化できるようになっている模様。もしコマンドラインからインポートする場合は以下。

>"C:\Program Files\Yubico\Yubico PIV Tool\bin\yubico-piv-tool.exe" -a import-key -s 9a -i id_rsa -K PEM -k(ManagementKeyの値)

画面上はあまり変化はないものの、秘密鍵はとりあえず保存されている。 なおここでの秘密鍵ファイルは以下のフォーマットになっていればよいはず。

-----BEGIN RSA PRIVATE KEY-----
・・・(省略)
-----END RSA PRIVATE KEY-----

Certificates設定画面から自己署名証明書を登録する。

あとはキーペアとなる証明書も追加でインポートすればよい。 ただし、普通にSSH用のOpenSSH用の公開鍵をエクスポートしていると形式が以下のようになっているはず。

---- BEGIN SSH2 PUBLIC KEY ----
AAA...(省略)
---- END SSH2 PUBLIC KEY ----

ここから証明書を作成する場合、大抵の場合はハマるので、証明書作成に適したPEMに変換しておく。 いわゆるMIIで始まるやつです。

---- BEGIN RSA PUBLIC KEY ----
MII...(省略)
---- END RSA PUBLIC KEY ----

コマンドは以下。

$ openssl rsa -in id_rsa -pubout > id_rsa.pub.pem

あとはopensslでCertification requestつくって自己署名証明書をつくってもよいと思われるが、ここではyubicoのツールを使ってみることにする。(参考リンクの通り)

>"C:\Program Files\Yubico\Yubico PIV Tool\bin\yubico-piv-tool.exe" -a verify-pin -a selfsign-certificate -s 9a -S "/CN=*.localhost.localdomain/OU=test/O=localhost.localdomain/" -i id_rsa.pub.pem -o id_rsa.crt --valid-days=1100
Enter PIN:
Successfully verified PIN.
Successfully generated a new self signed certificate.

f:id:kthrtty:20210520223719p:plain (Issuerにもワイルドカードが入ってしまったが、、、とりあえずスルー)

GUIからのcrtのインポートがどうにも成功しないので、コマンドラインからインポート。

>"C:\Program Files\Yubico\Yubico PIV Tool\bin\yubico-piv-tool.exe" -a import-certificate -s 9a -i id_rsa.crt -K PEM -k(ManagementKeyの値)
Successfully imported a new certificate.

f:id:kthrtty:20210520224430p:plain インポート済みの秘密鍵のペアとなる公開鍵の自己署名証明書が無事に登録された。

実際にPuttyを使ってホストへログインしてみる。

WindowsならCertification APIに対応したPutty-CAC、OSXならbrew install openscしてスマートカード用のモジュールを取得してから接続。

改めて結論

  • Yubikeyのセットアップの手間だけでなく、結局はクライアント環境の構築が必要。結構面倒。
  • Multi-factor Cryptographic Deviceを強制するような物理的な保護要件がなければ、クライアント証明書をPIN保護してMulti-factor Cryptographic Softwareでもいいかもしれない。
  • あまり流行らないうちにネイティブ対応のOpenSSHが普及しそう。

GoToキャンペーンの4連休に乗じて昔の新婚旅行の記録を残す

GoToキャンペーンが物議を醸しているコロナ禍で、すっかり遠出大好き派の自分としてはウズウズして仕方がない。

本来の目的である旅行メモを記載するため、遠出ウズウズ感を満たすため、あるいは逆に増長させるため、長らく記録していなかった新婚旅行について思い出しながら残してみる。

GoToキャンペーンは海外関係ないけれど、いざ出かけるときに参考にしてもらえるとよい。

エグサマ

結婚したら自由に海外旅行もいけなくなるな、ということで11日間の新婚旅行に全行程を個人手配で色々詰め込んで、家人を連れまわしてヒンシュクをかったり満足してもらえたりした話。

  1. 成田 to ボストン(飛行機)
  2. ボストン to マイアミ(飛行機)
  3. マイアミ to ラパス(飛行機)
  4. ラパス to ウユニ(飛行機)
  5. ウユニ to ラパス(飛行機)
  6. ラパス to クスコ(飛行機)
  7. クスコ to マチュピチュ(列車)
  8. マチュピチュ to クスコ(列車)
  9. クスコ to リマ(飛行機)
  10. リマ to マイアミ(飛行機)
  11. マイアミ to セントマーチン(飛行機)
  12. セントマーチン to マイアミ(飛行機)
  13. マイアミ to ダラス(飛行機)
  14. ダラス to 成田(飛行機)

f:id:kthrtty:20200717230037p:plain
移動経路イメージ(www.jetlovers.comより)

f:id:kthrtty:20200717225539p:plain
搭乗した飛行機の記録(www.jetlovers.comより)

Day1 - 移動:成田 → ボストン

成田は遠すぎ。

1月のボストンは結構寒いので、トランジットの合間に港エリアを街歩きして、いきなり不機嫌な家人ともめた記憶と、証跡としての写真・動画が数点残っていた。

家人は前日にニューヨークから帰宅したところだったので、同じ立場だったらイラつくのは分かる。とにかくもう飛行機に乗りたくないとキレていた。

Day2 - 移動:ボストン → マイアミ → ラパス

本当はマイアミで時間をとってセブンマイルブリッジをドライブしてキーストーンまで行きたかったのだが、このプランはどうしても押し込むことができなかった。映画「トゥルーライズ」で爆破されたその橋、胸熱。

ja.wikipedia.org

アメリカを出国して向かったボリビアの都市ラパスのエル・アルト国際空港というと、標高約4000mに位置しており、到着した場所が既に富士山頂よりも標高高いという恐ろしいところである。着陸して暫く過ごすと高山病になってしまう人もいる。

機内は与圧して1気圧から下げているが、空港の標高のほうが気圧が低いので、差圧調整時に設定を変更しておかないと気圧下降で酸素マスクが飛び出てくるらしい。

en.wikipedia.org

南米旅行&新婚旅行で高山病というのはよくある失敗談。 自分が個人旅行でキリマンジャロに登っていた際の経験で、ダイアモックスという薬を処方してもらっており、適宜それを服用することで高山病の防止に努め、道中で高山病には悩まされなかった。

kthrtty.hatenadiary.org

家人は高山病の恐怖にキレていた。

Day3 - ラパス → ウユニ塩湖&塩のホテル

移動方法

飛行機

ウユニ国際空港へはラパスからアマゾナス航空で移動するのが最速。 標高は少し下がって約3800m。

en.wikipedia.orghttps://en.wikipedia.org/wiki/Uyuni_Airport

f:id:kthrtty:20200726033709p:plain
Amazonas航空

f:id:kthrtty:20200726033952p:plain
ウユニ空港

バス

海外バックパッカーの記録には、ラパスからバスでアンデス山脈を1日かけて越えるプランも出てくるが、かなり揺れるらしくバス酔いもひどい模様。

今回は即却下。いちおう新婚旅行なので。

現地の楽しみ方

現地ツアーとしては、

  • 他のバックパッカーと一緒に回る安めの現地ツアー
  • 少人数で専属ガイドをつけて回る高めの現地ツアー

があったが、当然「少人数」を日本で予約しておいた。いちおう新婚旅行なので。

f:id:kthrtty:20200726034226p:plain
ウユニには列車の墓場的なものがあるらしい

ウユニ塩湖の鏡張り絶景

コンディションが良かったということに尽きるのだが、ウユニ塩湖は本当に信じられないほどに反射していて、息をのむ光景だった。サングラス必須。

f:id:kthrtty:20200726034506p:plain
テンション上がる

f:id:kthrtty:20200726034649p:plain
撮影自体は地味

f:id:kthrtty:20200726034829p:plain
ランチこんな感じ

f:id:kthrtty:20200726042354p:plain
フラミンゴもいる

f:id:kthrtty:20200726035601p:plain
距離感が崩壊

f:id:kthrtty:20200726040246p:plain
雨期+夕暮れ+風なし

f:id:kthrtty:20200726035819p:plain
新婚旅行っぽい写真

f:id:kthrtty:20200726041848p:plain
半月にて星はこれでもかなり少なめらしい

f:id:kthrtty:20200726040929p:plain
ウユニは雷が有名

タイミングとしては1月2月の雨季かつ、夜も楽しむならば新月の日程がベスト。自分が行ったときは残念ながら半月だったので月明かりで星空は少々見えにくかった。

宿泊

宿泊は有名な塩のホテル「ルナ・サラダ」で。 www.booking.com

f:id:kthrtty:20200726042044p:plain
徒歩でホテルへ向かう

建物が塩で作られていて、実際しょっぱい。

f:id:kthrtty:20200726041426p:plain
ルナサラダは塩を積み上げてできている

f:id:kthrtty:20200726042927p:plain
ベッドは普通

f:id:kthrtty:20200726043155p:plain
いちいち塩っぽい

f:id:kthrtty:20200726092115p:plain
絢爛豪華ではないが食事もきちんと出て満足&快適

f:id:kthrtty:20200726041610p:plain
やや機嫌戻る

夜、宿泊者が温水タンクを使いすぎたためか、水シャワーしか出なくなったため家人にキレられるなど。

f:id:kthrtty:20200726045102p:plain
GORETEXを着てやや冷たい水でシャンプーする図

夜のウユニ塩湖を見に行くツアーに参加していたため、帰ってくるのが遅くなったのが原因。コンテンツを詰め込んでいる人は要注意。いちおう新婚旅行なので。

翌朝太陽が出れば、また温かいシャワーが出るようになる

Day4 - ウユニ塩湖 → ラパス → クスコー → マチュピチュ

日の出をウユニ塩湖で迎える。この日は少々曇っていて、イマイチ。 私たちは1泊2日の強行軍だったが、できればもう少し長く滞在してのんびり過ごすことをオススメしたい。

一度ラパスに戻ってから、ボリビアを出国し、ペルーの都市クスコーへのフライト。標高4000mから3400mへと少し下がる。

標高が高いと酸素が薄くエンジンの燃焼効率が悪く、気圧が低いため揚力の確保にも不都合、離陸時には燃料重量をなるべく減らして長距離の滑走路で十分に加速する必要がある、という離陸時ウンチクがあるらしい。

f:id:kthrtty:20200726045543p:plain
クスコー市街地を散策@アルマス広場

マチュピチュの更に奥にあるワイナピチュに登るためには、事前に予約・抽選が必要とのことだったので、日本から現地代理店を探して列車とワイナピチュの入山券をゲットしておいた。あいにくクスコーからの列車の接続が悪く、車で2時間ほどかかる「オリャンタイタンボ」駅まで事前チャーターしておいたタクシー移動で時短移動。遅れたらマチュピチュへ辿り着けない。

f:id:kthrtty:20200726050042p:plain
ペルー鉄道オリャンタイタンボ駅

f:id:kthrtty:20200726050332p:plain
アンデス山脈の合間に駅がある

f:id:kthrtty:20200726092926p:plain
ビスタドーム号は見やすいようにガラス張りになっている

f:id:kthrtty:20200726091334p:plain
雨期ということもあり霧に包まれるマチュピチュ

Untitled

ここまでかなりのペースで飛ばしてきたので、マチュピチュ村でのホテルはやや高級なところにした。いちおう新婚旅行なので。

f:id:kthrtty:20200726094211p:plain
ホテルが豪華で掛け布団に地上絵が書いてある

f:id:kthrtty:20200726094420p:plain
まさかマチュピチュでジャグジー付きのホテルに泊まるとは

f:id:kthrtty:20200726100018p:plain
夜のマチュピチュ村を散策

f:id:kthrtty:20200726094543p:plain
村のおしゃれなレストランのおかげで家人がややご機嫌に

Day5 - マチュピチュ観光 → クスコー

雨期だから当たり前の光景なのかもしれないが、寝ているホテル削られて死ぬのか、と若干不安になりながら目が覚めた。

Untitled

マチュピチュへの移動

  • バス(15分)/往復2-3000円
  • 徒歩(2-3時間)/0円

マチュピチュ村からマチュピチュまでは早朝5時台からバスが出ていたはず。が、徒歩なんてしない、このあとの行程もあるので疲れは最小に。

f:id:kthrtty:20200726110640p:plain
左側に見えるジグザグルートがバス経路

この時は、つい先日に土砂崩れがあった影響で、経路が寸断されてしまっていたが、途中でバスを降りて徒歩で登り、寸断された道の先にあるバスに再度乗り換えて現地へ到着した。30分ぐらいかかったかな。

f:id:kthrtty:20200726101401p:plain
霧がかかるマチュピチュへ到着

この旅程では1日半しか滞在しないが、イレギュラーもあることを踏まえ、2日は滞在することをオススメしたい。

f:id:kthrtty:20200726105426p:plain
マチュピチュ遺跡自体の起伏も激しい

f:id:kthrtty:20200726105603p:plain
遺跡内にはリャマが放し飼いされている

Untitled

憧れのサンクチュアリロッジ

私達は残念ながら滞在かなわなかったが、日程に余裕がある人は是非サンクチュアリロッジを検討してもらいたい。

www.tripadvisor.jp

マチュピチュの横にホテルがあり、贅沢な非日常を過ごせると話題。友人が一人で3泊ほどとまって、部屋でマチュピチュの夕暮れを生演奏とフルーツとともに楽しむとかいう贅沢の限りを尽くした話を羨望の眼差しで聞いたものだ。

ワイナピチュだけだった入場規制がマチュピチュにも

2014年当時はマチュピチュは普通に入れた。そのときから「世界遺産の保護のため入場規制がかかるから、今のうちに行っておけ」というような話を聞いたが、実際に規制がかかりはじめたようで注意が必要。

www.travelvision.jp

ワイナピチュ入山券は現地の代理店に事前に依頼して入手しておいたが、当日に現地で、というのは時間制限もあり難しいと思われる。こちらも同様に注意されたし。

マチュピチュに来たらワイナピチュもぜひ

前述の注意事項に書いたが、せっかくマチュピチュに来たのだったらワイナピチュにも登っていただきたい。よく見る光景はワイナピチュから見た姿だというのがよく分かる。

f:id:kthrtty:20200726105805p:plain
マチュピチュ全景と後ろにそびえるワイナピチュ山

登坂になるため、体力は奪われる。崖に面した道を歩くため、まれに滑落して死人はでるそう。最近だと自撮りしようとした人が滑落したらしい。手すりもなく、わずか数十センチ横が絶壁という場所もあり、確かに危ないと感じる場所は多数あった。 www.bbc.com

f:id:kthrtty:20200726111519p:plain
ワイナピチュからのマチュピチュ全景

昼ごはんはどうすればよいの

入り口のところに大きなレストランがあるので、そこでお腹いっぱい食べれる。心配御無用。

帰りもペルー鉄道

新婚旅行ということで最高級ハイラム・ビンガム号に乗りたかったのだが、時間の都合でビスタドーム号で、オリャンタイタンボ駅に戻り、更に2時間バスでクスコーに戻る。

f:id:kthrtty:20200726112058p:plain
ビスタドーム号内でのエンタメ

ハイラム ビンガムで行く、マチュピチュ

Day6 - クスコー → リマ → マイアミ

ひたすら移動。リマではトランジットに時間があったので、ノープランながら入国して軽く散策してみる。

家人は海外は北米・欧州の綺麗な都市しか行ったことがないためか空港からダウンタウンへ向かう途中の過疎エリアのカオスさにショックをうけたようで旅行中最もテンション下がってしまった。大げさだなと思ったが、感じ方は人それぞれなので仕方がない。

そういえば、ダウンタウンの家電ショップでPS4が販売されていたが、このときまだ日本では販売前だったはず。日本は世界から取り残されてるな、と感じた。

ノープランなりにTripAcvisorで検索して、リマ一番の高級住宅街「ミラフローレス」を訪問し、ベイエリアのおしゃれな店でシーフードを堪能した。新婚旅行っぽかった。 en.wikipedia.org

f:id:kthrtty:20200726113912p:plain
シーフードが充実
f:id:kthrtty:20200726113530p:plain
南米ベイエリアから望む太平洋の夕暮れ

空港への帰りのタクシーをミラフローレス近くの高級ホテルのドアマンに声をかけて呼んでもらった際、「値段は高いがセーフタクシーを呼んだほうが良い」と言われたのが印象的。新婚旅行の数週間前、南米で新婚旅行中の夫婦が、現地タクシーの運転手に銃撃される事件が起きていたこともあり、ハッとさせられる瞬間でもあった。 www.nikkei.com

深夜過ぎの便でマイアミへ。

Day7 - マイアミ → セントマーチン

セントマーチンは、フランスとオランダが分割統治しており、島の南北で国が変わる。

宿泊した宿は、フランス側で「フランス料理が美味」ということで外からディナーを食べに来る人も多いと評判の宿にした。後述するマホビーチからは距離があるのだが、大変良い宿だった。 www.tripadvisor.jp

f:id:kthrtty:20200726114926p:plain
この行程始まって最初のフォアグラ

Day8 - セントマーチン

マホビーチとは

ある方面では世界で一番有名なビーチの一つである。つべこべ言わず写真を見ると「おっ、これ見たことある」となるだろう。

f:id:kthrtty:20200726115650p:plain
エアバスA340の4発機の着陸

f:id:kthrtty:20200726121602p:plain
USエアウェイズ

f:id:kthrtty:20200726122314p:plain
ビーチの人たちの2-30m上空を通過

f:id:kthrtty:20200726122114p:plain
手が届きそう

セントマーチンは飛行機とビーチとフランス料理目的で1週間ぐらい滞在してもいいなと思った。ここでの2泊3日が一番新婚旅行らしい過ごし方だった。

Day9 - セントマーチン → マイアミ

マホビーチでの動画はこちら。

MVI_1794

KLMオランダ航空が運用していたB747の4発機がセントマーチンを離発着できる最大の機材だったので、本当はその着陸を撮りたかったのだが、カメラの都合で撮れたのは離陸のときだけ。

Untitled

マホビーチで最終滞在日も飛行機の離着陸を眺めつつ、あとはテキパキと帰路につく。

マイアミで一度入国して夜、今回の旅で一番高級なホテルに泊まり、ダウンタウンでディナーを食べて旅程のハイライトをするなど。 www.hilton.com

Day10,11 - マイアミ → ダラス → 成田

ひたすら帰国の途。ダラス・フォートワースを経由して、無事に成田に到着。

11日間の新婚旅行を終えて、翌日から勤務にもどったのでした。詰め込んだが、満足いく新婚旅行だった。

行程はTripItで管理していたので今回あとから振り返りしやすかった。出張行程を上司に開示するのにも楽なので、入社当時に海外出張行きまくり諸先輩方から教えてもらってから使っている。 www.tripit.com

以下、TripItで管理していた全行程。

f:id:kthrtty:20200726124835p:plain
全行程のTripIt明細

以上、長文にて失礼しました。

個人手配のトータル旅程費用

[TBW] ニーズがあれば集計する。

NISTがSP800-63-3の改定に入る前にパブコメやるらしいからコメントを考えてみた

パブコメってどういうこと?

2017年に正式に改定され,「パスワードの定期変更」ネタで物議を呼んだご存知NIST SP800-63-3が,どうやら改定を検討しているらしく,そのインプットとしてのパブコメを募集するというアナウンスがありました.

csrc.nist.gov

ということでパブコメをしてみます.

パブコメなんて,「よく考えられていると思いました」とか意味不明な内容でも良いはずなので,とりあえず気に入らなかったり,分かりにくかったりした箇所があればどんどんコメントするのが良いですね!

そもそもNIST SP800-63って何でしたっけ?

2017-2018年のネタですからね,もう記憶喪失です.

オンラインにおけるDigital Identity Framework(昔は登録と認証メインでしたが,最近はフェデレーションも含まれるようになってまさにフレームワーク)と呼ぶにふさわしいガイドラインです.

一応,NISTのガイドラインは米国政府機関向けであり,その他の人にとって何ら参照しなければならないものではないですが,クレジットカードにおけるPCI-DSSや,FISC安対なんかでも(追いつき不足による内容不整合はおいておいて)参照されており,非常に由緒ただしいものと考えられているはず.

2006年4月に発行されたNIST SP800-63は,IPAサイトで翻訳版が公開されています.https://www.ipa.go.jp/security/publications/nist/

最新のSP800-63-3は,githubで差分管理することになり以下で公開されています. csrc.nist.gov

それを,OpenID Foundation JAPAN有志メンバで翻訳した版が以下のgithubで管理されています.私は63Bパートを担当しました. openid-foundation-japan.github.io

更に,解説スライドは以下に配置してあります.InternetWeek2018と,翌年の東北大学で実施したInternetWeek Showcaseで使いました. speakerdeck.com

パブコメの内容メモ

自分も記憶喪失なので,思い出しながら何をコメントしようか考えながら書いてみよう.とりあえず自分で担当していた63Bで気になったところが何だったっけかな?と.

"Device"って書いてあるのにハードウェアじゃないかもしれない件

63Bに限らず,Authenticatorがハードウェア(Hardware root of trust)かどうかはセキュリティの面で重要な意味を持ちます.

5.1.4 Single-Factor OTP Devices(および5.1.5 Multi-Factor OTP Devices)において,以下のように書いてあります.

This category includes hardware devices and software-based OTP generators installed on devices such as mobile phones. 

この2つのAuthenticator種別についてだけ,スマホにインストールされたOTPソフトウェアが混同されていて分かりにくかった記憶があります.

実際,

という具合に概ねデバイスとソフトウェアを明らかに「HWかどうか」で分割しているにも関わらず,OTPデバイスだけは「HWだったりSWだったり」という具合です.

そこで「tangibleかそうでないかははっきりと分離したほうが良いのでは」というコメントをしてみることにします.

一応紛らわしい例を想像してからコメントを書いてみなければ.

  • iPhoneに入っているGoogle Authenticator
    • iPhoneはSomething you haveである.
    • Google AuthenticatorはSoftwareである.
    • iPhone自体のFaceIDやTouchID,Passcodeは「かかっているかもしれないし,かかっていないかもしれないし,Verifier側でそれを知るすべはないので」1要素目の認証要素としてはカウントしない(という63Bの整理)

これはDevice(Hardware)なのかSoftwareなのかと言われるとやはり曖昧ですね.Hardwareでいいの?違うの?どっちなの,と.

VerifierとのAuthenticated protected channelを張っている端末上に存在していなければDevice,同じならSoftwareとかだったら複雑だなと想像しつつ,何かしら理由をつけて分離するか,補足しててほしいと思いました.

多要素認証と多段階認証

某セブンペイ事件の際に,世の中でだいぶ取り沙汰された2段階認証ですが,2要素認証との違いについて明記してほしいですよね.

PCI-DSSにおいては,Multi-Step AuthenticationとMulti-Factor Authenticationについて内部的にモメたようで,一定の見解を出していました.

blog.pcisecuritystandards.org

更に参照しているこれとか. https://www.pcisecuritystandards.org/pdfs/Multi-Factor-Authentication-Guidance-v1.pdf

要するに

  • アクセス許可する前に全ての要素の認証が成功しなければならない.
  • 全ての要素が提示される前に認証の成否が分かってはならない.

特に2項目は簡単に言ってくれるな,という感じです. サービスによってはユーザビリティ向上のために,メールアドレス入れた段階でユーザの存在有無を確定されるようなこともあるでしょう.(e.g, Google Account Chooser,Azure Active Directory)

OTPハードウェアのように,それ自身が既にユーザ(Subscriber)のIdentityにバインドされているならば良いですが,Push通知やSMS,電話(が適切なAuthenticatorであるかは棚上げして)など,サーバサイドでユーザのIdentityが特定されなければ開始できないことはままあります.

2. The authentication mechanisms are independent of one another, such that access to one factor does not grant access to any other factor, and the compromise of any one factor does not affect the integrity or confidentiality of any other factor.

というあたりも簡単に言ってくれるな,という感じです.

PCI-DSSのMulti-Factorがちょっと厳しすぎて世の中の相場観と乖離しているように思えるけれど,NISTとしてはこの辺について整理してみませんか?」という感じのコメントでしょうかね.

パスワードの定期変更の混乱に着地点を示してほしい

私はそこまでこれにご執心ではないのですが,皆さんイライラしているらしく盛り上がりまくった件です.

63Bでは定期変更をユーザに求めることを「すべきではない」こととしていますが,63Bが主なターゲットとして想定しているのは,政府機関が提供するデジタルサービスや職員向けの認証機能です.

ということで,PCI-DSSの「定期変更をユーザに求める」ことを強制する姿勢と不整合を発生させています.

ここはぜひ,

  • 政府機関職員(一般ユーザ)転じてコンシューマは63Bの定期変更を求めることはすべきではない
  • システム運用管理者(定期変更により弱いパスワードに至ることがないような強固な運用を期待できる人)には引き続き有効な側面が残る

など踏み込んで言及してもらえると世界平和につながると考えてコメントすることにします.

EmailはOOB Authenticatorには使えないが,他の目的を制限していない,という点がわかりにくい

5.1.3.1 アウトオブバンドAuthenticator

Voice-over-IP(VoIP)やEmailなど,特定デバイスの所持証明を行わない方式は,アウトオブバンドAuthenticationを行う目的では利用しないものとする(SHALL).

他の用途として,アカウントリカバリに利用している事例を考えてみます.

Login HintとしてAccount Chooserに過去にログインした形跡が残っている(アカウント名が選択可能)な場合は,PW忘れをした場合のEmailによる確認コード受信&入力でアカウントリカバリが直ちに完了します.(ログイン形跡のある環境であり,リスクが少ないと判断されるため)

一方,完全に新規環境だと,Emailによる確認コード受信&入力後に,「追加情報」として過去PWや過去接続元IP,過去接続ブラウザなどログインに関連するトラッキング情報との突き合わせ要素を増やすように促され,そういった環境でアカウントリカバリを試行すると成功します.

Googleの例のように「他の目的でのEmail OOB活用」をしているわけですが,ほとんど「解釈」の話になっていて,わかりにくいですよね.「アカウントリカバリに利用してもよい(MAY)が,その場合はリスクベースで〜」とか補足があると良いなと思いました.

アカウントリカバリのためのAuthenticatorの再バインディングでのIdentity Proofingの方法が曖昧

6.1.2.3 紛失したAuthentication要素の置き換え

要するにPWを忘れてしまったときの再バインディングの話なのですが,根が深い.

記憶シークレットの紛失(すなわち忘れてしまうこと)の置き換えは,非常に一般的であるがゆえに解決が難しい問題である.追加の “バックアップ” 記憶シークレットは,結局忘れる可能性があるために緩和策にはならない.もしバイオメトリックがアカウントにバインドされていれば,バイオメトリックと関連する物理Authenticatorが新しい記憶シークレットを設定するために利用されるべきである(SHOULD).
アカウントにバイオメトリックがバインドされていない場合には,上記のre-proofingプロセスの代わりとして,CSPは,Subscriberの住所の一つに送信された確認コードとともに2つの物理Authenticatorを用いたAuthenticationを行い新しい記憶シークレットをバインドしてもよい(MAY).

唐突に登場している物理Authenticator(phisical authenticators)に対して一切の説明がないのでわかりにくいです.また今までは複数の同じ認証要素のAuthenticatorを利用しても,多要素認証にはならない点に言及されているにも関わらず,唐突に要素数ではなく物理Authenticator数に言及しています.63Aにも2つの物理Authenticatorに関する記述はなく,唐突に感じられるため,具体例を補足してほしいというコメントをすることにしました.

加えて,前述の通りですが「Google/MSはPW忘れに伴うAAL2のRebindingをEmailを使って実施しているけれど,これはGoogle/MSがSelf-Evident/ClaimedなIAL1なサービスだからだと理解して良いよね?」というコメントは追加しておこうと思います.

他にも言いたいことはあるが・・・

これは米国政府機関向けという但し書きを考慮するとぐうの音もでない件がいくつか.まだコメントすることまでは考えていません.

IAL>AALのときパーソナルデータを開示するな

6.1.1 Enrollment時のバインディング

CSPはAAL1のAuthenticatorをIAL2のアイデンティティにバインドしてもよい(MAY)が,もしSubscriberがAAL1でAuthenticateされた場合には,CSPは,それが仮にself-assertedであっても,個人情報をSubscriberに対して開示しない(SHALL NOT).

冷静に考えて,AALとIALの関係に基づいて開示範囲をスコーピングするの実装が面倒ですよね.ましてOIDCのscopeなんかと組み合わせた日には,大して面白みもないのに複雑なテストパターンが出来上がりそうで辟易します.

Authentication Context Referenceをうまくアクセス可能エリアに結びつけて,ユーザ情報の確認・変更画面へのアクセスを禁止する程度ならOKでしょうが,至るところで表示するために参照しうるものを正しく一律に制限するだなんて,正気の沙汰とは思えません.UserInfoとか一箇所だから制御する気になるだけですよ.

パブコメ英文

英語は、結局自分でまずは書いてみて、そのあとDeepLとGoogleに和訳してもらって意味が崩壊していないか眺めた程度でそれっぽく.意味反転してなければよいですね。

I think there are several uncertain/ambiguous expression in 63B, so I comment as follows:


1) Some authenticators named "Device" are ambiguous as to whether they are hardware or software
Single-Factor OTP Authenticators are mentioned that "This category includes hardware devices and software-based OTP generators installed on devices such as mobile phones." in Sec 5.1.4.
However other "hardware" authenticators(e.g., "Single-Factor Cryptographic Device Authenticators") are all named "Device" and explicitly distinct from "software" authenticators(e.g., "Single-Factor Cryptographic Software Authenticators")

Thus, I think that "Single-Factor OTP Authenticators" should be divided into "Device"(hardware) and "Software".


2) NIST should explain the difference between "Multi-Factor" and "Multi-Step" authentication.
PCI-DSS introduces "Multi-Step" authentication in the following documents.

https://blog.pcisecuritystandards.org/faq-is-two-step-authentication-acceptable-for-pci-dss-requirement-8.3
https://www.pcisecuritystandards.org/pdfs/Multi-Factor-Authentication-Guidance-v1.pdf

NIST has no responsibility to mention about a specific industry, however the insight should be provided by NIST.
Technically speaking, some kinds of action need an email ID prior to inputting the 2nd factor value.
In fact, GAFA's authentication flow is not multi-factor but multi-step in the view of PCI and ambiguous.

Because of the above reason, I think that 63B should mention multi-step authentication.


3) Double standard about "memorized secret periodically changed" in 5.1.1.2 Memorized Secret Verifiers should be mentioned.
63B stated that 'Verifiers SHOULD NOT require memorized secrets to be changed arbitrarily (e.g., periodically)', then this policy transition caused lots of discussion.
NIST's new policy is acceptable especially for Web sites(e.g,. EC), however it conflicts with PCI-DSS's policy.

I propose that 63B show the difference between end-users and administrators/operators.
To show my intention, please see the following example.
- For end-users including US government staff, 63B's approach works well.
- For administrators/operators, traditional periodical password renewal policy may also work well because such persons can renew their password complicated enough according to the policy.


4) Use case of some OOB methods which do not prove possession should be mentioned.
63B says that "Methods that do not prove possession of a specific device, such as voice-over-IP (VOIP) or email, SHALL NOT be used for out-of-band authentication." in Section 5.1.3.1.
Actually, Google and Microsoft don't use such purpose, but use email OOB as a method of end-user account recovery flow with additional countermeasures.
For example, in Google's flow, if google account chooser memorize "logged-in" history, account recovery successfully finished right away, and if not, additional information is required.

I recognize that such an account recovery flow is one of the implementations of "Replacement of a Lost Authentication Factor".

If my understanding is correct, I offer to add the following example:
 "Such a OOB method MAY be used for the purpose of account recovery, and then the verifier should verify other additional attributes implicitly to confirm whether user access from customary environment or not. "



5) How csp does Identity Proofing to rebind authenticators for account recovery is ambiguous.
63B says that "As an alternative to the above re-proofing process when there is no biometric bound to the account, the CSP MAY bind a new memorized secret with authentication using two physical authenticators, along with a confirmation code that has been sent to one of the subscriber's addresses of record." in Sec6.1.2.3.

The word "physical authenticators" is first occurrence in 63-3 publication and 63A doesn't mention "physical authenticator".
AAL doesn't mention the number of physical authenticators, but multi-factor.
Please consider adding concrete examples to clear up any ambiguity I mentioned at the above.

Related to 6, my understanding is that Google and Microsoft using email(login ID) for account recovery don't comply with rebinding using two "physical" authenticators and that is because IAL is 1 (Self- Evident).
Because some people might have questions about it, please consider mentioning this point.

おしまいです。

U2F/FIDO2/WebAuthnのセキュリティキーのファームウェアをnRF52840-MDM USB Dongleに焼いてオレオレセキュリティキーを作って遊ぶ

これはなに

以下をします。

  • OpenSKというRustで書かれたFIDO2/WebAuthnのGithubプロジェクトがあります。
  • nRF52840というWiFi/Bluetooth/NFCに対応したUSBメモリサイズのお手軽マイコン開発キットがあります。
  • DFUモードでUSB経由でOpenSKを書き込むことで、お手製FIDO2/WebAuthn USBドングルが出来上がります。
  • システムを破壊してしまいDFUで書き込みができなくなったら、OpenOCD + Buspirateという安価な組み合わせでJTAG経由でnRF52840にメモリマップされる不揮発に直接書き込むことで復活させます。(未記載)

なぜそれをするの

  • 私が、10年ほどの間、Digital Identity(SAML/OpenID2.0/OAuth1.0a/OAuth2/OIDC/NIST SP800-63-Bなどなど)に関わる仕事をしていたのでそういうのが好きだからです。
  • 最近は、自動車セキュリティもとい、IoT/Hardware/Connected Serviceにまたがるセキュリティを仕事にしているので好きだからです。

OpenSK

https://github.com/google/OpenSK

Rustで書かれているらしいです。現時点で私はさっぱりRustは知りませんが、難しいと後輩が言っていました。もともと制的型付け言語が好きで、動的な型の言語は不安になるタイプなので、相性は良さそうな気がします。

nRF52840

あまり詳しくは知りませんが、要するに安いマイコンです。 Arduino界隈ではSTM32が激安で流行っていますが、そういうものの1種と考えておけばよいでしょう。

f:id:kthrtty:20200608000720p:plain

どこで購入するの

DigiKey、マルツオンライン、Mouser、Amazon、AliExpressあたりで購入できます。 基本的には国内在庫はなく海外発送となるため、2週間程度の時間がかかるでしょう。

私は"makerdiary"から発売されている"nRF52840-MDK USB Dongle"を買いました。送料込みで2000円ぐらいだったと記憶しています。

皆様は、公式でリストされている以下を買うのが良いと思います。

一応私が購入したnRF52840-MDKも動作実績あり、ということのようですが、末尾で記載している不具合が機種依存という可能性も捨てきれません。

OpenSKを導入する

Rustの環境を導入する

Ubuntuなのでapt search rustして適当なパッケージ入れる、だとうまくいかなそうなので素直に公式のInstructionに従います。

rustupを入れるそうです。 https://qiita.com/yoshiyasu1111/items/0c3d08658560d4b91431 を参考にしました。ありがとうございました。

一般ユーザの環境にランタイムを導入できるようなので、一般ユーザで作業します。

kthrtty@ubuntu:~/$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

It will add the cargo, rustc, rustup and other commands to
Cargo's bin directory, located at:

  /home/kthrtty/.cargo/bin

This can be modified with the CARGO_HOME environment variable.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:

  /home/kthrtty/.rustup

This can be modified with the RUSTUP_HOME environment variable.

This path will then be added to your PATH environment variable by
modifying the profile file located at:

  /home/kthrtty/.profile

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>
・・・
  stable installed - rustc 1.44.0 (49cae5576 2020-06-01)


Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.

To configure your current shell run source $HOME/.cargo/env

導入されたようです。

OpenSKのsetupスクリプトを動かして必要なツールチェインを導入する

叩いて待つべし。

スクリプトの実行中にバージョン依存関係のエラーとか出たりしますが、ログを見ると自動的にリカバってくれているようで、最終的には問題なく終了したようです。

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ ./setup.sh 
Submodule 'third_party/libtock-rs' (https://github.com/tock/libtock-rs) registered for path 'third_party/libtock-rs'
Submodule 'third_party/tock' (https://github.com/tock/tock) registered for path 'third_party/tock'
Cloning into '/home/kthrtty/OpenSK/github/OpenSK/third_party/libtock-rs'...
Cloning into '/home/kthrtty/OpenSK/github/OpenSK/third_party/tock'...
Submodule path 'third_party/libtock-rs': checked out 'ab2c945184b98ecae3e70ac678e9f5231deef73b'
Submodule path 'third_party/tock': checked out 'f1f9d717a4d9b5ae934a3b50ce6a18e6dff3e7c7'
[-] Copying additional boards to Tock... DONE.
[-] Applying patch "01-persistent-storage.patch"... DONE.
[-] Applying patch "02-usb.patch"... DONE.
[-] Applying patch "03-usb-debugging.patch"... DONE.
[-] Applying patch "04-additional-boards.patch"... DONE.
[-] Applying patch "01-linked_list_allocator.patch"... DONE.
[-] Applying patch "02-panic_console.patch"... DONE.
[-] Applying patch "03-timer.patch"... DONE.
[-] Applying patch "04-public_syscalls.patch"... DONE.
[-] Applying patch "05-bigger_heap.patch"... DONE.
[-] Applying patch "06-no_spin_allocator.patch"... DONE.
[-] Applying patch "07-debug_allocations.patch"... DONE.
Can't load /home/kthrtty/.rnd into RNG
139707493216704:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/kthrtty/.rnd
Signature ok
subject=CN = Google OpenSK CA
Getting Private key
Can't load /home/kthrtty/.rnd into RNG
140406427599296:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/kthrtty/.rnd
Signature ok
subject=CN = Google OpenSK Hacker Edition
Getting CA Private Key
info: syncing channel updates for 'nightly-2020-02-03-x86_64-unknown-linux-gnu'
info: latest update on 2020-02-03, rust version 1.42.0-nightly (f43c34a13 2020-02-02)
・・・
  Installing /home/kthrtty/.cargo/bin/elf2tab
   Installed package `elf2tab v0.5.0` (executable `elf2tab`)

FIDO2/WebAuthnのAttestation用の鍵が自動生成されているようですが、とりあえず動作確認したいだけなので、差し替えることもなくそのまま先へ進みます

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ ls -l ./crypto_data/
合計 28
-rw------- 1 kthrtty kthrtty 302  6月  7 17:46 opensk.key
-rw-r--r-- 1 kthrtty kthrtty 363  6月  7 17:46 opensk_ca.csr
-rw------- 1 kthrtty kthrtty 302  6月  7 17:46 opensk_ca.key
-rw-r--r-- 1 kthrtty kthrtty 489  6月  7 17:46 opensk_ca.pem
-rw-r--r-- 1 kthrtty kthrtty  41  6月  7 17:46 opensk_ca.srl
-rw-r--r-- 1 kthrtty kthrtty 379  6月  7 17:46 opensk_cert.csr
-rw-r--r-- 1 kthrtty kthrtty 489  6月  7 17:46 opensk_cert.pem

DFUモードで起動してUSB経由で焼く

都合の良いことにnRF52840-MDK USB Dongleは出荷時にDFUモードに対応しているBootloaderを焼いた状態で出荷してくれているようです。

Bootloaderに関する記述は以下を参考に。 https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/opensk/getting-started/ https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/programming/

公式を見ると、Advanced Installationに"Nordic DFU"があるじゃないですか。 https://github.com/google/OpenSK/blob/master/docs/install.md#advanced-installation

./deploy.pyを眺めると以下のような記述があります。 なるほどnrfutilというのが必要となるようです。

        return subprocess.run(
            [
                "nrfutil", "dfu", "usb-serial",
                "--package={}".format(dfu_pkg_file),
                "--serial-number={}".format(serial_number[0])
            ],
            check=False,
            timeout=None,
        ).returncode

ということで、次は"nRF Util"をインストールします。

nRF Utilのインストール

詳細は以下に書いてあります。

https://github.com/NordicSemiconductor/pc-nrfutil

環境次第ですが、Python3で入れましょう。 (公式でOpenSK自体がPython3依存っぽい感じで書いてあったので。)

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ pip3 install nrfutil

※エラー出たら--user指定で解決するかもしれない。

DFUモードで認識させる

各種ドキュメントを読むと、出荷時のBootloaderはDFUボタンを押しながら給電(USBポートに挿す)で、DFUモードとして認識されるようです。

lsusbの認識の差で把握できますね。

"Nordic Semiconductor ASA"(VID:PID=1915:cafe)と認識されているのは通常状態。

root@ubuntu:~# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 006: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 004: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 021: ID 1915:cafe Nordic Semiconductor ASA 
Bus 003 Device 005: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 003 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

"Nordic Semiconductor ASA"と認識されなくなればDFUモード(VID:PID=239a:0029)。

root@ubuntu:~# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 006: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 004: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 022: ID 239a:0029  
Bus 003 Device 005: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 003 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

OpenSKをビルドする

リポジトリからソースコードをとってきます。

kthrtty@ubuntu:~/OpenSK/github$ git clone https://github.com/google/OpenSK.git
Cloning into 'OpenSK'...
remote: Enumerating objects: 62, done.
remote: Counting objects: 100% (62/62), done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 1323 (delta 31), reused 37 (delta 15), pack-reused 1261
Receiving objects: 100% (1323/1323), 3.55 MiB | 276.00 KiB/s, done.
Resolving deltas: 100% (774/774), done.

ビルドします。

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ ./deploy.py --board=nrf52840_mdk_dfu --opensk --programmer=nordicdfu
・・・
info: Creating DFU package
Traceback (most recent call last):
  File "/home/kthrtty/.local/bin/nrfutil", line 6, in <module>
    from nordicsemi.__main__ import cli
  File "/home/kthrtty/.local/lib/python3.6/site-packages/nordicsemi/__main__.py", line 49, in <module>
    from nordicsemi.dfu.bl_dfu_sett import BLDFUSettings
  File "/home/kthrtty/.local/lib/python3.6/site-packages/nordicsemi/dfu/bl_dfu_sett.py", line 51, in <module>
    from nordicsemi.dfu.package import Package
  File "/home/kthrtty/.local/lib/python3.6/site-packages/nordicsemi/dfu/package.py", line 53, in <module>
    from nordicsemi.dfu.init_packet_pb import InitPacketPB, DFUType, CommandTypes, ValidationTypes, SigningTypes, HashTypes
  File "/home/kthrtty/.local/lib/python3.6/site-packages/nordicsemi/dfu/init_packet_pb.py", line 38, in <module>
    from . import dfu_cc_pb2 as pb
  File "/home/kthrtty/.local/lib/python3.6/site-packages/nordicsemi/dfu/dfu_cc_pb2.py", line 23, in <module>
    serialized_pb=_b('\n\x1bnordicsemi/dfu/dfu-cc.proto\x12\x03\x64\x66u\"6\n\x04Hash\x12 \n\thash_type\x18\x01 \x02(\x0e\x32\r.dfu.HashType\x12\x0c\n\x04hash\x18\x02 \x02(\x0c\"B\n\x0e\x42ootValidation\x12!\n\x04type\x18\x01 \x02(\x0e\x32\x13.dfu.ValidationType\x12\r\n\x05\x62ytes\x18\x02 \x02(\x0c\"\xf8\x01\n\x0bInitCommand\x12\x12\n\nfw_version\x18\x01 \x01(\r\x12\x12\n\nhw_version\x18\x02 \x01(\r\x12\x12\n\x06sd_req\x18\x03 \x03(\rB\x02\x10\x01\x12\x19\n\x04type\x18\x04 \x01(\x0e\x32\x0b.dfu.FwType\x12\x0f\n\x07sd_size\x18\x05 \x01(\r\x12\x0f\n\x07\x62l_size\x18\x06 \x01(\r\x12\x10\n\x08\x61pp_size\x18\x07 \x01(\r\x12\x17\n\x04hash\x18\x08 \x01(\x0b\x32\t.dfu.Hash\x12\x17\n\x08is_debug\x18\t \x01(\x08:\x05\x66\x61lse\x12,\n\x0f\x62oot_validation\x18\n \x03(\x0b\x32\x13.dfu.BootValidation\"\x1f\n\x0cResetCommand\x12\x0f\n\x07timeout\x18\x01 \x02(\r\"i\n\x07\x43ommand\x12\x1c\n\x07op_code\x18\x01 \x01(\x0e\x32\x0b.dfu.OpCode\x12\x1e\n\x04init\x18\x02 \x01(\x0b\x32\x10.dfu.InitCommand\x12 \n\x05reset\x18\x03 \x01(\x0b\x32\x11.dfu.ResetCommand\"m\n\rSignedCommand\x12\x1d\n\x07\x63ommand\x18\x01 \x02(\x0b\x32\x0c.dfu.Command\x12*\n\x0esignature_type\x18\x02 \x02(\x0e\x32\x12.dfu.SignatureType\x12\x11\n\tsignature\x18\x03 \x02(\x0c\"S\n\x06Packet\x12\x1d\n\x07\x63ommand\x18\x01 \x01(\x0b\x32\x0c.dfu.Command\x12*\n\x0esigned_command\x18\x02 \x01(\x0b\x32\x12.dfu.SignedCommand*\x1d\n\x06OpCode\x12\t\n\x05RESET\x10\x00\x12\x08\n\x04INIT\x10\x01*n\n\x06\x46wType\x12\x0f\n\x0b\x41PPLICATION\x10\x00\x12\x0e\n\nSOFTDEVICE\x10\x01\x12\x0e\n\nBOOTLOADER\x10\x02\x12\x19\n\x15SOFTDEVICE_BOOTLOADER\x10\x03\x12\x18\n\x14\x45XTERNAL_APPLICATION\x10\x04*D\n\x08HashType\x12\x0b\n\x07NO_HASH\x10\x00\x12\x07\n\x03\x43RC\x10\x01\x12\n\n\x06SHA128\x10\x02\x12\n\n\x06SHA256\x10\x03\x12\n\n\x06SHA512\x10\x04*t\n\x0eValidationType\x12\x11\n\rNO_VALIDATION\x10\x00\x12\x1a\n\x16VALIDATE_GENERATED_CRC\x10\x01\x12\x13\n\x0fVALIDATE_SHA256\x10\x02\x12\x1e\n\x1aVALIDATE_ECDSA_P256_SHA256\x10\x03*3\n\rSignatureType\x12\x15\n\x11\x45\x43\x44SA_P256_SHA256\x10\x00\x12\x0b\n\x07\x45\x44\x32\x35\x35\x31\x39\x10\x01')
TypeError: __new__() got an unexpected keyword argument 'serialized_options'
fatal: Failed to execute nrfutil: Command '['nrfutil', 'pkg', 'generate', '--hw-version=52', '--sd-req=0', '--application-version=1', '--application=target/nrf52840_mdk_dfu_merged.hex', 'target/nrf52840_mdk_dfu_dfu.zip']' returned non-zero exit status 1.

エラーで終了します。'serialized_options'というキーワードでprotobufを更新すればエラーが消える、という話があったので、更新します。

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ pip3 install -U protobuf --user

すると、どうやら通るようになります。

kthrtty@ubuntu:~/OpenSK/github/OpenSK$ ./deploy.py --board=nrf52840_mdk_dfu --opensk --programmer=nordicdfu
info: Updating rust toolchain to nightly-2020-02-03
info: syncing channel updates for 'nightly-2020-02-03-x86_64-unknown-linux-gnu'
info: checking for self-updates
info: component 'rust-std' for target 'thumbv7em-none-eabi' is up to date
info: Rust toolchain up-to-date
info: Building Tock OS for board nrf52840_mdk_dfu
    Finished release [optimized + debuginfo] target(s) in 0.03s
info: Building OpenSK application
    Finished release [optimized] target(s) in 0.05s
info: Generating Tock TAB file for application/example ctap2
info: Generating all-merged HEX file: target/nrf52840_mdk_dfu_merged.hex
info: Creating DFU package
info: Please insert the dongle and switch it to DFU mode by keeping the button pressed while inserting...
info: Press [ENTER] when ready.
fatal: Couldn't find any DFU device on your system.

エンターを押して、DFUモード経由で書き込みます。 ・・・と思っていたら、シリアルデバイスとして接続可能なDFUモードとして認識していないっぽいエラーメッセージが。

dmesgする。

[28158.127850] sd 33:0:0:0: [sdb] Attached SCSI removable disk
[28158.637656] usb 3-1.2: USB disconnect, device number 32
[28161.680491] usb 3-1.2: new full-speed USB device number 33 using xhci_hcd
[28161.978509] usb 3-1.2: New USB device found, idVendor=239a, idProduct=0029, bcdDevice= 1.00
[28161.978513] usb 3-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[28161.978516] usb 3-1.2: Product: nRF52840 MDK USB Dongle
[28161.978518] usb 3-1.2: Manufacturer: MakerDiary
[28161.978520] usb 3-1.2: SerialNumber: A6309C8E5741C991
[28161.986446] cdc_acm 3-1.2:1.0: ttyACM0: USB ACM device
[28161.987890] usb-storage 3-1.2:1.2: USB Mass Storage device detected
[28161.988488] scsi host33: usb-storage 3-1.2:1.2
[28162.995135] scsi host33: scsi scan: INQUIRY result too short (5), using 36
[28162.995140] scsi 33:0:0:0: Direct-Access     Adafruit nRF UF2          1.0  PQ: 0 ANSI: 2
[28162.995979] sd 33:0:0:0: Attached scsi generic sg2 type 0
[28162.998225] sd 33:0:0:0: [sdb] 8000 512-byte logical blocks: (4.10 MB/3.91 MiB)
[28162.999342] sd 33:0:0:0: [sdb] Write Protect is off
[28162.999344] sd 33:0:0:0: [sdb] Mode Sense: 03 00 00 00
[28163.000510] sd 33:0:0:0: [sdb] No Caching mode page found
[28163.000513] sd 33:0:0:0: [sdb] Assuming drive cache: write through
[28163.020195]  sdb:
[28163.027262] sd 33:0:0:0: [sdb] Attached SCSI removable disk

なるほど、すでにBootloaderがUF2として出荷されており、ボタンを押しながら認識させるとマスストレージデバイスとして認識されているということですね。

https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/programming/#dfu-via-uf2-bootloader

ファームウェアをUF2 Bootloaderの機能で書き込む

先ほど./deployを動かしていれば、target/nrf52840_mdk_dfu_merged.hexができているので、ここから変換すれば良さそうです。

https://github.com/makerdiary/nrf52840-mdk-usb-dongle/tree/master/tools

git clone https://github.com/makerdiary/nrf52840-mdk-usb-dongle

して、uf2convコマンドでhexファイルからuf2ファームウェアイメージを生成します。

kthrtty@ubuntu:~/OpenSK/github/nrf52840-mdk-usb-dongle/tools$ python3 uf2conv.py ../../OpenSK/target/nrf52840_mdk_dfu_merged.hex -c -f 0xADA52840
Converting to uf2, output size: 781312, start address: 0x1000
Wrote 781312 bytes to flash.uf2.

すると、

kthrtty@ubuntu:~/OpenSK/github/nrf52840-mdk-usb-dongle/tools$ ls
flash.uf2  mergehex  uf2conv.py

無事にflash.uf2が作成されたようです。

あとはフラッシュメモリとして認識されたドングルに書き込めば良いですね。

root@ubuntu:/mnt# mkdir mass_storage
root@ubuntu:/mnt# mount -t vfat /dev/sdb /mnt/mass_storage/
root@ubuntu:/mnt# cp /home/kthrtty/OpenSK/github/nrf52840-mdk-usb-dongle/tools/flash.uf2 /mnt/mass_storage/

書き込みが終わるとUSB認識がどう変わりますかね?

root@ubuntu:/mnt# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 006: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 004: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 037: ID 1915:521f Nordic Semiconductor ASA 
Bus 003 Device 005: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 003 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
root@ubuntu:/mnt# dmesg
[29355.553043] usb 3-1.2: new full-speed USB device number 37 using xhci_hcd
[29355.851469] usb 3-1.2: New USB device found, idVendor=1915, idProduct=521f, bcdDevice= 0.01
[29355.851472] usb 3-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[29355.851473] usb 3-1.2: Product: OpenSK
[29355.851475] usb 3-1.2: Manufacturer: Nordic Semiconductor ASA
[29355.851476] usb 3-1.2: SerialNumber: v0.1
[29355.858547] hid-generic 0003:1915:521F.0004: hiddev0,hidraw1: USB HID v1.10 Device [Nordic Semiconductor ASA OpenSK] on usb-0000:03:00.0-1.2/input0

USBのVenderIDが1915、ProductIDが521fになっており、Productが"OpenSK"として表示されるようになりました。

緑色の電源LEDが点灯、動作LEDが白点滅となっています。

動作確認をする

Windowsで認識させる

Windowsに認識させてみると、"HID-compliant fido"と認識されるようになりました。これは、いけそうです。

f:id:kthrtty:20200607235646p:plain

Googleのセキュリティキーとして追加しようとすると"User Intent"の確認から先に進めない

挿入が検出されているようなので追加できるな、、、と思ったのですが、いわゆるユーザが認証行為を行うことを確認する"User intent"の確認において、ドングルについているボタン押下が効きません。

なんということでしょう・・・

f:id:kthrtty:20200607235732p:plain

ここから先に進みません。

Githubのセキュリティキーとして追加できるが認証時に"User Intent"の確認から先に進めない

Githubの設定画面ではIntentまでは確認されず、セキュリティキーを追加することができました。が、いざログインしようとすると同じように"User intent"の確認で先に進みません。

なんということでしょう・・・

TODO

とりあえずGithubにissueを上げて本日はおしまいです。

  • ボタンを押下しても"User intent"を押したことにならない問題を解決する。
  • (今回はUF2 bootloaderのおかげで不要だったが) OpenOCD + Buspirateの組み合わせでJTAG経由でBootloaderとFirmwareを焼いてみるテストをする。
  • JTAG経由でメモリマップIOを抜いてきて、生成された鍵を抽出したり差し替えたりしてみる。
  • Rustを勉強して修正する。(ハードル高そう)

大画面テレビの録画用HDDの代わりにRasberry Pi4のUSB OTG機能を生かしてUSBtoLAN(NAS)なストレージゲートウェイを構成する

Qiitaは洗練された技術記事しか書いちゃいけない掟があるらしいのと、プライバシについて仕事でGDPR/CCPA/ISO29134系/個情報保護法などを斜め読みしている立場もあって、Qiitaからお引越しした。

目的

最近テレビを10年ぶりに新調した。 以下のようなNASと外付けHDDに記憶装置が分かれてしまっている状況をやめたい。 (記載のないTimeCupsuleも合わせるとリビングで6台のHDDが可動しており、寝ている時にファンが結構うるさく、昔から統合したい思いがあった。)

テレビ---(USB)-----外付けHDD
  |
  +---(LAN)-------L2SW---(LAN)---NAS(DLNA Server/DTCP-IP)
                    |
                  古いテレビ(DLNA Serverへのダビング・ムーブ対応)

ストレージゲートウェイを作って、テレビはUSB外付けHDDに録画しているが、実際にはNASに録画しているように変換してあげる。その結果、1台のRAID機の電源を落とせる。

テレビ---(USB)-----ストレージゲートウェイ(Pi4)
  |                 |
  +---(LAN)-------L2SW---(LAN)---NAS(DLNA Server/DTCP-IP/外付けHDD代替)
                    |
                  古いテレビ(DLNA Serverへのダビング・ムーブ対応)

やることは極めて単純。

  • 家のNASをRaspberry Pi4(以下Pi4)にマウントして録画領域のディスクイメージを作成する。
  • Pi4のUSB OTG機能を利用してPi4が外付けHDDとして振舞うようにする。
  • 家の大画面テレビにUSB接続して、外付けHDDに予約していると見せかけてNASに録画するためのコンバータとして仕上げる。

役に立つかもしれない人

こんな人向け。

  • テレビに外付けHDDを接続して録画をしている。
  • 家にNASも持っている。
  • 外付けHDDではなくNASにLAN経由で直接録画できたらよいのにとイライラしている。
  • LGテレビのようにDLNA/DTCP-IPサーバへの録画機能に対応していない機種を利用している。
  • わざわざレコーダーを買うほどの熱意も予算はない。
  • Pi4の使い所に困っている。
  • nasneなんて持ってない。
  • USB OTGをためしたい。

USB OTG(On-The-Go)とは

通常LinuxをインストールしたPi4はUSBホストとして動作する。そのため、キーボードやマウスを認識することができる。

OTG機能に対応しているハードウェアでは、LinuxだとGadgetFSという仕組みで様々なUSBデバイス(マウスとかキーボード)に化けることができる。AndroidでUSBにマウスを挿すとポインタが表示されるような機種があるが、これもOTG機能である(mini/microのID線をGNDショートしているOTGケーブルを使う)。Androidの場合は普段はPC側がUSBホストになるところが、AndroidがUSBホストになるパターン。

ホスト/ターゲットが切り替わる契機は、

  • 設定で切り替えるものや、
  • OTGケーブルを利用するものや、
  • バイスが起動して最初の数秒だけはターゲットとして動くものや、
  • (ちょっと違うけど)USB信号線のD+をGNDと短絡させる(XiaomiなどでQualcommバイスとして認識させる)手法や、

など様々。

仕事ではUSBホストがどのようなUSBクラス、ベンダID(VID)、プロダクトID(PID)に対応しているかをチェックしたり、なんちゃってUSBファジングを実施するために、USBターゲットとして振舞うfacedancer21umap2/Raspberry Pi Zero Wを使ったりしており、意外にも有用。

OTGに対応した最も有名なハードウェアはRaspberry Pi Zero。ただ、今回は録画に差し支えるような処理性能だと困るので最初からPi4を採用した。結果的にはほとんど性能関係なさそうなのでPi Zeroでも十分かもしれない。

Pi4環境情報

Raspbian(buster)が入っている手元環境を構成変更していく。

cat /etc/os-release 
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
(略)

uname -a
Linux raspberrypi 4.19.106-v7l+ #1297 SMP Tue Feb 25 13:19:54 GMT 2020 armv7l GNU/Linux

テスト用のイメージファイルを用意する

テスト用のイメージファイルを用意する。これは最終的に同じディレクトリにマウントポイントを上書きすることを意図している。

# テスト用にローカルに録画データを記録するイメージファイルを作成する
mkdir -p /root/NAS/ForRecording/
dd if=/dev/zero of=/root/NAS/ForRecording/recording.img bs=1M count=1024
# cifsマウント失敗時の判定のためにcifs上に作るファイルと同名かつ書き込みできないようにしておく
chmod ugo+w /root/NAS/ForRecording/recording.img

# クイックフォーマットする(フォーマットはテレビに応じて。我が家はNTFSをマウントできる。)
losetup /dev/loop0 /root/NAS/ForRecording/recording.img
mkfs -t ntfs -Q -L raspi4-storage-gw /dev/loop0

# おためしマウント
mount -t ntfs /dev/loop0 /mnt
mount | grep loop0
/dev/loop0 on /mnt type fuseblk (rw,relatime,user_id=0,group_id=0,allow_other,blksize=4096)

# イメージファイル開放
unmount /mnt
losetup -d /dev/loop0

Pi4のUSB type-C(給電ポート)でOTG機能を有効化

Very simple OTG on pi4にしたがって、以下修正。

# Enable USB OTG function
dtoverlay=dwc2

モジュールロードの方法はいくつかある。(モジュールによってはうまくいかないものもある。今回のg_mass_storageモジュールはまさにそれ。)

dwc2モジュールロード

方法1)/boot/cmdline.txtrootwaitのあとにmodules-load=dwc2のようにロードするモジュールを指定する。

方法2)/etc/modulesにモジュールロードのパラメータを指定して記載する。

echo "dwc2" | sudo tee -a /etc/modules

g_mass_storageモジュールロード

rootでcrontab -eして再起動時にイメージファイルの場所を指定したうえで動作するようにcron登録。

crontab -e
(以下入力)
@reboot sudo modprobe g_mass_storage file=/root/NAS/ForRecording/recording.img removeable=1

再起動してロードされているモジュールを確認し、存在していればOK。この時点でPC/MacからUSB給電していると、ホストのマシンにすでにストレージとして認識されているはず。。

lsmod | grep -E '(dwc|mass)'
g_mass_storage         16384  0
usb_f_mass_storage     49152  2 g_mass_storage
libcomposite           57344  2 g_mass_storage,usb_f_mass_storage
dwc2                  167936  0
udc_core               53248  3 usb_f_mass_storage,dwc2,libcomposite

sambaマウントして録画領域のディスクイメージを作る

NASなしでPi4に外付けHDDを接続させてもOK。NASよりもスループットに優れた高速コンバータになると思われる。 今回はNASを持っているので、SMBマウントする。

mbclient -L {host}
(共有名を確認)

mount -t cifs //{host}/{sharename} {mount_point} -o user={user},password={password},vers=3.0,iocharset=utf8
(SMBのバージョンやロケールは環境に応じて自由に)

マウントできていそうだったらfstabに設定。リブートして自動マウントされるか確認しておく。なお、マウントタイミングではNWが疎通しておらずマウントには失敗する。単に後からmount -aたたくだけで済むようにしたいがための設定。

# cifs
//{host}/{sharename} {mount_point} cifs username={user},password={password},vers=3.0,iocharset=utf8 0 2

先ほど同様、ddするなりしてディスクイメージを作る。録画用にサイズを大きくとるわけで、この工程が一番時間がかかる。LAN経由でやったら半日くらいかかったので、ローカルの容量が潤沢なら手元で作ってコピーする方が早い。NASによっては圧縮した状態で送り込んでCLIで展開できるものもあるので、都合の良い方法を選択すること。

ここまでできたら、CIFSマウントとモジュールロードを再起動時に一連の流れでやってもらうために以下書いておく。

crontab -e
(以下入力)

# removable=1だとUSBメモリ、0だと外付けHDDとして見える
@reboot sleep 5; mount -a && sudo modprobe g_mass_storage file=/root/NAS/ForRecording/recording.img removeable=0

こんな感じになる。

image.png

速度調査

BlackMagic Disk Speed Testで測定。

ローカルのSSDは高速。 image.png

USB経由NAS書き込みだと、こんなもの。 メーカー公称値が112Mbpsだから、100Mbps弱の性能が出ているということになる。 そもそも遅すぎるのはここでは無視する。 image.png

安定運用のポイントは電源

単純にテレビのバスパワーで使っているととにかく不安定。バスパワーじゃ電源容量不足気味だし、テレビの電源落とした時の省電力設定など邪魔でしかない。 実運用するときには、セルフパワーのUSBハブ、もしくはGPIOのVCC/GNDで電力しっかり給電してあげること。

ちなみに、我が家のテレビではハブを介していると録画できなかったので、GPIOに5Vを入れて安定可動している。

追記(2020/07/24)

Linux Kernelのg_mass_storageのドキュメント. https://www.kernel.org/doc/Documentation/usb/mass-storage.txt

参考