Open moonmile opened 4 years ago
ちょうど拙作のprobeCOCOATek使って、EN ServerのMock作れそうだな、現在の本物EN server(list.json + ZIP/TEK)の内容をキャッシュしてProxy的にも出来るしこれまで公開されてて溜め込んだ任意のlist.json + ZIP/TEKの状態を再現するとかも出来そうだなあと思っていたのですが、そういうことでしょうか。 その時々の状況で良いなら、単純にProxyでもいいかも知れませんね。 Validation Serverの方はFunctionsと同様のAPIパラメータを受け入れる(+Responseエミュレーション)だけですから、これも大した話では無さそうです。
ただし本物にProxy経由としてもアクセスし続けるのはあまり好ましくないやも知れないので、閉じた検証環境の方が良いのでしょうね。
ちょうど拙作のprobeCOCOATek使って、EN ServerのMock作れそうだな、現在の本物EN server(list.json + ZIP/TEK)の内容をキャッシュしてProxy的にも出来るしこれまで公開されてて溜め込んだ任意のlist.json + ZIP/TEKの状態を再現するとかも出来そうだなあと思っていたのですが、そういうことでしょうか。
さいです。そういうことです。
実機でテストする場合に、スマホ内にある TEK がわからないので、
というパターンを想像しています。 陽性者スマホのほうは、ビーコンを出せばよいので M5Stack でも可能かなと思ったりしてます。
1 と 4 のところでプロキシが必要なんですが、ここは Windows 10 やラズパイのホットスポット機能でできるかもしれません。
流れは理解しました。疑似TEKも生成したいですよね。署名鍵も疑似になりますが、アプリ的には問題無いですよね。 1,4は当面は単純なProxyでも良いとして、疑似TEK/ZIP生成含めて、作ってみますね。期待せずにいて下さい。Docker化してもいいですよね? COCOAでのValidation ServerのI/F、手っ取り早く知りたいんですけど、どこ見るのが最適でしょうか。
因みに、本物のTEKの署名って、Debug_Mockや実機テストで検証可能です?
本物TEKでも問題無いのか、あるいはどうしても疑似TEK必要になるのか、という意味です。
COCOAでのValidation ServerのI/F、手っ取り早く知りたいんですけど、どこ見るのが最適でしょうか。
https://github.com/openCACAO/Covid19Radar/blob/master/src/Covid19Radar.Api/DiagnosisApi.cs#L30
ここの Azure Functions の DiagnosisApi です。
因みに、本物のTEKの署名って、Debug_Mockや実機テストで検証可能です?
Debug_Mock では zip を解凍していないので、ちょっと無理だと思います。 https://github.com/openCACAO/Covid19Radar/blob/master/Covid19Radar/Covid19Radar/Services/TestNativeImplementation.cs#L49 このあたりで、ExposureInfo を入れているだけです。
Xamarin.ExposureNotifications のほうの PlatformDetectExposuresAsync 内でも、.bin と .sig を合わせて iOS EN api に渡すようになっているので、TEK の署名のところは実際やってみないとわからないという感じですね。解凍しているだけなので、本物でも疑似でも大丈夫だとは思うんですが。
サーバーサイドは、接触確認アプリをホットスポットに繋げれば、Docker でも構わないですね。
陽性者の TEK は、Android などででビーコンを出すツールと作れば、それで十分かも。
「陽性者 TEK 登録アプリ」を作ると
確認ですが、「陽性者 TEK 登録アプリ」からのValidation Serverへの登録は必ずしもDiagnosisApiに準じなくともよいのだろうと推察しますが、まあ擬似的にエミュレーションできたに越したことは無い、感じですね。 API側でのValidation処理はサボるとして、結局、 https://github.com/Covid-19Radar/Covid19Radar/blob/786a5f5c95bef96313ecb77be854ca6688791d1b/src/Covid19Radar.Api/Models/DiagnosisSubmissionParameter.cs で定義されているJSONがPOSTされてくるのでこれをTemporaryExposureKeyExport message化とZIP化してEN(配信)サーバーからDL出来れば良い(URLはあまり拘り無し)ということになりますね?
これはもし可能ならですが、上記のPOSTについて(1の処理)、curl形式などでRequest/Responseの実例が入手出来るようなら頂けませんか。要は陽性者 TEK 登録アプリとの結合テストとなると思いますが、こちらでも単体テストで試せた方が無難と思いますので。 DiagnosisSubmissionParameter.の既定値については実際のTEKを参考にしておきます。もし不明な点があればご質問させて下さい。
Xamarin.ExposureNotifications のほうの PlatformDetectExposuresAsync 内でも、.bin と .sig を合わせて iOS EN api に渡すようになっているので、TEK の署名のところは実際やってみないとわからないという感じですね。解凍しているだけなので、本物でも疑似でも大丈夫だとは思うんですが。
こちら確実にマッチする陽性者TEKを渡さずとも、現在の本物TEKであっても.sigの署名検証は通るかどうかは分かるかと思うのですがいかがでしょうか。(検証失敗でもエラーが返らない可能性もありますが) ただし、と言うことであればEN APIが署名鍵に対応する検証鍵を「内部的に」有しており変更不可ということになりますか?だとすると疑似鍵では駄目かも知れないですね… (Registration時に登録するのでしたっけ…??)
Androidなら実機テストでも署名検証は無視できそうですね。 https://developers.google.com/android/exposure-notifications/debug-mode iOSはちょっと分かりませんでした。
ここの前後を見る限り、.bin と .sig を渡しているだけなので、DL 時に検証鍵が必要かどうかはわかりません。
ここの部分のテストって、rocaz さんの言われるように、「現在の本物のTEKを固めて、疑似的な配信サーバから iOS に落としてみる」で検証ができそうですね。何かプロキシを立てて。
もうひとつの「陽性登録」の json のほうは、最終的に、
を呼び出すですが、この手間がいくつかあって、結構かかりそう。
DL時は何ら認証などは無いのですが、Googleのドキュメントを読む限りは、検証鍵(公開鍵)はGoogleへ提供されるとあります。これは恐らく、EN APIが呼び出されるとGoogle Playサービスが呼び出されて.sigの署名検証がされるの意味かなと思います。 Appleはやはり詳細不明なのですが恐らくはほぼ同様にアプリ公開時にiTunes Connectなど経由で預けているような気がします。 とすると
ここの部分のテストって、rocaz さんの言われるように、「現在の本物のTEKを固めて、疑似的な配信サーバから iOS に落としてみる」で検証ができそうですね。何かプロキシを立てて。
は本物なので問題無さそうですが、疑似TEKでは真性の署名は行えませんから、陽性者用スマホから疑似Validation Serverへの登録は行えても、疑似署名鍵で署名した疑似TEKではEN APIではねられてしまう気がします。
ただ上記の通りAndroidであればデバッグモードで検証を行われなくは出来そうですが、iOSの場合は難しいかも知れませんね。
とりあえず別に無駄になってもよいので疑似EN ServerとValidation Serverは何かしら作ってみますが、JSONだけ何とか頂ければ助かります。
もしValidation Serverからの陽性者TEK取り込みは難しくとも、過去の本物TEKを用いた(特に障害を起こしてた奴とか)疑似EN Serverだけでも何かと便利かも知れません。
なるほど。疑似TEKが通らないの件、わかりました。 これは、試しにプロキシを立てて list.json を実機に流し込めるかどうかで確認できそうです。これはどっちにせよ試さないといけないので、調べていきますね。
陽性登録のときに PUT する JSON データは、少しコードを変えて Debug_Mock でも拾えそうなので試してみます。
陽性登録をしたときに PUT method で送る JSON データです。 戻り値はなしで、ステータスのみチェック。
{
"userUuid":"dummy uuid",
"keys":[
{"keyData":"BVHX3Xp9tYtwFak2QtkoHw==","rollingStartNumber":2664652,"rollingPeriod":2,"transmissionRisk":3},
{"keyData":"CGRNx3RfpSDqJBVN20JiBg==","rollingStartNumber":2664508,"rollingPeriod":2,"transmissionRisk":5},
{"keyData":"IYJChwe9HQi6rL9jmPcf0w==","rollingStartNumber":2664364,"rollingPeriod":0,"transmissionRisk":6},
{"keyData":"aj3deDhakpB2Ijln0zxUUQ==","rollingStartNumber":2664220,"rollingPeriod":7,"transmissionRisk":5},
{"keyData":"zIFdj6pspPhgh4weLQjANA==","rollingStartNumber":2664076,"rollingPeriod":9,"transmissionRisk":5},
{"keyData":"+ahJ6PR2R/rOU9iDDkb5yQ==","rollingStartNumber":2663932,"rollingPeriod":10,"transmissionRisk":3},
{"keyData":"0NqScNRA+QsEWPQxpp6gDA==","rollingStartNumber":2663788,"rollingPeriod":3,"transmissionRisk":6},
{"keyData":"DOSN5kiwnlAf3XhGJGQBvw==","rollingStartNumber":2663644,"rollingPeriod":0,"transmissionRisk":7},
{"keyData":"tIjUCe7oce8FnKw54gLupA==","rollingStartNumber":2663500,"rollingPeriod":2,"transmissionRisk":4},
{"keyData":"cuqR4tKDIByTvjOPMOc63w==","rollingStartNumber":2663356,"rollingPeriod":0,"transmissionRisk":4},
{"keyData":"gAP1pHRZqgGj5vp2WH+o3g==","rollingStartNumber":2663212,"rollingPeriod":10,"transmissionRisk":6},
{"keyData":"LtK1UOnSVbO7dmvF/wOqHg==","rollingStartNumber":2663068,"rollingPeriod":7,"transmissionRisk":6},
{"keyData":"LM51ODjQ3zoiHWa051KawA==","rollingStartNumber":2662924,"rollingPeriod":6,"transmissionRisk":7}
],
"regions":["440"],
"platform":"android",
"deviceVerificationPayload":"dummy DeviceVerificationPayload",
"appPackageName":"net.moonmile.cocoa",
"verificationPayload":"12345678",
"padding":"4mMZg2sUvv9/YcmntCX5ZIsfx3dTTp9ZXKaVNTXAeFoss13On6jhLh8PFAdT1q1MnNtAdXWfPpPDwCfEfonKvuluegDIK8XiPplYQzqYBob5SZhZgALljHXJCt+BHK+WRVUe5Fusg3EVXTfcR/pZK7mhyzXwcD7mie6L/yHmYSHpwv3keiIqTMUmQyPLCEV6b98l31B5pdUXN1psIYRQL/sV84T9dbGXdn9wySwaGoCoEY9BwIHRbx0ozELHvipB1hQntRXAffktjmnRCduo/R3Zww/0Dm55RINn1Khv0LhULvMY8mRr55SVFDIYAGVCf88l7fobTMilrXGSWahRvr2wfNvokAq4btx7Xvo8YUQeJFTZSeK+OeEqckTz2Y7sRFV0aIOKWu/FbH8eoO3OaEwtczRTd3Tb8kL5vR3FxZVOjhqkx/jAk8plE91joJn0XAn3xWVZmMzgmXVAkZzRAKfqzxHTKqFUbeBq7WSXeFKpBWETzf8w9fZNwy6CfwItlBM1edCHIDJoo1eE7qtoSSEjl4w7vTOnudobhmZz0e7dPFlQYf+BIuQvqymnqwZL5HoNX+d28tfgjr/1bJrL9dJpp7JRGOchoXWnDBEJ/w7Uz6HsZnEGZ0qCcNNAjLVKY7ZWPgRT+cyS671yzuXF1o6wIJz5349gYz9oz6cU4RW/Xahbs8EKgdbUWXq0k5hkvJlEj2wYUYQn9dvE6ykkfgmos3E8aSPNpp/GJIUUrM+mAxIOz1KppwskNL3mRZWtr9twDOAmKFN2d+jMUiz/EkkLHecWFq63/wtmGDVm52EJGXSpo5/vgNu/mFhp3FTeJSFz2yeulpksuQ3UwA8dD5CGwzm4VW+pG50LV35WTiK2h/MU8gw6WoLbfeqSGr/LoYjH4hgbJs+xyr+Joj8b1WrqHTkcCR2ETGi23NiFFwHeQLX1jmsJm2AjYihMsLhDwKd/qPyGr5hqQ5WwZEr3LLO+q2KJJUf/4ILZ+af1N5uwcxwxBW4EV5Edi0vVWTYWLDV1jgVWuptgf8d6rLXwdQz6APEtxSZEuVPRa5r8KYZMw81xf6iWWrmB0YWniqvbLBU+21lE6GYPMdWxhE4BY1ZALenof/ZkY6Y4rPSgnC+o9T+EvU0tB9tnYU7S5dVrgG0vZlzC860f2XizCztxbpSlCY4OfKYIEyYOgB2nkX3QzSyHcDbT5gKSZeTVefuf9fO6LdxGXAPHJ1I1s3KPd0f64j4P6v1d0SKMfBfBMpoqb4oq7WShinEov0njnTKQAu2c2pa9jwv1XFu5ud/sBfS4C0TnZIrjWVjM5iq0eTTw/HkpJVgDbd9/NZAJ6NCRPLpVK5LtqJ5iuYE+8KjhQeMnyL1lwt+ctZ1laC/62nCHOqpvXk6hAlPbxmzWxHw2nfSt4h8TLHnauXX0iD9vaN8Qt7X+ceKUhcVpivxzxfEhLss6TPhRdA4wlaXZrHoUa16NxQzHOCsRGVxZqaF9Z/OD01l0NBvmvkOmzdw9u9hLVR6e5f/lWfafKTOb+Mpssy2yVdkhnzdCpgfIIGpO1F6DDlOD34Hznnli1QvnS8Z4ORmvi2X/KbbnWz/4HDiLUFTVGOp36dSsvrZfvsosWltAT+ZRn0n4g+SdnINB+4JA14S2ETLcAVBFAsyvoGvruwHkt8xSOrAL3ZQF2U1RasIzHudFdUgoZE2WC518SeN5seGCXUO27pzTqjSe1igD9JgKe4GDpkW4u1N+uy8TjpSxjChszgx+fS9LcsoUio0lkxcFcs1h9QubyaLvNzJtwByg"
}
以下は、コードを参照するときのメモ書き1
+ NotifyOtherPageViewModel::OnClickRegister
userData.AddDiagnosis(_diagnosisUid, new DateTimeOffset(DateTime.Now));
await userDataService.SetAsync(userData);
await Xamarin.ExposureNotifications.ExposureNotification.SubmitSelfDiagnosisAsync()
+ ExposureNotification::SubmitSelfDiagnosisAsync
var selfKeys = await GetSelfTemporaryExposureKeysAsync();
await Handler.UploadSelfExposureKeysToServerAsync(selfKeys);
internal static Task<IEnumerable<TemporaryExposureKey>> GetSelfTemporaryExposureKeysAsync()
=> nativeImplementation != null ? nativeImplementation.GetSelfTemporaryExposureKeysAsync() : PlatformGetTemporaryExposureKeys();
+ ExposureNotificationHandler::UploadSelfExposureKeysToServerAsync
var latestDiagnosis = userData.LatestDiagnosis;
var selfDiag = await CreateSubmissionAsync(temporaryExposureKeys, latestDiagnosis);
HttpStatusCode httpStatusCode = await httpDataService.PutSelfExposureKeysAsync(selfDiag);
+ ExposureNotificationHandler::CreateSubmissionAsync
+ HttpDataService::PutSelfExposureKeysAsync
var url = $"{AppSettings.Instance.ApiUrlBase.TrimEnd('/')}/diagnosis";
var content = new StringContent(Utils.SerializeToJson(request), Encoding.UTF8, "application/json");
HttpStatusCode status = await PutAsync(url, content);
この場合、実機cocoaのTEKs(陽性者のTEKs)を取得できるが、結構面倒。
の手順でもよいかもしれない。
PROXY 自体は、Windows 10 かラズパイのホットスポット機能を使えばよい。
SafetyNet Attestation API | Android デベロッパー | Android Developers
JSONありがとうございました。承知です。
あ、そうそう、SafetyNetのことを言いたいのでした。また、”Attestation”ですね。 APK詐称防止以外にもFIDO2のPlatform Authenticatorとかいろいろ広く応用されてます。
なんとなく動くような感じのが出来たので、置いておきました。非常に簡易版です。よろしければ試してみて下さい。 ただし疑似署名なので、Androidなどで署名チェックを無視できないとEN APIでは無視されるだろうなあとも思います。 現在は1TEK=1ZIPにバラしています。本当は、モバイルアプリ側から固める単位を指示させたり、rolling_start_numberあたりから同一判明日?単位でまとめたりはすべきなのでしょうね
export.binのFixed Header付けるのとSHA256取り忘れるという大失敗があったので、version2に更新しました
COCOAmockservice ありがとうございます。 環境構築をして、まずは「Androidなどで署名チェック」が突破できるか/できないかを確認しないと。
陽性登録のほうは、改めてシーケンスを見ると突破できそうになさそうですね。 スマホ内の TEKs が抜けてしまうと、zip と直接照合できるから駄目なのかな。
検証環境では、スマホのプロキシを使って HTTP Proxy + Hosts を使えばいけるんじゃないか、というアドバイスを貰いました。 確かに、PC にプロキシサーバーを立てて、スマホにプロキシを設定したほうがよさそう。あとで検証。
と、Android のブラウザでプロキシ設定をしたが、どうやらブラウザの方にしか効かないので、アプリのほうはうまくいかず。まじめに、ラズパイあたりでルーターを作って「ポートフォーワード」するのが良いっぽい。
プロキシで list.json と zip を切り替える方式なのですが、Android で https を呼び出すときにプロキシが正式な CA を返せないので無理そうです。
この場合、途中で挟まるプロキシの CA を Android 側に入れてやれば通ると思うのですが、もとの COCOA は改変しない前提なので駄目。
正式な環境であれば、
ことで可能なのですが。この環境ができているかどうかが不明だし。 ということで、いったんこの issue は保留にさせてください。あとで、ドイツとフランスではどうやっているか見ておきます。
はじめに戻って要件のまとめ
これで、list.json や zip のすり替えが出来きることによって、
などが実機でできるのではいないか、という想定。
ただし、rocaz さんの
ただし疑似署名なので、Androidなどで署名チェックを無視できないとEN APIでは無視されるだろうなあとも思います。
のような懸念があるので、想定通りには動かない可能性があり。
なので、あまり検証環境に労力を掛けても無駄かもしれないという不安があるので、そもそもこの環境が構築可能かどうかを考えておきたい。
アクセスポイントを通した後に、透過プロキシ、 SOCKS Proxy を通したらいけるのではないかという話もあるのだけど、ちょっとよくわからず。
書き殴ったので読みにくいかもですが、どうぞ。
結論としては rooted 端末か Android 6 であれば単体で可能。出来ればアプリで network-security-config 設定して。と言う事になります。
https://gist.github.com/b-wind/d69ec95604329a3af6e3f618e4cf93c9
ありがとうございます。なかなか、ネットワーク素人には難しそうですね。
App Store に公開されている COCOA を使ってのテストは厚生労働省環境以外では難しそうなので、 EN API をすり替えて、通知と Risk 計算のところをテストできないかと模索中です。
エミュレータ上で、key server から zip をダウンロードするところまではいけたので、接触データの TEK を入れ替えながらというのはできそうです。まあ、このあたりは単体テスト部分を切り出して、xUnit を使うのが筋なんですが。
日本版のCOCOAに限らずなんですが、EN APIの制限のおかげで各国のアプリの実機デバッグが非常にやりにく状態になっています(リリースした後にしかデータ不整合をチェックできない)。
本来ならば検証サーバーを立てて、疑似的にTEKの蓄積を行って検証パターンを決めていくところなので、その環境構築が作れるのかどうかをまずは考察しましょう。
という issue 出しです。
方法案
陽性登録はHER-SYS から割り振られるので、適当な番号を登録すれば間違って本番サーバーに通知されたとしても弾かれるはず。というか、弾かれないと困るし。