diff --git a/Covid19Radar/Covid19Radar/Common/DeviceVerifierUtils.cs b/Covid19Radar/Covid19Radar/Common/DeviceVerifierUtils.cs index 964bb4a39..94259db64 100644 --- a/Covid19Radar/Covid19Radar/Common/DeviceVerifierUtils.cs +++ b/Covid19Radar/Covid19Radar/Common/DeviceVerifierUtils.cs @@ -29,7 +29,8 @@ public static byte[] CreateAndroidNonceV3(V1EventLogRequest eventLogRequest) public static string GetNonceClearTextV3(DiagnosisSubmissionParameter submission) { - return string.Join("|", submission.SymptomOnsetDate, submission.AppPackageName, GetKeyString(submission.Keys), GetRegionString(submission.Regions), submission.VerificationPayload); + string hasSymptom = submission.HasSymptom ? "HasSymptom" : "NoSymptom"; + return string.Join("|", submission.AppPackageName, submission.OnsetOfSymptomOrTestDate, hasSymptom, GetKeyString(submission.Keys), GetRegionString(submission.Regions), submission.VerificationPayload); static string GetKeyString(IEnumerable keys) => string.Join(",", keys.OrderBy(k => k.KeyData).Select(k => GetKeyStringCore(k))); diff --git a/Covid19Radar/Covid19Radar/Model/DiagnosisSubmissionParameter.cs b/Covid19Radar/Covid19Radar/Model/DiagnosisSubmissionParameter.cs index dfe54d395..6fe330e0d 100644 --- a/Covid19Radar/Covid19Radar/Model/DiagnosisSubmissionParameter.cs +++ b/Covid19Radar/Covid19Radar/Model/DiagnosisSubmissionParameter.cs @@ -8,8 +8,11 @@ namespace Covid19Radar.Model { public class DiagnosisSubmissionParameter { - [JsonProperty("symptomOnsetDate")] - public string SymptomOnsetDate { get; set; } + [JsonProperty("hasSymptom")] + public bool HasSymptom { get; set; } + + [JsonProperty("onsetOfSymptomOrTestDate")] + public string OnsetOfSymptomOrTestDate { get; set; } [JsonProperty("keys")] public Key[] Keys { get; set; } diff --git a/Covid19Radar/Covid19Radar/Services/DiagnosisKeyRegisterServer.cs b/Covid19Radar/Covid19Radar/Services/DiagnosisKeyRegisterServer.cs index 9f52ad677..6689dbb05 100644 --- a/Covid19Radar/Covid19Radar/Services/DiagnosisKeyRegisterServer.cs +++ b/Covid19Radar/Covid19Radar/Services/DiagnosisKeyRegisterServer.cs @@ -33,6 +33,7 @@ IDeviceVerifier deviceVerifier } public async Task SubmitDiagnosisKeysAsync( + bool hasSymptom, DateTime symptomOnsetDate, IList temporaryExposureKeys, string processNumber, @@ -60,7 +61,13 @@ string idempotencyKey ); } - var diagnosisInfo = await CreateSubmissionAsync(symptomOnsetDate, temporaryExposureKeys, processNumber, idempotencyKey); + var diagnosisInfo = await CreateSubmissionAsync( + hasSymptom, + symptomOnsetDate, + temporaryExposureKeys, + processNumber, + idempotencyKey + ); return await _httpDataService.PutSelfExposureKeysAsync(diagnosisInfo); } finally @@ -70,6 +77,7 @@ string idempotencyKey } private async Task CreateSubmissionAsync( + bool hasSymptom, DateTime symptomOnsetDate, IList temporaryExposureKeys, string processNumber, @@ -93,7 +101,8 @@ string idempotencyKey // Create the submission var submission = new DiagnosisSubmissionParameter() { - SymptomOnsetDate = symptomOnsetDate.ToString(AppConstants.FORMAT_TIMESTAMP), + HasSymptom = hasSymptom, + OnsetOfSymptomOrTestDate = symptomOnsetDate.ToString(AppConstants.FORMAT_TIMESTAMP), Keys = keys.ToArray(), Regions = AppSettings.Instance.SupportedRegions, Platform = DeviceInfo.Platform.ToString().ToLowerInvariant(), diff --git a/Covid19Radar/Covid19Radar/Services/IDiagnosisKeyRegisterServer.cs b/Covid19Radar/Covid19Radar/Services/IDiagnosisKeyRegisterServer.cs index c398fa465..1a4d01764 100644 --- a/Covid19Radar/Covid19Radar/Services/IDiagnosisKeyRegisterServer.cs +++ b/Covid19Radar/Covid19Radar/Services/IDiagnosisKeyRegisterServer.cs @@ -13,6 +13,7 @@ namespace Covid19Radar.Services public interface IDiagnosisKeyRegisterServer { public Task SubmitDiagnosisKeysAsync( + bool hasSymptom, DateTime symptomOnsetDate, IList temporaryExposureKeys, string processNumber, diff --git a/Covid19Radar/Covid19Radar/ViewModels/HomePage/NotifyOtherPageViewModel.cs b/Covid19Radar/Covid19Radar/ViewModels/HomePage/NotifyOtherPageViewModel.cs index 04135f4cd..55f2d6169 100644 --- a/Covid19Radar/Covid19Radar/ViewModels/HomePage/NotifyOtherPageViewModel.cs +++ b/Covid19Radar/Covid19Radar/ViewModels/HomePage/NotifyOtherPageViewModel.cs @@ -33,6 +33,8 @@ public class NotifyOtherPageViewModel : ViewModelBase, IExposureNotificationEven private readonly IEssentialsService _essentialsService; private readonly int _delayForErrorMillis; + private bool _hasSymptom = false; + private string _processingNumber; public string ProcessingNumber { @@ -366,6 +368,7 @@ IList filteredTemporaryExposureKeyList } return await diagnosisKeyRegisterServer.SubmitDiagnosisKeysAsync( + _hasSymptom, _diagnosisDate, filteredTemporaryExposureKeyList, ProcessingNumber, @@ -429,11 +432,13 @@ public void OnClickRadioButtonIsTrueCommand(string text) if (AppResources.NotifyOtherPageRadioButtonYes.Equals(text)) { + _hasSymptom = true; IsVisibleWithSymptomsLayout = true; IsVisibleNoSymptomsLayout = false; } else if (AppResources.NotifyOtherPageRadioButtonNo.Equals(text)) { + _hasSymptom = false; IsVisibleWithSymptomsLayout = false; IsVisibleNoSymptomsLayout = true; } diff --git a/Covid19Radar/Tests/Covid19Radar.UnitTests/Common/DeviceVerifierUtilsTests.cs b/Covid19Radar/Tests/Covid19Radar.UnitTests/Common/DeviceVerifierUtilsTests.cs index 897c735e8..6306fd3b3 100644 --- a/Covid19Radar/Tests/Covid19Radar.UnitTests/Common/DeviceVerifierUtilsTests.cs +++ b/Covid19Radar/Tests/Covid19Radar.UnitTests/Common/DeviceVerifierUtilsTests.cs @@ -16,7 +16,8 @@ public class DeviceVerifierUtilsDiagnosisSubmissionParametersTests { private const string EXPECTED_CLEAR_TEXT_V1 = "jp.go.mhlw.cocoa.unit_test|S2V5RGF0YTE=.10000.140.0,S2V5RGF0YTI=.20000.141.0,S2V5RGF0YTM=.30000.142.0,S2V5RGF0YTQ=.40000.143.0,S2V5RGF0YTU=.50000.70.0|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; private const string EXPECTED_CLEAR_TEXT_V2 = "jp.go.mhlw.cocoa.unit_test|S2V5RGF0YTE=.10000.140,S2V5RGF0YTI=.20000.141,S2V5RGF0YTM=.30000.142,S2V5RGF0YTQ=.40000.143,S2V5RGF0YTU=.50000.70|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; - private const string EXPECTED_CLEAR_TEXT_V3 = "2021-12-19T19:02:00.000+09:00|jp.go.mhlw.cocoa.unit_test|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; + private const string EXPECTED_CLEAR_TEXT_V3_HASSYMPTOM = "jp.go.mhlw.cocoa.unit_test|2021-12-19T19:02:00.000+09:00|HasSymptom|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; + private const string EXPECTED_CLEAR_TEXT_V3_NOSYMPTOM = "jp.go.mhlw.cocoa.unit_test|2021-12-19T19:02:00.000+09:00|NoSymptom|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; private static DiagnosisSubmissionParameter.Key CreateDiagnosisKey( string keyData, @@ -124,7 +125,7 @@ public void AndroidClearTextTestV2() } [Fact] - public void AndroidClearTextTestV3() + public void AndroidClearTextTestV3_HasSymptom() { var platform = "Android"; var dummyDiagnosisKeyDataList = new[] { @@ -151,9 +152,57 @@ public void AndroidClearTextTestV3() var submissionParameter = new DiagnosisSubmissionParameter() { + HasSymptom = true, + Platform = platform, + Regions = dummyRegions, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, + Keys = dummyDiagnosisKeyDataList, + DeviceVerificationPayload = dummyDeviceVerificationPayload, + AppPackageName = dummyAppPackageName, + VerificationPayload = dummyVerificationPayload, + Padding = dummyPadding, + }; + + string clearText = DeviceVerifierUtils.GetNonceClearTextV3(submissionParameter); + Assert.Equal( + EXPECTED_CLEAR_TEXT_V3_HASSYMPTOM, + clearText + ); + } + + + [Fact] + public void AndroidClearTextTestV3_NoSymptom() + { + var platform = "Android"; + var dummyDiagnosisKeyDataList = new[] { + CreateDiagnosisKey("KeyData1", 10000, 140, 1), + CreateDiagnosisKey("KeyData2", 20000, 141, 1), + CreateDiagnosisKey("KeyData3", 30000, 142, 1), + CreateDiagnosisKey("KeyData4", 40000, 143, 1), + CreateDiagnosisKey("KeyData5", 50000, 70, 1), + }; + + var dummyRegions = new string[] + { + "440", + "441", + }; + + var dummySymptomOnsetDate = "2021-12-19T19:02:00.000+09:00"; + var dummyDeviceVerificationPayload = "DeviceVerificationPayload THIS STRING IS MEANINGLESS"; + var dummyAppPackageName = "jp.go.mhlw.cocoa.unit_test"; + var dummyVerificationPayload = "VerificationPayload THIS STRING IS MEANINGLESS"; + + // This value will not affect any result. + var dummyPadding = new Random().Next().ToString(); + + var submissionParameter = new DiagnosisSubmissionParameter() + { + HasSymptom = false, Platform = platform, Regions = dummyRegions, - SymptomOnsetDate = dummySymptomOnsetDate, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, Keys = dummyDiagnosisKeyDataList, DeviceVerificationPayload = dummyDeviceVerificationPayload, AppPackageName = dummyAppPackageName, @@ -163,7 +212,7 @@ public void AndroidClearTextTestV3() string clearText = DeviceVerifierUtils.GetNonceClearTextV3(submissionParameter); Assert.Equal( - EXPECTED_CLEAR_TEXT_V3, + EXPECTED_CLEAR_TEXT_V3_NOSYMPTOM, clearText ); } diff --git a/src/Covid19Radar.Api.Common/Common/Constants.cs b/src/Covid19Radar.Api.Common/Common/Constants.cs index da78b963e..1a60e54e7 100644 --- a/src/Covid19Radar.Api.Common/Common/Constants.cs +++ b/src/Covid19Radar.Api.Common/Common/Constants.cs @@ -28,10 +28,21 @@ public static class Constants public const uint ActiveRollingPeriod = 144; /// + /// For compatible with Legacy-V1 mode. /// Number of days relative that have infectiousness from the date of diagnosis or onset. /// public const int DaysHasInfectiousness = -3; + /* + * [Important] + * The value `daysSinceOnsetOfSymptoms` must be less than or equal to `+14` and greater than or equal to `-14`. + * + * If any diagnosis-keys file CONTAMINATED by out of range value(e.g. -199, 62) that provide detectExposure/provideDiagnosisKeys method, + * ExposureNotification API for Android doesn't return any result(ExposureDetected/ExposureNotDetected) to BroadcastReceiver. + */ + public const int MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS = -14; + public const int MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS = 14; + /// /// Extra value when TemporaryExposureKey reoprtType missing. /// diff --git a/src/Covid19Radar.Api.Tests/Models/V3DiagnosisSubmissionParameterTest.cs b/src/Covid19Radar.Api.Tests/Models/V3DiagnosisSubmissionParameterTest.cs index 67665de96..16568e4a7 100644 --- a/src/Covid19Radar.Api.Tests/Models/V3DiagnosisSubmissionParameterTest.cs +++ b/src/Covid19Radar.Api.Tests/Models/V3DiagnosisSubmissionParameterTest.cs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; -using Covid19Radar.Api.Extensions; using System.Text; using Covid19Radar.Api.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -14,11 +13,13 @@ namespace Covid19Radar.Api.Tests.Models [TestCategory("Models")] public class V3DiagnosisSubmissionParameterTest { - private const string EXPECTED_CLEAR_TEXT_V3 = "2021-12-19T19:02:00.000+09:00|jp.go.mhlw.cocoa.unit_test|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; - private const string EXPECTED_TRANSACTION_ID_SEED_V3 = "2021-12-19T19:02:00.000+09:00jp.go.mhlw.cocoa.unit_testS2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1440,441"; + private const string EXPECTED_CLEAR_TEXT_V3_HASSYMPTOM = "jp.go.mhlw.cocoa.unit_test|2021-12-19T19:02:00.000+09:00|HasSymptom|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; + private const string EXPECTED_TRANSACTION_ID_SEED_V3_HASSYMPTOM = "jp.go.mhlw.cocoa.unit_test2021-12-19T19:02:00.000+09:00HasSymptomS2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1440,441"; + private const string EXPECTED_CLEAR_TEXT_V3_NOSYMPTOM = "jp.go.mhlw.cocoa.unit_test|2021-12-19T19:02:00.000+09:00|NoSymptom|S2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1|440,441|VerificationPayload THIS STRING IS MEANINGLESS"; + private const string EXPECTED_TRANSACTION_ID_SEED_V3_NOSYMPTOM = "jp.go.mhlw.cocoa.unit_test2021-12-19T19:02:00.000+09:00NoSymptomS2V5RGF0YTE=.10000.140.1,S2V5RGF0YTI=.20000.141.1,S2V5RGF0YTM=.30000.142.1,S2V5RGF0YTQ=.40000.143.1,S2V5RGF0YTU=.50000.70.1440,441"; - private const string EXPECTED_CLEAR_TEXT_V3_NO_KEY = "2021-12-19T19:02:00.000+09:00|jp.go.mhlw.cocoa.unit_test||440,441|VerificationPayload THIS STRING IS MEANINGLESS"; - private const string EXPECTED_TRANSACTION_ID_SEED_V3_NO_KEY = "2021-12-19T19:02:00.000+09:00jp.go.mhlw.cocoa.unit_test440,441"; + private const string EXPECTED_CLEAR_TEXT_V3_NO_KEY = "jp.go.mhlw.cocoa.unit_test|2021-12-19T19:02:00.000+09:00|HasSymptom||440,441|VerificationPayload THIS STRING IS MEANINGLESS"; + private const string EXPECTED_TRANSACTION_ID_SEED_V3_NO_KEY = "jp.go.mhlw.cocoa.unit_test2021-12-19T19:02:00.000+09:00HasSymptom440,441"; [TestMethod] public void CreateMethod() @@ -36,37 +37,6 @@ public void PropertiesTest() Helper.ModelTestHelper.PropetiesTest(model); } - [DataTestMethod] - [DataRow("KEYDATA", 0, 0, 0, true)] - [DataRow("KEYDATA", 145, 0, 0, false)] - [DataRow("KEYDATA", 144, -15, 0, false)] - [DataRow("KEYDATA", 144, -14, 0, true)] - [DataRow("KEYDATA", 144, -6, 0, true)] - [DataRow("KEYDATA", 144, -5, 0, true)] - [DataRow("KEYDATA", 144, -4, 0, true)] - [DataRow("KEYDATA", 144, -3, 0, true)] - [DataRow("KEYDATA", 144, -2, 0, true)] - [DataRow("KEYDATA", 144, -1, 0, true)] - [DataRow("KEYDATA", 144, 0, 0, true)] - [DataRow("KEYDATA", 144, 0, -14, true)] - [DataRow("KEYDATA", 144, 0, 14, true)] - [DataRow("KEYDATA", 144, 0, -15, false)] - [DataRow("KEYDATA", 144, 0, 15, false)] - [DataRow("KEYDATA", 144, 1, 0, false)] - public void KeyValidationTest(string keyData, int rollingPeriod, int rollingStartNummberDayOffset, int daysSinceOnsetOfSymptoms, bool isValid) - { - var dateTime = DateTime.UtcNow.Date; - - var key = CreateDiagnosisKey( - keyData, - (int)dateTime.AddDays(rollingStartNummberDayOffset).ToRollingStartNumber(), - rollingPeriod, - 1, - daysSinceOnsetOfSymptoms - ); - Assert.AreEqual(isValid, key.IsValid()); - } - private static V3DiagnosisSubmissionParameter.Key CreateDiagnosisKey( string keyData, int rollingStartNumber, @@ -89,7 +59,60 @@ int daysSinceOnsetOfSymptoms } [TestMethod] - public void DeviceVerificationTest() + public void DeviceVerificationTest_HasSymptom() + { + var platform = "Android"; + var dummyDiagnosisKeyDataList = new[] { + CreateDiagnosisKey("KeyData1", 10000, 140, 1, -1), + CreateDiagnosisKey("KeyData2", 20000, 141, 1, 0), + CreateDiagnosisKey("KeyData3", 30000, 142, 1, 1), + CreateDiagnosisKey("KeyData4", 40000, 143, 1, 2), + CreateDiagnosisKey("KeyData5", 50000, 70, 1, 3), + }; + + var dummyRegions = new string[] + { + "440", + "441", + }; + var dummySymptomOnsetDate = "2021-12-19T19:02:00.000+09:00"; + + var dummyDeviceVerificationPayload = "DeviceVerificationPayload THIS STRING IS MEANINGLESS"; + var dummyAppPackageName = "jp.go.mhlw.cocoa.unit_test"; + var dummyVerificationPayload = "VerificationPayload THIS STRING IS MEANINGLESS"; + + // This value will not affect any result. + var dummyPadding = new Random().Next().ToString(); + + // preparation + var model = new V3DiagnosisSubmissionParameter() + { + HasSymptom = true, + Platform = platform, + Regions = dummyRegions, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, + Keys = dummyDiagnosisKeyDataList, + DeviceVerificationPayload = dummyDeviceVerificationPayload, + AppPackageName = dummyAppPackageName, + VerificationPayload = dummyVerificationPayload, + Padding = dummyPadding, + }; + + Assert.AreEqual(dummyDeviceVerificationPayload, model.JwsPayload); + Assert.AreEqual( + EXPECTED_CLEAR_TEXT_V3_HASSYMPTOM, + model.ClearText + ); + + Assert.AreEqual(dummyDeviceVerificationPayload, model.DeviceToken); + Assert.AreEqual( + EXPECTED_TRANSACTION_ID_SEED_V3_HASSYMPTOM, + model.TransactionIdSeed + ); + } + + [TestMethod] + public void DeviceVerificationTest_NoSymptom() { var platform = "Android"; var dummyDiagnosisKeyDataList = new[] { @@ -117,9 +140,10 @@ public void DeviceVerificationTest() // preparation var model = new V3DiagnosisSubmissionParameter() { + HasSymptom = false, Platform = platform, Regions = dummyRegions, - SymptomOnsetDate = dummySymptomOnsetDate, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, Keys = dummyDiagnosisKeyDataList, DeviceVerificationPayload = dummyDeviceVerificationPayload, AppPackageName = dummyAppPackageName, @@ -129,13 +153,13 @@ public void DeviceVerificationTest() Assert.AreEqual(dummyDeviceVerificationPayload, model.JwsPayload); Assert.AreEqual( - EXPECTED_CLEAR_TEXT_V3, + EXPECTED_CLEAR_TEXT_V3_NOSYMPTOM, model.ClearText ); Assert.AreEqual(dummyDeviceVerificationPayload, model.DeviceToken); Assert.AreEqual( - EXPECTED_TRANSACTION_ID_SEED_V3, + EXPECTED_TRANSACTION_ID_SEED_V3_NOSYMPTOM, model.TransactionIdSeed ); } @@ -164,9 +188,10 @@ public void DeviceVerificationTest_NoKey() // preparation var model = new V3DiagnosisSubmissionParameter() { + HasSymptom = true, Platform = platform, Regions = dummyRegions, - SymptomOnsetDate = dummySymptomOnsetDate, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, Keys = dummyDiagnosisKeyDataList, DeviceVerificationPayload = dummyDeviceVerificationPayload, AppPackageName = dummyAppPackageName, @@ -210,9 +235,10 @@ public void DeviceVerificationTestKeysNull() // preparation var model = new V3DiagnosisSubmissionParameter() { + HasSymptom = true, Platform = platform, Regions = dummyRegions, - SymptomOnsetDate = dummySymptomOnsetDate, + OnsetOfSymptomOrTestDate = dummySymptomOnsetDate, Keys = dummyDiagnosisKeyDataList, DeviceVerificationPayload = dummyDeviceVerificationPayload, AppPackageName = dummyAppPackageName, diff --git a/src/Covid19Radar.Api.Tests/Services/TemporaryExposureKeyValidationServiceTest.cs b/src/Covid19Radar.Api.Tests/Services/TemporaryExposureKeyValidationServiceTest.cs new file mode 100644 index 000000000..4a74fe9f6 --- /dev/null +++ b/src/Covid19Radar.Api.Tests/Services/TemporaryExposureKeyValidationServiceTest.cs @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +using System; +using Covid19Radar.Api.Extensions; +using Covid19Radar.Api.Models; +using Covid19Radar.Api.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Covid19Radar.Api.Tests.Services +{ + [TestClass] + [TestCategory("Services")] + public class TemporaryExposureKeyValidationServiceTest + { + [DataTestMethod] + + // KeyData + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 0, 1, 0, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ=", 0, 144, 0, 1, 0, -20, +20, -20, +20, false)] + [DataRow(false, "VE9PT09PT09PT09PTE9OR0tFWQ==", 0, 144, 0, 1, 0, -20, +20, -20, +20, false)] + [DataRow(false, " ", 0, 144, 0, 1, 0, -20, +20, -20, +20, false)] + [DataRow(false, null, 0, 144, 0, 1, 0, -20, +20, -20, +20, false)] + [DataRow(false, "", 0, 144, 0, 1, 0, -20, +20, -20, +20, false)] + + // rollingStartNumber + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 1, 144, 0, 1, 0, -20, +20, -20, +20, false)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", -1, 144, 0, 1, 0, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", -15, 144, 0, 1, 0, -20, +20, -20, +20, false)] + + // rollingPeriod + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 0, 1, 0, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 145, 0, 1, 0, -20, +20, -20, +20, false)] + + // TransmissionRisk + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, 0, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 8, 5, 0, -20, +20, -20, +20, false)] + + // ReportType + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, 0, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 6, 0, -20, +20, -20, +20, false)] + + // DaysSinceOnsetOfSymptoms + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +14, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +15, -20, +20, -20, +20, false)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -14, -20, +20, -20, +20, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -15, -20, +20, -20, +20, false)] + + // DaysSinceOnsetOfSymptom + [DataRow(true, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -4, -4, +10, -3, +7, true)] + [DataRow(true, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -5, -4, +10, -6, +7, false)] + [DataRow(true, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +10, -4, +10, -3, +9, true)] + [DataRow(true, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +11, -4, +10, -6, +12, false)] + + // DaysSinceOnsetOfDiagnosis + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -3, -2, +10, -3, +7, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, -4, -4, +10, -3, +7, false)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +10, -4, +9, -3, +10, true)] + [DataRow(false, "IVb9YMEnlTzsReDYUmZxQQ==", 0, 144, 7, 5, +11, -4, +12, -6, +10, false)] + public void Test( + bool hasSymptom, + string keyData, + int rollingStartNumberDayOffset, + int rollingPeriod, + int transmissionRisk, + int reportType, + int daysSinceOnsetOfSymptoms, + int infectiousFilterDaysSinceOnsetOfSymptomsFrom, + int infectiousFilterDaysSinceOnsetOfSymptomsTo, + int infectiousFilterDaysSinceTestFrom, + int infectiousFilterDaysSinceTestTo, + bool expectedResult + ) + { + // arrange + var config = new Mock(); + config.Setup(x => x["InfectiousFilterDaysSinceOnsetOfSymptomsFrom"]).Returns($"{infectiousFilterDaysSinceOnsetOfSymptomsFrom}"); + config.Setup(x => x["InfectiousFilterDaysSinceOnsetOfSymptomsTo"]).Returns($"{infectiousFilterDaysSinceOnsetOfSymptomsTo}"); + config.Setup(x => x["InfectiousFilterDaysSinceTestFrom"]).Returns($"{infectiousFilterDaysSinceTestFrom}"); + config.Setup(x => x["InfectiousFilterDaysSinceTestTo"]).Returns($"{infectiousFilterDaysSinceTestTo}"); + + var logger = new Mock.LoggerMock(); + + var service = new TemporaryExposureKeyValidationService( + config.Object, + logger + ); + + bool result = service.Validate(hasSymptom, new V3DiagnosisSubmissionParameter.Key() + { + KeyData = keyData, + RollingStartNumber = DateTime.UtcNow.Date.AddDays(rollingStartNumberDayOffset).ToRollingStartNumber(), + RollingPeriod = (uint)rollingPeriod, + TransmissionRisk = transmissionRisk, + ReportType = (uint)reportType, + DaysSinceOnsetOfSymptoms = daysSinceOnsetOfSymptoms, + }); + + Assert.AreEqual(expectedResult, result); + } + } +} diff --git a/src/Covid19Radar.Api.Tests/V3DiagnosisApiTest.cs b/src/Covid19Radar.Api.Tests/V3DiagnosisApiTest.cs index fff549260..7069bdcff 100644 --- a/src/Covid19Radar.Api.Tests/V3DiagnosisApiTest.cs +++ b/src/Covid19Radar.Api.Tests/V3DiagnosisApiTest.cs @@ -39,12 +39,14 @@ public void CreateMethod() var validationServer = new Mock(); var deviceCheck = new Mock(); var verification = new Mock(); + var temporaryExposureKeyValidationService = new Mock(); var logger = new Mock.LoggerMock(); var diagnosisApi = new V3DiagnosisApi(config.Object, tekRepo.Object, deviceCheck.Object, verification.Object, validationServer.Object, + temporaryExposureKeyValidationService.Object, logger); } @@ -85,12 +87,17 @@ HttpStatusCode expectedStatusCode var deviceCheck = new Mock(); deviceCheck.Setup(_ => _.Validation(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(isValidDevice); var verification = new Mock(); + + var temporaryExposureKeyValidationService = new Mock(); + temporaryExposureKeyValidationService.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(true); + var logger = new Mock.LoggerMock(); var diagnosisApi = new V3DiagnosisApi(config.Object, tekRepo.Object, deviceCheck.Object, verification.Object, validationServer.Object, + temporaryExposureKeyValidationService.Object, logger); var context = new Mock(); var keydata = new byte[KEY_LENGTH]; @@ -101,7 +108,8 @@ HttpStatusCode expectedStatusCode var bodyJson = new V3DiagnosisSubmissionParameter() { - SymptomOnsetDate = dateTime.ToString(Constants.FORMAT_TIMESTAMP), + HasSymptom = true, + OnsetOfSymptomOrTestDate = dateTime.ToString(Constants.FORMAT_TIMESTAMP), VerificationPayload = verificationPayload, Regions = new[] { region }, Platform = platform, @@ -188,12 +196,17 @@ HttpStatusCode expectedStatusCode var deviceCheck = new Mock(); deviceCheck.Setup(_ => _.Validation(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(isValidDevice); var verification = new Mock(); + + var temporaryExposureKeyValidationService = new Mock(); + temporaryExposureKeyValidationService.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(true); + var logger = new Mock.LoggerMock(); var diagnosisApi = new V3DiagnosisApi(config.Object, tekRepo.Object, deviceCheck.Object, verification.Object, validationServer.Object, + temporaryExposureKeyValidationService.Object, logger); var context = new Mock(); var keydata = new byte[KEY_LENGTH]; @@ -204,7 +217,8 @@ HttpStatusCode expectedStatusCode var bodyJson = new V3DiagnosisSubmissionParameter() { - SymptomOnsetDate = dateTime.ToString(Constants.FORMAT_TIMESTAMP), + HasSymptom = true, + OnsetOfSymptomOrTestDate = dateTime.ToString(Constants.FORMAT_TIMESTAMP), VerificationPayload = verificationPayload, Regions = new[] { region }, Platform = platform, @@ -259,24 +273,25 @@ HttpStatusCode expectedStatusCode V3DiagnosisSubmissionParameter resultParameter = JsonConvert.DeserializeObject(okObjectResult.Value.ToString()); - // Expect 2 items filtered. - Assert.AreEqual(15, resultParameter.Keys.Count()); + Assert.AreEqual(17, resultParameter.Keys.Count()); - Assert.AreEqual(-7, resultParameter.Keys[0].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-6, resultParameter.Keys[1].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-5, resultParameter.Keys[2].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-4, resultParameter.Keys[3].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-3, resultParameter.Keys[4].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-2, resultParameter.Keys[5].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(-1, resultParameter.Keys[6].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(0, resultParameter.Keys[7].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(1, resultParameter.Keys[8].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(2, resultParameter.Keys[9].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(3, resultParameter.Keys[10].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(4, resultParameter.Keys[11].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(5, resultParameter.Keys[12].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(6, resultParameter.Keys[13].DaysSinceOnsetOfSymptoms); - Assert.AreEqual(7, resultParameter.Keys[14].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-8, resultParameter.Keys[0].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-7, resultParameter.Keys[1].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-6, resultParameter.Keys[2].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-5, resultParameter.Keys[3].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-4, resultParameter.Keys[4].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-3, resultParameter.Keys[5].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-2, resultParameter.Keys[6].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(-1, resultParameter.Keys[7].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(0, resultParameter.Keys[8].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(1, resultParameter.Keys[9].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(2, resultParameter.Keys[10].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(3, resultParameter.Keys[11].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(4, resultParameter.Keys[12].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(5, resultParameter.Keys[13].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(6, resultParameter.Keys[14].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(7, resultParameter.Keys[15].DaysSinceOnsetOfSymptoms); + Assert.AreEqual(8, resultParameter.Keys[16].DaysSinceOnsetOfSymptoms); } else { diff --git a/src/Covid19Radar.Api/Extensions/IConfigurationExtension.cs b/src/Covid19Radar.Api/Extensions/IConfigurationExtension.cs index b549dd01f..442ce6171 100644 --- a/src/Covid19Radar.Api/Extensions/IConfigurationExtension.cs +++ b/src/Covid19Radar.Api/Extensions/IConfigurationExtension.cs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +using Covid19Radar.Api.Common; using Microsoft.Extensions.Configuration; namespace Covid19Radar.Api @@ -38,6 +39,39 @@ public static bool iOSDeviceValidationEnabled(this IConfiguration config) public static string VerificationPayloadApiSecret(this IConfiguration config) => config["VerificationPayloadApiSecret"]; public static string VerificationPayloadPfx(this IConfiguration config) => config["VerificationPayloadPfx"]; public static string VerificationPayloadUrl(this IConfiguration config) => config["VerificationPayloadUrl"]; + + public static int InfectiousFilterDaysSinceOnsetOfSymptomsFrom(this IConfiguration config) + { + if (int.TryParse(config["InfectiousFilterDaysSinceOnsetOfSymptomsFrom"], out int result)) + { + return result; + } + return Constants.MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS; + } + public static int InfectiousFilterDaysSinceOnsetOfSymptomsTo(this IConfiguration config) + { + if (int.TryParse(config["InfectiousFilterDaysSinceOnsetOfSymptomsTo"], out int result)) + { + return result; + } + return Constants.MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS; + } + public static int InfectiousFilterDaysSinceTestFrom(this IConfiguration config) + { + if (int.TryParse(config["InfectiousFilterDaysSinceTestFrom"], out int result)) + { + return result; + } + return Constants.MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS; + } + public static int InfectiousFilterDaysSinceTestTo(this IConfiguration config) + { + if (int.TryParse(config["InfectiousFilterDaysSinceTestTo"], out int result)) + { + return result; + } + return Constants.MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS; + } } - + } diff --git a/src/Covid19Radar.Api/Models/V3DiagnosisSubmissionParameter.cs b/src/Covid19Radar.Api/Models/V3DiagnosisSubmissionParameter.cs index 6e4431613..ff1fe0005 100644 --- a/src/Covid19Radar.Api/Models/V3DiagnosisSubmissionParameter.cs +++ b/src/Covid19Radar.Api/Models/V3DiagnosisSubmissionParameter.cs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using Covid19Radar.Api.Common; -using Covid19Radar.Api.Extensions; using Newtonsoft.Json; using System; using System.Linq; @@ -12,20 +11,13 @@ namespace Covid19Radar.Api.Models { public class V3DiagnosisSubmissionParameter : IPayload, IDeviceVerification { - /* - * [Important] - * The value `daysSinceOnsetOfSymptoms` must be less than or equal to `+14` and greater than or equal to `-14`. - * - * If any diagnosis-keys file CONTAMINATED by out of range value(e.g. -199, 62) that provide detectExposure/provideDiagnosisKeys method, - * ExposureNotification API for Android doesn't return any result(ExposureDetected/ExposureNotDetected) to BroadcastReceiver. - */ - private const int MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS = -14; - private const int MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS = 14; + [JsonProperty("hasSymptom")] + public bool HasSymptom { get; set; } // RFC3339 // e.g. 2021-09-20T23:52:57.436+00:00 - [JsonProperty("symptomOnsetDate")] - public string SymptomOnsetDate { get; set; } + [JsonProperty("onsetOfSymptomOrTestDate")] + public string OnsetOfSymptomOrTestDate { get; set; } [JsonProperty("keys")] public Key[] Keys { get; set; } @@ -74,10 +66,18 @@ public string DeviceToken [JsonIgnore] public string TransactionIdSeed - => SymptomOnsetDate - +AppPackageName - + KeysTextForDeviceVerification - + IAndroidDeviceVerification.GetRegionString(Regions); + { + get + { + var hasSymptom = HasSymptom ? "HasSymptom" : "NoSymptom"; + return + AppPackageName + + OnsetOfSymptomOrTestDate + + hasSymptom + + KeysTextForDeviceVerification + + IAndroidDeviceVerification.GetRegionString(Regions); + } + } #endregion @@ -89,7 +89,13 @@ public string JwsPayload [JsonIgnore] public string ClearText - => string.Join("|", SymptomOnsetDate, AppPackageName, KeysTextForDeviceVerification, IAndroidDeviceVerification.GetRegionString(Regions), VerificationPayload); + { + get + { + var hasSymptom = HasSymptom ? "HasSymptom" : "NoSymptom"; + return string.Join("|", AppPackageName, OnsetOfSymptomOrTestDate, hasSymptom, KeysTextForDeviceVerification, IAndroidDeviceVerification.GetRegionString(Regions), VerificationPayload); + } + } #endregion @@ -131,31 +137,6 @@ public TemporaryExposureKeyModel ToModel() Exported = false }; } - /// - /// Validation - /// - /// true if valid - public bool IsValid() - { - if (string.IsNullOrWhiteSpace(KeyData)) return false; - if (RollingPeriod > Constants.ActiveRollingPeriod) return false; - - var dateTime = DateTime.UtcNow.Date; - var todayRollingStartNumber = dateTime.ToRollingStartNumber(); - - var oldestRollingStartNumber = dateTime.AddDays(Constants.OutOfDateDays).ToRollingStartNumber(); - if (RollingStartNumber < oldestRollingStartNumber || RollingStartNumber > todayRollingStartNumber) - { - return false; - } - - if (DaysSinceOnsetOfSymptoms < MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS - || DaysSinceOnsetOfSymptoms > MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS) - { - return false; - } - return true; - } public string GetKeyString() => string.Join(".", KeyData, RollingStartNumber, RollingPeriod, ReportType); } @@ -176,10 +157,10 @@ public virtual bool IsValid() public void SetDaysSinceOnsetOfSymptoms() { - var symptomOnsetDate = DateTime.ParseExact(SymptomOnsetDate, Constants.FORMAT_TIMESTAMP, null).ToUniversalTime().Date; + var onsetOfSymptomOrTestDate = DateTime.ParseExact(OnsetOfSymptomOrTestDate, Constants.FORMAT_TIMESTAMP, null).ToUniversalTime().Date; foreach (var key in Keys) { - var dateOffset = key.GetDate() - symptomOnsetDate; + var dateOffset = key.GetDate() - onsetOfSymptomOrTestDate; key.DaysSinceOnsetOfSymptoms = dateOffset.Days; } } diff --git a/src/Covid19Radar.Api/Services/TemporaryExposureKeyValidationService.cs b/src/Covid19Radar.Api/Services/TemporaryExposureKeyValidationService.cs new file mode 100644 index 000000000..2cd038826 --- /dev/null +++ b/src/Covid19Radar.Api/Services/TemporaryExposureKeyValidationService.cs @@ -0,0 +1,145 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +using System; +using Covid19Radar.Api.Common; +using Covid19Radar.Api.Extensions; +using Covid19Radar.Api.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Covid19Radar.Api.Services +{ + public interface ITemporaryExposureKeyValidationService + { + public bool Validate(bool hasSymptom, V3DiagnosisSubmissionParameter.Key key); + } + + public class TemporaryExposureKeyValidationService : ITemporaryExposureKeyValidationService + { + private readonly int _infectiousFilterDaysSinceOnsetOfSymptomsFrom; + private readonly int _infectiousFilterDaysSinceOnsetOfSymptomsTo; + + private readonly int _infectiousFilterDaysSinceTestFrom; + private readonly int _infectiousFilterDaysSinceTestTo; + + private readonly ILogger _logger; + + public TemporaryExposureKeyValidationService( + IConfiguration configuration, + ILogger logger + ) + { + _infectiousFilterDaysSinceOnsetOfSymptomsFrom = configuration.InfectiousFilterDaysSinceOnsetOfSymptomsFrom(); + _infectiousFilterDaysSinceOnsetOfSymptomsTo = configuration.InfectiousFilterDaysSinceOnsetOfSymptomsTo(); + + _infectiousFilterDaysSinceTestFrom = configuration.InfectiousFilterDaysSinceTestFrom(); + _infectiousFilterDaysSinceTestTo = configuration.InfectiousFilterDaysSinceTestTo(); + + _logger = logger; + } + + public bool Validate(bool hasSymptom, V3DiagnosisSubmissionParameter.Key key) + { + if (string.IsNullOrWhiteSpace(key.KeyData)) + { + _logger.LogWarning("key.KeyData is null or whiteSpace."); + return false; + } + + // The devices generate the 16-byte Temporary Exposure Key. + // https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf/ + try + { + byte[] keyDataBytes = Convert.FromBase64String(key.KeyData); + if (keyDataBytes.Length != 16) + { + _logger.LogWarning("key.KeyData length must be 16 bytes."); + return false; + } + } + catch (FormatException) + { + _logger.LogError($"Convert.FromBase64String FormatException occurred. {key.KeyData}"); + return false; + } + catch (Exception) + { + _logger.LogError($"Convert.FromBase64String Exception occurred. {key.KeyData}"); + return false; + } + + if (key.RollingPeriod > Constants.ActiveRollingPeriod) + { + _logger.LogWarning($"key.RollingPeriod must be less or equal 144 but {key.RollingPeriod}"); + return false; + } + + // https://developer.apple.com/documentation/exposurenotification/enexposureinfo/3583716-transmissionrisklevel + if (key.TransmissionRisk < 0 || key.TransmissionRisk > 7) + { + _logger.LogWarning($"key.RollingPeriod must be int 0 to 7 but {key.TransmissionRisk}"); + return false; + } + + // https://developer.apple.com/documentation/exposurenotification/endiagnosisreporttype + if (key.ReportType < 0 || key.ReportType > 5) + { + _logger.LogWarning($"key.ReportType must be int 0 to 5 but {key.ReportType}"); + return false; + } + + var dateTime = DateTime.UtcNow.Date; + var todayRollingStartNumber = dateTime.ToRollingStartNumber(); + + var oldestRollingStartNumber = dateTime.AddDays(Constants.OutOfDateDays).ToRollingStartNumber(); + + if (key.RollingStartNumber < oldestRollingStartNumber) + { + _logger.LogWarning("key.RollingStartNumber must be a date newer than 14 days ago"); + return false; + } + if (key.RollingStartNumber > todayRollingStartNumber) + { + _logger.LogWarning("key.RollingStartNumber must be a date older than today"); + return false; + } + + if (key.DaysSinceOnsetOfSymptoms < Constants.MIN_DAYS_SINCE_ONSET_OF_SYMPTOMS + || key.DaysSinceOnsetOfSymptoms > Constants.MAX_DAYS_SINCE_ONSET_OF_SYMPTOMS + ) + { + _logger.LogWarning("key.DaysSinceOnsetOfSymptoms must be in -14 to 14 but {key.DaysSinceOnsetOfSymptoms}"); + return false; + } + + if (hasSymptom) + { + _logger.LogDebug("hasSymptom"); + + if (_infectiousFilterDaysSinceOnsetOfSymptomsFrom > key.DaysSinceOnsetOfSymptoms + || _infectiousFilterDaysSinceOnsetOfSymptomsTo < key.DaysSinceOnsetOfSymptoms + ) + { + _logger.LogInformation($"key.DaysSinceOnsetOfSymptoms must be in {_infectiousFilterDaysSinceOnsetOfSymptomsFrom} to {_infectiousFilterDaysSinceOnsetOfSymptomsTo} but {key.DaysSinceOnsetOfSymptoms}"); + return false; + } + } + else + { + _logger.LogDebug("diagnosis"); + + if (_infectiousFilterDaysSinceTestFrom > key.DaysSinceOnsetOfSymptoms + || _infectiousFilterDaysSinceTestTo < key.DaysSinceOnsetOfSymptoms + ) + { + _logger.LogInformation($"key.DaysSinceOnsetOfSymptoms must be in {_infectiousFilterDaysSinceTestFrom} to {_infectiousFilterDaysSinceTestTo} but {key.DaysSinceOnsetOfSymptoms}"); + return false; + } + } + + return true; + } + } +} diff --git a/src/Covid19Radar.Api/Startup.cs b/src/Covid19Radar.Api/Startup.cs index 8b83f9ef8..e2c243138 100644 --- a/src/Covid19Radar.Api/Startup.cs +++ b/src/Covid19Radar.Api/Startup.cs @@ -33,6 +33,7 @@ public override void Configure(IFunctionsHostBuilder builder) builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); } } } diff --git a/src/Covid19Radar.Api/V3DiagnosisApi.cs b/src/Covid19Radar.Api/V3DiagnosisApi.cs index b28a9a1a9..0a67018df 100644 --- a/src/Covid19Radar.Api/V3DiagnosisApi.cs +++ b/src/Covid19Radar.Api/V3DiagnosisApi.cs @@ -38,6 +38,7 @@ public class V3DiagnosisApi private readonly IDeviceValidationService _deviceValidationService; private readonly IVerificationService _verificationService; private readonly IValidationServerService _validationServerService; + private readonly ITemporaryExposureKeyValidationService _temporaryExposureKeyValidationService; private readonly ILogger _logger; @@ -47,6 +48,7 @@ public class V3DiagnosisApi IDeviceValidationService deviceValidationService, IVerificationService verificationService, IValidationServerService validationServerService, + ITemporaryExposureKeyValidationService temporaryExposureKeyValidationService, ILogger logger ) { @@ -56,6 +58,7 @@ ILogger logger _deviceValidationService = deviceValidationService; _verificationService = verificationService; _validationServerService = validationServerService; + _temporaryExposureKeyValidationService = temporaryExposureKeyValidationService; _logger = logger; } @@ -88,7 +91,9 @@ ILogger logger } // Filter valid keys - submissionParameter.Keys = submissionParameter.Keys.Where(key => key.IsValid()).ToArray(); + submissionParameter.Keys = submissionParameter.Keys + .Where(key => _temporaryExposureKeyValidationService.Validate(submissionParameter.HasSymptom, key)) + .ToArray(); var reqTime = DateTimeOffset.UtcNow; @@ -153,6 +158,10 @@ ILogger logger await _tekRepository.UpsertAsync(key); } + // Clear Payloads + submissionParameter.VerificationPayload = null; + submissionParameter.DeviceVerificationPayload = null; + return new OkObjectResult(JsonConvert.SerializeObject(submissionParameter)); } } diff --git a/src/Covid19Radar.Api/local.settings.json.example b/src/Covid19Radar.Api/local.settings.json.example index 4ceb5d9af..e271f5b23 100644 --- a/src/Covid19Radar.Api/local.settings.json.example +++ b/src/Covid19Radar.Api/local.settings.json.example @@ -31,6 +31,10 @@ "iOSDeviceCheckPrivateKey": null, "iOSDeviceCheckTeamId": null, "iOSDeviceValidationEnabled": false, - "InquiryLogApiKey": "EXAMPLE" + "InquiryLogApiKey": "EXAMPLE", + "InfectiousFilterDaysSinceOnsetOfSymptomsFrom": -3, + "InfectiousFilterDaysSinceOnsetOfSymptomsTo": 10, + "InfectiousFilterDaysSinceTestFrom": -3, + "InfectiousFilterDaysSinceTestTo": 7 } } \ No newline at end of file