Open keiji opened 2 years ago
SecureStorage からのデータ復号に失敗してそうなので、機種変更などが原因で暗号化・復号の為のキーが変わってしまうのかなと(Android 事情はよく知らないですが)
現状推測されるシンプルな再現手段としては。
と言う所でしょうか。
例外の発生場所。
KeyStore.getKey() https://docs.microsoft.com/en-us/dotnet/api/java.security.keystore.getkey?view=xamarin-android-sdk-12#java-security-keystore-getkey(system-string-system-char())
Chipher.doFinal() https://docs.microsoft.com/en-us/dotnet/api/javax.crypto.cipher.dofinal?view=xamarin-android-sdk-12#javax-crypto-cipher-dofinal(system-byte())
情報提供ありがとうございます。 セキュリティ設定を変更したことがあるか聞いてみますね(機種変更はしていないとの情報をもらっています)。
さて具体的な対処ですが、上がってきている例外が Java.Lang.Exception
と言うところが問題を複雑にしているように思います。
これだと共通プロジェクト側で受けられないことに加えて、セキュリティ系の例外であることも判別できなさそうです。AndroidネイティブだとExceptionがどかんと上がってくることはないと思うので、Xamarin固有の挙動な気がします。この点も再現試験をしながら確認します。
原因の究明と合わせて対処方法ですが、これはSecureStorage使わない方向に舵を切ることも検討したいと考えています。 今は接触情報はエクスポートできるようにしてありますし(インポートはできませんが)、アプリの専用領域に保存していれば通常のAndroid端末であれば他のアプリから容易にアクセスできることはないという認識です。
加えて、現時点で発生している人たちへの案内も必要に思います。
暗号化鍵が失われてると既存のデータの復号は不可能なので、そのことを案内するのが主眼になると思います。1つ言えるのは、アプリを起動時にSecureStorageをチェックしてSignature/MAC verification failed
を検出して案内を出す。と言うことが考えられます。
接触が発生しているのに見られないという状況をリカバーする方策についてはい、くつか方法が考えられますが、再現の成功を待ってから検討したいと考えています。
接触情報がデバイス内で秘匿されるべき情報化は判断が付きかねますが、所有者本人であっても改竄出来ると困りそうだなと言う印象は有ります。
一応 StackTrace 内には AEADBadTagException とか KeyStoreException の文字は出てくるので Exception#getCause でさかのぼれば判別のは付くんですかね。やった事無いので分からないですが。
本件の原因と関連するかはさておき、 Android Keystore は簡単に鍵が消えてしまう(または新しい鍵に置き換わる)様なのでこれにのみ依存したデータ保存は避けた方が良さそうですね。 iOS の KeyChain は設計思想が違うのか、同様の操作では消えないようです。(詳細は追っていません)
状態そのものは再現できた。
DailySummaryまたはExposureWindows、その両方の暗号化に用いたキー情報がシステムから失われることで復号が不可能な状態になり「Signature/MAC verification」が発生している。
一方、単純にDailySummaryまたはExposureWindowsのどちらかが復号されない状態であれば、そもそも「接触情報画面」に遷移が発生せず「接触なし」という取り扱いになるはず。
DailySummaryまたはExposureWindowsのどちらか、または両方が復号できない状態で、かつCOCOAv1時代のExposureInformationが1つ以上存在する状態であると考えられる。ExposureInformationはシステムのキーで復号可能で(「Signature/MAC verification」が発生せず)、そのため「接触情報画面」に遷移が発生してしまっている。
ExposureInformationの時期のキーAが存在して、かつDailySummaryまたはExposureWindowsの暗号に使ったキーBが失われている状態がなぜ発生したのかは現時点で不明。
端末Aで保存したSecureStorageファイルを端末Bに移動する。 それだけだとホーム画面で「陽性登録者との接触結果を確認」をタップしても、その時点で読み込みに失敗して「接触情報画面」に遷移しないので、ソースコードを書き換えて無理矢理「接触情報画面」に遷移するようにした。
hasHighRiskExposure
の初期値はExposureInformationが1つ以上あるかとなっているので、ここをtrueと設定した。
セキュリティ設定の変更でシステムの鍵が消える挙動については、次の端末とAndroidのバージョンで試してみたが発生を確認できなかった。
Issueに挙げられているAndroid 8および9ではテストできていない。いま手元にないので、次に端末のある場所にいったら探してみます。
一方、今回の事象がセキュリティ設定の変更に起因するなら、鍵Aと鍵B、どちらも失われてExposureInformationも復号化できないので「接触情報画面」に遷移しないと考える。また、ExposureWindowが記録されるCOCOA2以降のバージョンになればExposureInformationは記録しないので、仮に推測通り端末内に復号可能なExposureInformationが記録されているとすれば、以前のキーは存在するが、後のキーが失われている状態になっていると考えられるので、事象とマッチしていないように思われる。
失礼。 https://github.com/cocoa-mhlw/cocoa/issues/1113#issuecomment-1219292722 こちらの資料を読み間違えていました。
セキュリティ設定の変更で鍵が消えるのは AndroidKeyStore 以前の旧APIの話で AndroidKeyStore を前提とし Android 6 以降を対象にするCOCOAには関係ありません。 AndroidKeyStore を使用しているケースでも指紋認証の追加で鍵が消えるパターンも有るようですが、鍵生成時の設定に依存しこれもCOCOAでは該当しません。
ユーザー操作により鍵が消失するケースとして想定されるのは以下の2パターンぐらいでしょうか。
@b-wind お気になさらず。リサーチと情報提供に感謝しています!
バックアップからの復元について、キー自体はアプリ内ではなく端末内部のKeyStoreに保存して取り出せないという性質を考えると、どういうケースで起きるかなと言ったところです。
原因は突き詰めていくとして、修復と回避策をどうするかも悩みどころですね……SecureStorageを止めてしまって、SharedPreferenceに保存する。 マイグレーションの過程でSecureStorageが読み出せなければExposureNotification APIからgetDailySummariesとgetExposureWindowsを使って最新情報を取得する。と言うことも考えられますね。
デバッグ版なら、アプリのストレージを1回全消去したとき、これまでの日次キーが速やかに消えたりしない(2,3時間猶予がある)のですが、先ほどぼくの普段使いの端末のCOCOAのストレージを全消去してみたら14日分の日次キー(おそらく受信したRPIも)が消えたので、この手は使えなさそうです。
何が起こっているのか、に着目すると今ハッキリ言えることは以下の 一点のみ 二点なのかなと。
鍵や暗号化されたデータの破損という可能性も考えましたが、それは別の例外が上がってきそう。 そしておそらくは鍵は失っているのでは無く、想定される物とは別の物が(Android Keystore から)取り出されていそう。
現状からの修復という面で見るとやるべき事は比較的明らかで以下の項目ぐらいしか手は無い物と思います。
保存先のマイグレーションを行う場合でも同様で、不整合があるデータをマイグレーションするのは想定外の状況になり得る怖さがありますね。
SecureStrage は暗号化処理を行いますが、実データは SharedPreference に保存していると言う理解です。
バックグラウンド等での接触チェックと画面表示が並列で動作したときに不整合が起きたりしないだろうかと考える物の、そう言うことが起こるのかどうか自体分かっておらず…… 正直よくわからない状況であるのは本音ですね。
ローカル通知からアプリを起動するとHomePageを経由せずに「過去14日間の接触」(ContactedNotifyPage ?)画面は開けますか?
本件とは恐らく関係無い物の、Android Keystore API は Thread Safe が保証されて居ないので同時実行制御を行った方が良さそうです。
https://weidianhuang.medium.com/how-to-write-thread-safe-code-for-android-keystore-40a0fa17f416
ローカル通知からアプリを起動するとHomePageを経由せずに「過去14日間の接触」(ContactedNotifyPage ?)画面は開けますか?
はい。開くことができます。
Android Keystore API は Thread Safe が保証されて居ないので
たしかに、ここを見る限りセマフォ取ってませんね。
SecureStorageService自体はasync対応に作っていないので、ExposureDataRepositoryのレベルで取ることを検討します。
不具合の内容 / Describe the bug
接触情報が正常に表示されない場合がある。
ホーム画面から「陽性登録者との接触結果を確認」をタップすると、過去14日間の接触画面には「陽性登録者との接触確認」と表示されるが、その下に表示されるべき「○日間に合計○分間の接触」の表示がない。
また、「陽性登録者との接触一覧」で遷移した先にも接触情報が表示される部分が空欄になっている。
動作ログを確認したところ、SecureStorageからデータを取り出すときに
Java.Lang.Exception: Signature/MAC verification failed
が発生している。再現手順 / Steps to reproduce
不明。
開発チームよりバックアップ・リストアが悪さしているかもという情報有り。 https://stackoverflow.com/a/69345932
期待される挙動 / Expected behavior
陽性登録者との接触結果が正常に表示される。
スクリーンショット
(画像は https://twitter.com/chokuzou44/status/1558725166762377217 より)
動作環境 / Environments
サポートチームに聞いたところ、今回とは別に1件だけ、同事象の問い合わせを受けたことがあるとのこと。
その他 / Additional context
これまでの発生報告が極めて少ない事象であり、現時点で再現方法などの手がかりが乏しいので、「うちでも発生したことがある」など手がかりがあればコメントで教えてください。
SecureStorage周りだと、過去に暗号化を止める(通常のアプリ管理領域に保存する)ことも検討している( #374 )ので、抜本的な解決策がないようであればこちらも考えてもいいかも。。
Internal IDs: