From 91ee310ded06e76714758f2531cb8f7437b57c0f Mon Sep 17 00:00:00 2001 From: Thomas Klingbeil Date: Mon, 27 Sep 2021 09:34:44 +0200 Subject: [PATCH] Update solution architecture document --- .../exposure_windows.svg | 3 + images/solution_architecture/figure_1.svg | 901 ------------------ images/solution_architecture/figure_12.svg | 3 - images/solution_architecture/figure_13.svg | 3 - images/solution_architecture/figure_5.svg | 3 - .../high_level_architecture.svg | 3 + images/solution_architecture/ios_releases.svg | 3 + images/solution_architecture/rat_process.svg | 3 + .../risk_calculation.svg/image2.pdf | Bin 0 -> 32875 bytes .../risk_calculation.svg/risk_calculation.svg | 3 + images/solution_architecture/trl_mapping.svg | 3 + .../solution_architecture/upload_schedule.svg | 3 + overview-security.md | 2 +- solution_architecture.md | 141 +-- 14 files changed, 98 insertions(+), 976 deletions(-) create mode 100644 images/solution_architecture/exposure_windows.svg delete mode 100644 images/solution_architecture/figure_1.svg delete mode 100644 images/solution_architecture/figure_12.svg delete mode 100644 images/solution_architecture/figure_13.svg delete mode 100644 images/solution_architecture/figure_5.svg create mode 100644 images/solution_architecture/high_level_architecture.svg create mode 100644 images/solution_architecture/ios_releases.svg create mode 100644 images/solution_architecture/rat_process.svg create mode 100644 images/solution_architecture/risk_calculation.svg/image2.pdf create mode 100644 images/solution_architecture/risk_calculation.svg/risk_calculation.svg create mode 100644 images/solution_architecture/trl_mapping.svg create mode 100644 images/solution_architecture/upload_schedule.svg diff --git a/images/solution_architecture/exposure_windows.svg b/images/solution_architecture/exposure_windows.svg new file mode 100644 index 0000000..08beccd --- /dev/null +++ b/images/solution_architecture/exposure_windows.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-25 09:36:45 +0000Ideas 2Ebene 1RealitySeconds sincelast scanintervals without results for that devicetimeattenuation (dB)weighted timeactual timeVisible through APIand interpretation„gaps“ from scan intervals without results for that device are closedtimeattenuation (dB) diff --git a/images/solution_architecture/figure_1.svg b/images/solution_architecture/figure_1.svg deleted file mode 100644 index af1442c..0000000 --- a/images/solution_architecture/figure_1.svg +++ /dev/null @@ -1,901 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Produced by OmniGraffle 7.17.5\n2020-10-16 16:08:56 +0000 - - high-level-overview - - - Layer 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Figure 1: High-level architecture overview - - - - - - - Mobile Phone - Apple iPhone or Android phone - - - - - - - Bluetooth - Low - Energy - (BLE) - Hardware - Interface - - - - - - - Exposure - Notification - Framework - - - - - - - Corona-Warn-App (CWA) - Test result retrieval and exposure - notification (tracing) - (Apple iOS and Google Android) - - - - - - - Open Source - - - - - - - Existing Solutions - - - - - - - - - - - R - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Exposure - Notification Data - - - - - - - - - - - - - - - Mobile Phone - - - - - - - BLE Beacon Mechanics - (No active connection - - - - - - - - - - - - - - - - - - - - - - - - CWA - - - - - - - Mobile Phone - - - - - - - CWA - - - - - - - Mobile Phone - - - - - - - CWA - - - - - - - - - - - - - - Broadcasting - - - - - Broadcasting of RPI - encrypted metadata - - - - - Scanning - - - - - - - Laboratory Information - System (LIS) - - - - - - - Health - Authority - - - - - - - Hotline - - - - - - - Test Result Server - - - - - - - Portal Server - - - - - - - Verification Server - - - - - - - Corona-Warn-App - Server - - - - - - - European Federation - Gateway - - - - - - - - - - - - - - - Content Delivery - Network (CDN) - - - - - - - - - - R - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - - - Verification - of TAN - - - - - - - - - - - R - - - - - - - - - - - - - - - - - Aggregated keys + - Configuration - - - - - - - - - - R - - - - - - - - - - - - - - - - R - - - - - - - - - - - - - - - - - - - - - - - Upload/Download - of keys - - - - - Callback - Notifications - - - - - Retrieve - Results - - - - - - - - - - R - - - - - - - - - - - - - - - - Upload of keys - (+TAN for Verification) - - - - - - - - - - R - - - - - - - - - - - - - - - - Download of keys and - configuration - - - - - - - - - - R - - - - - - - - - - - - - - - - Retrieve results - + TAN - - - - - Scanning - - - - - diff --git a/images/solution_architecture/figure_12.svg b/images/solution_architecture/figure_12.svg deleted file mode 100644 index 7452eea..0000000 --- a/images/solution_architecture/figure_12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - Produced by OmniGraffle 6.6.2 2020-06-05 16:10:19 +0000Figure 12Ebene 1123456780-80-80-80-80-80-80-80-8>= 14 days12-13 days10-11 days8-9 days6-7 days4-5 days2-3 days0-1 days0-80-80-80-80-80-80-80-8D = 0D <= 55 < D <= 1010 < D <= 1515 < D <= 2020 < D <= 2525 < D <= 30D > 300-80-80-80-80-80-80-80-8A > 7373 >= A > 6363 >= A > 5151 >= A > 3333 >= A > 2323 >= A > 1515 >= A > 10A <= 100-80-80-80-80-80-80-80-8Lower riskHigher riskMore time since exposureLess time since exposureLower contact durationHigher contact durationHigher attenuation (probably higher distance)Lower attenuation (likely lower distance)Transmission Risk factor (meaning defined by app), use is optionalDays since exposureExposure duration (minutes)Signal attenuationDaysRiskExposureRiskAttenuationRiskTransmission Risk====xxx=Total RiskInput:Input:Input:Input:Figure 12: Risk Calculation diff --git a/images/solution_architecture/figure_13.svg b/images/solution_architecture/figure_13.svg deleted file mode 100644 index cf0a629..0000000 --- a/images/solution_architecture/figure_13.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - Produced by OmniGraffle 6.6.2 2020-06-08 11:05:07 +0000Figure 13Ebene 1attenuation < low timexweight_3ExposureScore=xFigure 13: Calculation of the combined riskhigh > attenuation >= low timexweight_2attenuation > high timexweight_1Lower riskHigher risk++(defaultBucketOffset)weight_4+defined by the application Normalizing RiskFrom Apple/Google calculationMaximum Total Risk Score/=Normalized Total Risk Score=Combined Risk Score diff --git a/images/solution_architecture/figure_5.svg b/images/solution_architecture/figure_5.svg deleted file mode 100644 index d6859e5..0000000 --- a/images/solution_architecture/figure_5.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - Produced by OmniGraffle 6.6.2 2020-05-17 06:27:37 +0000Figure 5Ebene 1positivetest(1) keys uploaded immediately Uploads of Diagnosis Keys(3) user is still considered contagiousnegativetest(2)currently not uploadedFigure 5: Upload schedule for Temporary Exposure Keys (Diagnosis Keys) diff --git a/images/solution_architecture/high_level_architecture.svg b/images/solution_architecture/high_level_architecture.svg new file mode 100644 index 0000000..0a7c012 --- /dev/null +++ b/images/solution_architecture/high_level_architecture.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-24 08:44:05 +0000Arbeitsfläche 6Ebene 1Mobile PhoneCWAMobile PhoneCWAMobile PhoneApple iPhone or Android phoneRetrieve results+ TANCorona-Warn-App (CWA)Test result retrieval and exposure notification (tracing)(Apple iOS and Google Android)Verificationof TAN Exposure NotificationFrameworkExposure NotificationDataRBroadcastingof RPI andencrypted metadata ScanningBLE Beacon Mechanics(No active connection)ScanningBroadcastingBluetoothLowEnergy(BLE)Hardware InterfaceVerification ServerCorona-Warn-App ServerDownload of keys, configuration and statistics RCDNCDNContent Delivery Network (CDN)RRUpload of keys (ENF + Events)(+TAN for verification)Aggregated keys+ configuration+ statisticsLaboratory Information System (LIS)Portal ServerHotlineRRRRRRetrieve resultsMobile PhoneCWAOpen SourceExisting SolutionsFigure 1: High-level architecture overview(24.09.2021 10:45)Test Result ServerREuropean Federation Gateway Service(EFGS)RRSwiss Key Exchange ServerRRR(Both)Upload German Diagnosis KeysRDownloadEU keys(Callback upon availability)DownkoadSwiss keys(Callback upon availability)Data Donation ServerContact JournalEntriesRVoluntarily share Privacy preserving Analytics (PPA)Collected usage statistics optional)Apple Device CheckGoogle Safety Net ServiceRWhen using PPA on AndroidGenerate teleTANCWA Analytics ServerRMake general statistics availableRRRCollect data frommultiple sourcesGeneral statisticsExternal Covid-19Data sourcesRLab DashboardRfetch technical result transmission statistics Event KeysDiagnosis Keys (ENF)Event CheckinsR(Android)Safety Net Attestation APIRDigital Covid Certificate (DCC) ServerCovPass Issuer ServerRetrieve digital certificateRR diff --git a/images/solution_architecture/ios_releases.svg b/images/solution_architecture/ios_releases.svg new file mode 100644 index 0000000..38ee96e --- /dev/null +++ b/images/solution_architecture/ios_releases.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-02-10 09:51:29 +0000Arbeitsfläche 1Ebene 112.012.1.x12.2.x12.3.x12.4.x (Security fixes)13.1.x13.2.x13.3.x13.4.x13.5.x13.6.x13.7.x14.0.x14.114.2.x14.314.412.5.xJanuary 2021January 2020January 2019No Exposure Notification FrameworkExposure Notification Framework Version 1Exposure Notification Framework Version 2January 2018backport of ENFto iOS 12.5End of OS support foriPhone 6, iPhone 6 Plus, iPhone SE diff --git a/images/solution_architecture/rat_process.svg b/images/solution_architecture/rat_process.svg new file mode 100644 index 0000000..2a5f014 --- /dev/null +++ b/images/solution_architecture/rat_process.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-23 13:18:20 +0000RAT processEbene 1Third Party Registration (App or Web)Validate Data,Document ConsentThird Party Backend orCWA Rapid Antigen Test PortalPersonal DataPersonal DataNameDate of BirthContact DataThird Party Application (PoC Frontend)Internal IDNameDate of BirthTest TimestampContact DataRegister for test appointmentGenerate internal ID and store dataInternal IDAppointmentPersonal DataTest ResultSubmit Registration with appointment bookingAppointmentInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultInternal IDAppointmentPersonal DataTest ResultCWA Test ID can be calculated on-the-fly from available data, as long as the salt is persistedConfirmationInternal IDAppointmentPersonal DataPoC Staff at test locationConfirmationInternal IDAppointmentPersonal DataAt time of test:Check-In at test location(e.g. scan of Third Party QR code)Load data from PoC backend/check in userConduct testEvaluate testEnter test resultInternal IDTest ResultSubmit test resultStore test resultCalculate CWA Test IDHashInternal IDTime of testPersonal DataCWA Test IDSaltProcess StepsCorona-Warn-AppCustomerDate of BirthTest TimestampCWA Test IDScan QR code/click linkCWA QR/LinkInternal IDSaltNameDate of BirthTime of testIf Personal Data is contained: Validate CWA Test ID by re-calculating it from hash (see step 4)CWA Verification ServerRegister Test hash(CWA Test ID)Registration TokenMap hash(GUID) to a new registration tokenCWA Test Result ServerCWA Test IDTest ResultSubmit to CWA infrastructureStore test result with hash(CWA Test ID)Registration TokenRegistration TokenStore Registration TokenFetch Test Resulthash(Test ID)Fetch Test ResultTest ResultTest ResultReturn Test result (if available yet)Test Result/TimestampStore Test Result and display with personal data (if available)611toCWA Registration can take place any time after the QR code/CWA link have been issued to the user and don’t influence subsequent steps.AEtoPolling for availability of test resultsSteps are repeated regularly. They start after 8-11 have been completed. Steps C-E only possible after step 22.15toInitial Registration for Test Appointment through Web Page or designated app by Third Party1215toActual Test with presence of user - after step 15 users can leave the test location1618toEvaluation of Test and submission to Third Party Backend1922toSubmission of test result toCorona-Warn-App environmentCWA Test IDNameInternal IDSalthash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration Tokenhash(CWA Test ID)Registration TokenProcess OwnersObjectData fieldObjectData fieldOptional fieldCorona-Warn-AppThird Party, e.g. Rapid Antigen Test providerBackend can be provided by Third Party or as part of the CWA infrastructureResult Timestamphash(CWA Test ID)Test ResultResult TimestampResult TimestampResult Timestamp678910212211ADE12351213141516171819BC420„Time of test“ refers to the time set in the appointment, NOT the actual time of the sample collectionPersonal data/Internal ID NOT used in creation of CWA Test ID for pseudonymous transmissionhash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult Timestamphash(CWA Test ID)Test ResultResult TimestampFigure 5: Rapid Antigen Testing process diff --git a/images/solution_architecture/risk_calculation.svg/image2.pdf b/images/solution_architecture/risk_calculation.svg/image2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b7cea1037aaed6537a8dd08edc32dfea229470ff GIT binary patch literal 32875 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 C$zZ<# literal 0 HcmV?d00001 diff --git a/images/solution_architecture/risk_calculation.svg/risk_calculation.svg b/images/solution_architecture/risk_calculation.svg/risk_calculation.svg new file mode 100644 index 0000000..471048d --- /dev/null +++ b/images/solution_architecture/risk_calculation.svg/risk_calculation.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-25 09:36:45 +00002021-08-23 Risk Calculation V2 ohne Vorfilter 3Ebene 1ConfigurationTRL MappingTransmission RiskLevel (TRL) to Value (TRV)IIIIIIIVVVIVIIVIIIExposure Notification Framework(Controlled through Parameters)App LogicDetails of the Exposure Window from the Google/Apple Exposure Notification Framework(Processed within the app)0.00.60.81.01.21.41.60.0Report TypeInfectiousnessTRL (Level) from componentsProcess all contained scan InstancesWeightedExposureTimex=>= 9Encounter(s) withHigh risk>= 5 && < 9Encounter(s) withlow riskTime t at minimum* attenuation attt(att < 63 dB) x 0.8t(att >= 63 dB && att < 73 dB) x 1.0+(seconds/minutes)* changes possibleNormalisedexposuretime (t )(per window)nAttenuation „buckets“„Close“ threshold63 dB„Medium“ threshold73 dB„Close“ weight0.8„Medium“ weight1.0Overall Risk DefinitionGreen (low risk with encounter(s))>= 5 && < 13 minutesRed (high risk)>= 13 minutesExposure Notification Framework (Apple/Google)ENF ConfigurationDSOS to InfectiousnessInfectiousness to weightReport type to weight„Positivbegegnung“exposure„Begegnung(en) mit niedrigem oder erhöhtem RisikoLow or High risk exposure(s)„Nicht-Risiko-Begegnung(en)“Non-risk exposure(s)TRL EncodingReport Type toTRL componentInfectiousness to TRL componentLast updated: 23.08.2021, 10:30Normalisedexposuretime (t )(per window)nNormalisedexposuretime (t )(per window)nNormalisedexposuretime (t )(per window)nNormalisedexposuretime (t )(per window)nNormalisedexposuretime (t )(per window)nNormalisedexposuretime (t )(per window)n„Far“ threshold79 dB„Far“ weight0.1„Very far“ weight0.0t(att >= 73 dB && att < 79 dB) x 0.1t(att >= 79 dB) x 0.0++Green (low risk)< 5 minutes< 5Low riskMappingTRL to TRVΣ(t )of all windows (i.e. all exposures)nnResult:Multiple windows with a low exposure time each can add up to a high risk.3.3 minutesStatus:High RiskΣ(t ) >= 9n5.1 minutes7.8 minutesExample: diff --git a/images/solution_architecture/trl_mapping.svg b/images/solution_architecture/trl_mapping.svg new file mode 100644 index 0000000..0d9ffce --- /dev/null +++ b/images/solution_architecture/trl_mapping.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-25 10:03:07 +0000Arbeitsfläche 10Ebene 1Report TypeInfectiousnessStandard (1)High (2)Confirmed Test1DSOSClinical Diagnosis2Self Report3Recursive4Report Type+=12345678TRLinfectiousnessForDaysSinceOnsetOfSymptoms-14-13-12-11-10-9-8-7-6-5-4-3-2-10000000000000000123456789101112131412000000000000configuration126420Keys with Report Type„Recursive“ might be dropped, however they are dropped in the calculation anyways diff --git a/images/solution_architecture/upload_schedule.svg b/images/solution_architecture/upload_schedule.svg new file mode 100644 index 0000000..0d84a94 --- /dev/null +++ b/images/solution_architecture/upload_schedule.svg @@ -0,0 +1,3 @@ + + + Produced by OmniGraffle 6.6.2 2021-09-23 13:16:01 +0000Upload ScheduleEbene 1positivetest(1) keys uploaded immediately Uploads of Diagnosis Keys(2) user is still considered contagiousnegativetestnot uploadedFigure 6: Upload schedule for Temporary Exposure Keys (Diagnosis Keys) diff --git a/overview-security.md b/overview-security.md index b826f36..5d76ad2 100644 --- a/overview-security.md +++ b/overview-security.md @@ -457,7 +457,7 @@ The following chapters contain a brief introduction to each capability. - Guarantee collaboration and support by partners in case of security incident (e.g. to update or recover a system in the specified time). - Ensure controlled, monitored and minimized access for partners. - Ensure compliance with internal/external requirements (e.g. security checks). -- Cooperate with selected/certified suppliers (blacklist/whitelist). +- Cooperate with selected/certified suppliers (avoid/prefer list). ### Secure Development diff --git a/solution_architecture.md b/solution_architecture.md index 8fa87b7..68ec367 100644 --- a/solution_architecture.md +++ b/solution_architecture.md @@ -13,7 +13,8 @@ We assume a close association of a mobile phone and its user and, thus, equate t ## TABLE OF CONTENTS 1. [INTRODUCTION](#introduction) - 1. [Retrieval of lab results and verification process](#retrieval-of-lab-results-and-verification-process) + 1. [Retrieval of lab results and verification process](#pcr-tests-retrieval-of-lab-results-and-verification-process) + 2. [Rapid Antigen Tests: Result Retrieval](#rapid-antigen-tests-result-retrieval) 2. [Upload schedule for Diagnosis Keys](#upload-schedule-for-diagnosis-keys) 2. [BACKEND](#backend) 1. [Data format](#data-format) @@ -30,27 +31,27 @@ We assume a close association of a mobile phone and its user and, thus, equate t ## INTRODUCTION -To reduce the spread of [COVID-19](https://www.ecdc.europa.eu/en/covid-19-pandemic), it is necessary to inform people about their close proximity to positively tested individuals. So far, health departments and affected individuals have identified possibly infected individuals in personal conversations based on each individuals' memory. This has led to a high number of unknown connections, e.g. when using public transport. +To reduce the spread of [COVID-19](https://www.ecdc.europa.eu/en/covid-19-pandemic), it is necessary to inform people about their close proximity to individuals that have tested positive. So far, health departments and affected individuals have identified possibly infected individuals in personal conversations based on each individuals' memory. This has led to a high number of unknown connections, e.g. when using public transport. -![Figure 1: High-level architecture overview](images/solution_architecture/figure_1.svg "Figure 1: High-level architecture overview") +![Figure 1: High-level architecture overview](images/solution_architecture/high_level_architecture.svg "Figure 1: High-level architecture overview") The Corona-Warn-App (see [scoping document](https://github.com/corona-warn-app/cwa-documentation/blob/master/scoping_document.md )), shown centrally in *Figure 1*, enables individuals to trace their personal exposure risk via their mobile phones. The Corona-Warn-App uses a new framework provided by Apple and Google called [Exposure Notification Framework](https://www.apple.com/covid19/contacttracing). The framework employs [Bluetooth Low Energy (BLE)](https://en.wikipedia.org/wiki/Bluetooth_Low_Energy) mechanics. BLE lets the individual mobile phones act as beacons meaning that they constantly broadcast a temporary identifier called Rolling Proximity Identifier (RPI) that is remembered and, at the same time, lets the mobile phone scan for identifiers of other mobile phones. This is shown on the right side of *Figure 1*. Identifiers are ID numbers sent out by the mobile phones. To ensure privacy and to prevent the tracking of movement patterns of the app user, those broadcasted identifiers are only temporary and change constantly. New identifiers are derived from a Temporary Exposure Key (TEK) that is substituted at midnight (UTC) every day through means of cryptography. For a more detailed explanation, see *Figure 10*. Once a TEK is linked to a positive test result, it remains technically the same, but is then called a Diagnosis Key. -The collected identifiers from other users as well as the own keys, which can later be used to derive the identifiers, are stored locally on the phone in the secure storage of the framework provided by Apple and Google. The application cannot access this secure storage directly, but only through the interfaces the Exposure Notification Framework provides. To prevent misuse, some of these interfaces are subjected to [rate limiting](https://developer.apple.com/documentation/exposurenotification/enmanager/3586331-detectexposures). If app users are tested positively for SARS-CoV-2, they can update their status in the app by providing a verification of their test and select an option to send their recent keys from up to 14 days back. On the Corona-Warn-App back-end server, all keys of positively tested individuals are aggregated and are then made available to all mobile phones that have the app installed. Additionally, the configuration parameters for the framework are available for download, so that adjustments to the risk score calculation can be made, see the *Risk Scores* section. -Once the keys and the exposure detection configuration have been downloaded, the data is handed over to the Exposure Notification Framework, which analyzes whether one of the identifiers collected by the mobile phone matches those of a positively tested individual. Additionally, the metadata that has been broadcasted together with the identifiers such as the transmit power can now be decrypted and used. Based on the collected data, the Exposure Notification Framework provided by Apple and Google calculates a risk score for each individual exposure as well as for the overall situation. Exposures are defined as an aggregation of all encounters with another individual on a single calendar day (UTC timezone). For privacy reasons, it is not possible to track encounters with other individuals across multiple days. +The collected identifiers from other users as well as the own keys, which can later be used to derive the identifiers, are stored locally on the phone in the secure storage of the framework provided by Apple and Google. The application cannot access this secure storage directly, but only through the interfaces the Exposure Notification Framework provides. To prevent misuse, some of these interfaces are subjected to [rate limiting](https://developer.apple.com/documentation/exposurenotification/enmanager/3586331-detectexposures). If app users are tested positive for SARS-CoV-2, they can update their status in the app by providing a verification of their test and select an option to send their recent keys from up to 14 days back. On the Corona-Warn-App back-end server, all keys of individuals that have tested positive are aggregated and are then made available to all mobile phones that have the app installed. Additionally, the configuration parameters for the framework are available for download, so that adjustments to the risk score calculation can be made, see the *Risk Scores* section. +Once the keys and the exposure detection configuration have been downloaded, the data is handed over to the Exposure Notification Framework, which analyzes whether one of the identifiers collected by the mobile phone matches those of a individual that has tested positive. Additionally, the metadata that has been broadcasted together with the identifiers such as the transmit power can now be decrypted and used. Based on the collected data, the Exposure Notification Framework provided by Apple and Google groups exposures into 30 minute "exposure windows", which in turn can be used to determine the individual risk. Exposures are defined as an aggregation of all encounters with another individual on a single calendar day (UTC timezone). For privacy reasons, it is neither possible to track encounters with other individuals across multiple days, nor to link individual exposure windows to each other. -It is important to note that the persons that have been exposed to a positively tested individual are **not informed by a central instance**, but the risk of an exposure is calculated locally on their phones. The information about the exposure remains on the user’s mobile phone and is not shared. +It is important to note that the persons that have been exposed to a positive tested individual are **not informed by a central instance**, but the risk of an exposure is calculated locally on their phones. The information about the exposure remains on the user’s mobile phone and is not shared. The Corona-Warn-App pursues two objectives: -1. It supports individuals in finding out whether they have been exposed to a person that has later been tested positively. +1. It supports individuals in finding out whether they have been exposed to a person that has later been tested positive. 2. It receives the result of a SARS-CoV-2 test on a user's mobile phone through an online system. This helps reduce the time until necessary precautions, e.g. a contact reduction and testing, can be taken. -In order to prevent misuse, individuals need to provide proof that they have been tested positively before they can upload their keys. Through this integrated approach, the verification needed for the upload of the diagnosis keys does not require any further action from the users. +In order to prevent misuse, individuals need to provide proof that they have been tested positive before they can upload their keys. Through this integrated approach, the verification needed for the upload of the diagnosis keys does not require any further action from the users. They only have to confirm in the app and for the Exposure Notification Framework that they agree to share their diagnosis keys. Manual verification is also possible if the lab that performed the testing does not support the direct electronic transmission of test results to the users' mobile phones or if users have decided against the electronic transmission of their test results. -### Retrieval of Lab Results and Verification Process +### PCR tests: Retrieval of Lab Results and Verification Process Reporting positive tests to the Corona-Warn-App is crucial for informing others about a relevant exposure and potential infection. However, to prevent misuse, a verification is required before diagnosis keys can be uploaded. There are two ways for receiving this verification: @@ -62,7 +63,7 @@ There are two ways for receiving this verification: *Figure 2* and *Figure 3* illustrate the verification process. *Figure 2* shows the interaction flow of the user and *Figure 3* the flow and storage of data. Additions to the preexisting 'conventional' process through the introduction of the Corona-Warn-App and the integrated test result retrieval are marked blue in *Figure 2*. -This preexisting process for the processing of lab results includes that the doctor requesting the test also receives the results, so patients can be informed in a timely manner. As required by law ([§9 IfSG](https://www.gesetze-im-internet.de/ifsg/__9.html)), the responsible health authority (“Gesundheitsamt”) is notified by the lab about the test results as well. The notifications in case of a positive test includes, amongst others, the name, address, and date of birth of the positively tested individuals, so that they can be contacted and informed about the implications of their positive test. This is also represented in step 3 of *Figure 2*. +This preexisting process for the processing of lab results includes that the doctor requesting the test also receives the results, so patients can be informed in a timely manner. As required by law ([§9 IfSG](https://www.gesetze-im-internet.de/ifsg/__9.html)), the responsible health authority (“Gesundheitsamt”) is notified by the lab about the test results as well. The notifications in case of a positive test includes, amongst others, the name, address, and date of birth of the positive tested individuals, so that they can be contacted and informed about the implications of their positive test. This is also represented in step 3 of *Figure 2*. The flow for using the app is as follows, referencing the steps from *Figure 2*: @@ -95,17 +96,33 @@ From a privacy protection perspective, sending push notifications via Apple’s If a user did not receive a teleTAN from the health authority and/or has lost the QR code, a teleTAN needs to be retrieved from a hotline. The hotline ensures that users are permitted to perform an upload before issuing the teleTAN. It is then used as described before, starting from *Figure 4*, step 7. +### Rapid Antigen Tests: Result Retrieval + +While the PCR tests described before require a laboratory to receive the test results, Rapid Antigen Tests (RAT) can be evaluated shortly after taking the sample, at the Point of Care (PoC). Also the results for those Rapid Antigen Tests shall be transmitted to the Corona-Warn-App installed on the users' phones, so they can subsequently be used to warn others. + +As the infrastructure for Rapid Antigen Tests is more distributes in comparison to PCR tests (i.e. locally and with regards to the operators: mobile test locations at venues, workplaces, etc., ), also the infrastructure for transmitting the test results needs to operate in a distributed way. + +![Figure 5: End-to-end overview for Rapid Antigen Tests](images/solution_architecture/rat_process.svg "Figure 5: End-to-end overview for Rapid Antigen Tests") + +The overview contains two processes, one is part of the CWA scope, while the other belongs to a third party (the test provider). + +The shown process flow assumes that users schedule an appointment on the provider's infrastrucure (step 1-2), which is assigned an internal ID specific to the provider (step 3). +The backend is then able to calculate a CWA Test ID (step 4) by applying a hash algorithm. Depending on the choice of the users, personal data may or may not be used to calculate the hash. +The backend may then return a confirmation, which is then used to provide a QR-Code and/or link. With this QR-Code/link users can add the Rapid Antigen Test to their Corona-Warn-Apps. +The CWA Test ID can then be validated locally (not as a means of security, but to make sure it is a valid code) using the exact same hashing algorithm as used on the backend (step 7). +The test is then registered on the CWA infrastructure (step 8-11). The testing process itself, including the transmission to the providers backend (steps 12-20) takes place independently from the CWA infrastructure. +The test result is linked to the CWA Test ID and transmitted to the CWA infrastructure (step 21-22). + ### Upload Schedule for Diagnosis Keys -According to the current version of the documentation from Apple and Google (1.3), the first set of up to 14 Temporary Exposure Keys (TEK; called Diagnosis Keys when linked to a positive test) needs to be uploaded after the positive test result becomes available and the consent to the upload has been given (see (1) in *Figure 5*). As the TEK of the current day can still be used to generate new RPIs, it cannot be made available right away. If it was uploaded before the end of the day, malicious third parties could use it to generate valid RPIs linked to a positive test. Instead, once it is uploaded, it is replaced by a new TEK (see (2) in *Figure 5*). This upload takes place in the background and requires no additional consent as the framework grants a 24-hour grace period for the request of Diagnosis Keys. +A set of up to 15 Temporary Exposure Keys (TEK; called Diagnosis Keys when linked to a positive test) needs to be uploaded after the positive test result becomes available. The consent might have either been given when registering the test or after receiving the positive test result. +In order to prevent that the TEK of the current day can be used to generate new RPIs after the submission, it is uploaded with a shorter validity (only until the point of submission) in comparison to the other Diagnosis Keys. To make sure that malicious third parties cannot use it to generate valid RPIs linked to a positive test, uploaded keys are not published immediately, but only after a defined safety period. -![Figure 5: Upload schedule for Temporary Exposure Keys (Diagnosis Keys)](images/solution_architecture/figure_5.svg "Figure 5: Upload schedule for Temporary Exposure Keys (Diagnosis Keys)") +![Figure 6: Upload schedule for Temporary Exposure Keys (Diagnosis Keys)](images/solution_architecture/upload_schedule.svg "Figure 6: Upload schedule for Temporary Exposure Keys (Diagnosis Keys)") -As users are not required to confirm negative test results, the functionality of uploading Diagnosis Keys on subsequent days remains theoretical. Each of those uploads could take place earliest at the end of each subsequent day (see (3) in *Figure 5*). It would require explicit consent of the user for each day and could take place up to the time when the person is not considered contagious anymore (but not any longer, as this would lead to false positives). +As users are not required to confirm negative test results, the functionality of uploading Diagnosis Keys on subsequent days remains theoretical. Each of those uploads could take place earliest at the end of each subsequent day (see (2) in *Figure 6*). It would require explicit consent of the user for each day and could take place up to the time when the person is not considered contagious anymore (but not any longer, as this would lead to false positives). -As of now, two uploads are required from the same mobile phone (past diagnosis keys and from the current day). This means, the registration token may not be invalidated after the first upload, but must remain active. The TANs sent to the Corona-Warn-App Server are only valid for a single use. In case of the teleTAN, an additional registration token is created which then allows the app to retrieve TANs for individual uploads. - -## Back End +## Backend ![Figure 6: Actors in the system, including external parties (blue components to be open-sourced)](images/solution_architecture/figure_6.svg "Figure 6: Actors in the system, including external parties (blue components to be open-sourced)") @@ -113,11 +130,12 @@ The Corona-Warn-App Server needs to fulfill the following tasks: - Accept upload requests from clients - Verify association with a positive test through the Verification Server and the associated workflow as explained in the “Retrieval of Lab Results and Verification Process” section and shown in the center of the left side of *Figure 6*. - - Accept uploaded diagnosis keys and store them (optional) together with the corresponding transmission risk level parameter (integer value of 1-8) into the database. Note that the transport of metadata (e.g. IP) of the incoming connections for the upload of diagnosis keys is removed in a dedicated actor, labeled “Transport Metadata Removal” in *Figure 6*. + - Accept uploaded diagnosis keys and store them (optional) together with the corresponding information (days since onset of symptoms/transmission risk level ) into the database. Note that the transport of connection metadata (e.g. IP) of the incoming connections for the upload of diagnosis keys is removed in a dedicated actor, labeled “Transport Metadata Removal” in *Figure 6*. - Provide [configuration parameters](#data-format) to the mobile applications - Threshold values for [attenuation buckets](#attenuation-buckets) - - Risk scores for defined values + - Encoding and mapping of the Transmission Risk Level - Threshold values for risk categories and alerts + - Weight mappings for the ENF (not used, but need to be present) - Valid country codes for EFGS Visited Countries - On a regular schedule (e.g. hourly) - Assemble diagnosis keys into chunks for a given time period @@ -129,40 +147,38 @@ The Corona-Warn-App Server needs to fulfill the following tasks: - Expose a callback API which can be used by the EFGS to notify CWA when new key batches are available for download - Handle the translation of keys values for DSOS and TRL -Those tasks relevant for interaction with the CWA Mobile application are visualized in *Figure 7*. Each of swim lanes (vertical sections of the diagram) on the left side (Phone 1, Phone 2, Phone n) represents one device that has the Corona-Warn-App installed. The user of Phone 1 has taken a SARS-CoV-2 test (which comes back positive later). The users of Phone 2 and Phone n only use the functionality to trace potential exposure. +Those tasks relevant for interaction with the CWA Mobile application are visualized in *Figure 7*. Each of the swim lanes (vertical sections of the diagram) on the left side (Phone 1, Phone 2, Phone n) represents one device that has the Corona-Warn-App installed. The user of Phone 1 has taken a SARS-CoV-2 test (which comes back positive later). The users of Phone 2 and Phone n only use the functionality to trace potential exposure. The Corona-Warn-App Server represents the outside picture of the individual service working in the back end. For a better understanding, the database has been visualized separately. ![Figure 7: Interaction of the mobile application(s) with the back-end servers and CDN](images/solution_architecture/figure_7.svg "Figure 7: Interaction of the mobile application(s) with the back-end servers and CDN") Note that even if a user has not been tested positive, the app randomly submits requests to the Corona-Warn-App Server (represented in *Figure 7* by Phone 2) which on the server side can easily be ignored, but from an outside perspective exactly looks as if a user has uploaded positive test results. This helps to preserve the privacy of users who are actually submitting their diagnosis keys due to positive test results. Without dummy requests, a malicious third party monitoring the traffic could easily find out that users uploading something to the server have been infected. With our approach, no pattern can be detected and, thus, no assumption can be taken. -If diagnosis keys need to be uploaded on subsequent days of the submission of a positive test result, also that behavior should be represented within the random (dummy) submissions. - It could be possible to identify temporary exposure keys that belong together, i.e. belong to the same user, because they are posted together which results in them being in a sequential order in the database. To prevent this, the aggregated files are shuffled, e.g. by using an ORDER BY RANDOM on the database while fetching the data for the corresponding file. Alternatively, returning them in the lexicographic order of the RPIs (which are random) is a valid option as well. The latter might be more efficient for compressing the data afterwards. -The configuration parameters mentioned above allow the health authorities to dynamically adjust the behavior of the mobile applications to the current epidemiological situation. For example, the risk score thresholds for the risk levels can be adjusted, as well as how the individual data from exposure events influence the overall score. +The configuration parameters mentioned above allow the health authorities to dynamically adjust the behavior of the mobile applications to the current epidemiological situation. For example, the risk score thresholds for the risk levels can be adjusted, as well as how the individual data from exposure events influence the overall outcome of the risk assessment. -Further information can be found in the dedicated architecture documents for the Corona-Warn-App Server, the Verification Server, and the Portal Server. The documents will be linked here, as soon as they are available. +Further information can be found in the dedicated architecture documents for the [Corona-Warn-App Server](https://github.com/corona-warn-app/cwa-server/blob/main/docs/ARCHITECTURE.md), the Verification Server, and the Portal Server. ### Data Format -The current base unit for data chunks will be one hour. Data will be encoded in the protocol buffer format as specified by Apple and Google (see *Figure 8*). It is planned that in case a data chunk does not hold any or too few diagnosis keys, the chunk generation will be skipped. +The current base unit for data chunks will be one hour. Data will be encoded in the protocol buffer format as specified by Apple and Google (see *Figure 8*). In case a data chunk does not hold any or too few diagnosis keys, the chunk generation will be skipped and the keys will be made available as soon as the threshold has been passed. -The server will make the following information available through a RESTful interface: +The makes the following information available through a RESTful interface: -- Available items through index endpoints (not all implemented in first iteration) +- Available items through index endpoints - Newly-added Diagnosis Keys (Temporary Exposure Keys) for the time frame - Configuration parameters - - 32 parameters for configuring the risk score of the Apple/Google Exposure Notification Framework + - Parameters for configuring the risk Apple/Google Exposure Notification Framework - [Attenuation bucket](#attenuation-buckets) thresholds - Risk score threshold to issue a warning - Risk score ranges for individual risk levels -Return data needs to be signed and will contain a timestamp (please refer to protocol buffer files for further information). Using index endpoints will not increase the number of requests, as they can be handled within a single HTTP session. In case the hourly endpoint does not hold diagnosis keys for the selected hour, the mobile application does not need to download it. If, on the other hand multiple files need to be downloaded (e.g. because the client was switched off overnight), they can be handled in a single session as well. +Return data needs to be signed and needs to contain a timestamp (please refer to protocol buffer files for further information). Using index endpoints will not increase the number of requests, as they can be handled within a single HTTP session. In case the hourly endpoint does not hold diagnosis keys for the selected hour, the mobile application does not need to download it. If, on the other hand multiple files need to be downloaded (e.g. because the client was switched off overnight), they can be handled in a single session as well. -In order to ensure the authenticity of the files, they need to be signed (according to the specifications of the API) on server side with a private key, while the app uses the public key to verify that signature. To ensure roaming qualities (protocol interoperability with servers in other geographical regions), it is planned to move to a single agreed protocol once finally defined. +In order to ensure the authenticity of the files, they need to be signed (according to the specifications of the API) on server side with a private key, while the app uses the public key to verify that signature. Exchange with other geographical regions takes place through the European Federation Gateway. ![Figure 8: Data format (protocol buffer) specified by Apple/Google](images/solution_architecture/figure_8.svg "Figure 8: Data format (protocol buffer) specified by Apple/Google") @@ -182,65 +198,60 @@ The data on all involved servers is only retained as long as required. Diagnosis The functional scope of the mobile applications (apps) is defined in the corresponding [scoping document](https://github.com/corona-warn-app/cwa-documentation/blob/master/scoping_document.md). The apps are developed natively for Apple’s iOS and Google’s Android operating systems. They make use of the respective interfaces for the exposure notification, i.e. broadcasting and scanning for Bluetooth advertisement packages, see *Figure 9*. -For Apple devices an OS version of at least 13.5 will be required for the system to work, as the framework is integrated into the operating system. +For Apple devices an OS version of at least 12.5 (for older devices) or 13.7 is required for the system to work, as the framework is integrated into the operating system (see Figure 9). -For Android devices, the features will be integrated into the [Google Play Services](https://9to5google.com/2020/04/13/android-contact-tracing-google-play-services/), which means that only this specific application needs to be updated for it to work. Devices starting with Android 6.0 (API version 23) and integrated BLE chips will be [supported](https://developers.google.com/android/exposure-notifications/exposure-notifications-api#architecture). +![Figure 9: iOS Releases and ENF Support](images/solution_architecture/ios_releases.svg "Figure 9: iOS Releases and ENF Support") -![Figure 9: Architecture overview of the mobile application (focused on API usage/BLE communication)](images/solution_architecture/figure_9.svg "Figure 9: Architecture overview of the mobile application (focused on API usage/BLE communication)") +For Android devices, the features is integrated into the [Google Play Services](https://9to5google.com/2020/04/13/android-contact-tracing-google-play-services/), which means that only this specific application needs to be updated for it to work. Devices starting with Android 6.0 (API version 23) and integrated BLE chips will be [supported](https://developers.google.com/android/exposure-notifications/exposure-notifications-api#architecture). + +![Figure 10: Architecture overview of the mobile application (focused on API usage/BLE communication)](images/solution_architecture/figure_9.svg "Figure 9: Architecture overview of the mobile application (focused on API usage/BLE communication)") The app itself does not have access to the collected exposures, i.e. the Rolling Proximity Identifiers, and neither is it informed if a new one has been collected by the framework. As depicted in the *Figure 10* and *Figure 11*, the Exposure Notification encapsulates handling of the keys, including all cryptographic operations on them. The only output of the black box upon an infection is a collection of temporary exposure keys as shown in *Figure 10*. Those are subsequently called diagnosis keys. ![Figure 10: Key flow from the sending perspective (as described in the specification by Apple/Google)](images/solution_architecture/figure_10.svg "Figure 10: Key flow from the sending perspective (as described in the specification by Apple/Google)") -The encapsulation especially applies to the part where matches are calculated, as the framework only accepts the diagnosis keys as input, matches them to (internally stored) RPIs and returns a list of exposure events without a link to the corresponding Rolling Proximity Identifiers (see *Figure 11*). With the use of the corresponding Associated Encrypted Metadata Key, the Associated Encrypted Metadata (AEM) of the captured RPI can be decrypted. This metadata contains the transmission power (which is used to calculate the attenuation). Additionally, an epoch (usually a 24 hour window) for the exposure is determined, as well as the duration of the exposure in 5-minute increments (capped at 30 minutes). +The encapsulation especially applies to the part where matches are calculated, as the framework only accepts the diagnosis keys as input, matches them to (internally stored) RPIs and returns a list of exposure events without a link to the corresponding Rolling Proximity Identifiers (see *Figure 11*). With the use of the corresponding Associated Encrypted Metadata Key, the Associated Encrypted Metadata (AEM) of the captured RPI can be decrypted. This metadata contains the transmission power (which is used to calculate the attenuation). The Exposure Notification Framework assembles exposures into 30-minute-windows per other device and 24-hour epoch. Those windows contain additional details for individual scan instances, which will be explained later. ![Figure 11: Key flow from the receiving perspective (as described in the specification by Apple/Google)](images/solution_architecture/figure_11.svg "Figure 11: Key flow from the receiving perspective (as described in the specification by Apple/Google)") [Information provided from the framework API to the app per exposure](https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-FrameworkDocumentationv1.2.pdf): -- **Attenuation value** (Reported Transmit Power - Measured RSSI) -- **Attenuation “buckets”**, i.e. times spent within certain attenuation ranges (see below) -- **Date** when the exposure occurred (with reduced precision, i.e. one day) -- **Duration** of the exposure (<5/5/10/15/20/25/30/>30 minutes) -- **Transmission risk level** associated with diagnosis key of other person (downloaded from server, together with diagnosis key) -- **Total Risk Score** calculated exposure risk level (with a range from 0-4096) according to the defined parameters +All exposure events are collected by the ENF internally and are split up into "Exposure Windows", which represent all instances where one other specific device (without known identity) has been detected within a 30 minute window. If an encounter lasted for more then 30 minutes, multiple exposure windows are derived. Those cannot be related to each other neither can it be determined in which order (and possible overlap), exposures windows have occured. This means that if for example five exposure windows are presented to the app by the ENF, it cannot be determined whether those have been five different devices or a single other device with 2,5 hours of contact. Same applies to the timely arrangement, i.e. all windows could have happend in parallel, with partial overlap or after one another. + +![Figure 12: Exposure Windows](images/solution_architecture/exposure_windows.svg "Figure 11: Exposure Windows") + +Each exposure window contains the following information: + +- **infectiousness** and **report type** parameters, defined by the sending app +- **day of the exposure** +- **multiple scan instances**, i.e. occurences, where the other device has been actively identified during the scanning process ### Attenuation Buckets -Both, Apple and Google allow to define a low and a high threshold for the attenuation, forming three individual buckets: +While in the first version of the Exposure Notification Framework, Apple and Google allowed to define multiple thresholds for the attenuation, this functionality can now be implemented within the application through the data from the exposure windows. -- Attenuation < low threshold -- Low threshold <= attenuation < high threshold -- High threshold <= attenuation +Currently, the application forms four attenuation ranges (sometimes also called "buckets"), which have a specific weight applied: -While in the Google implementation of the Exposure Notification Framework, those buckets are contained within the `ExposureSummary` (`attenuationDurations`), Apple has added them to the [`metadata`](https://developer.apple.com/documentation/exposurenotification/enexposureinfo/3586326-metadata) attribute of the [`ENExposureInfo`](https://developer.apple.com/documentation/exposurenotification/enexposureinfo). -In the latter implementation, the [`attenuationDurations`](https://developer.apple.com/documentation/exposurenotification/enexposureinfo/3586325-attenuationdurations) of the `ENExposureInfo` contains two buckets around a fixed threshold of 50. +- Very far +- Far +- Medium +- Close -### Risk Score Calculation +### Risk Calculation The information listed above is not visible to the user, but is used internally to calculate a risk score, which again is mapped to one specific app-defined risk level. This easy-to-understand risk level is displayed to the user. Further information regarding the individual exposure events (such as the matched Rolling Proximity Identifier, the Temporary Exposure Key or the exact time) remains within the secure storage of the framework and cannot be retrieved by the application. -![Figure 12: Risk calculation](images/solution_architecture/figure_12.svg "Figure 12: Risk calculation") +![Figure 13: Risk calculation](images/solution_architecture/trl_mapping.svg "Figure 13: Risk calculation") -*Figure 12* displays how the total risk score is being calculated. The application is provided with a set of parameters, which are marked in blue within the figure. + +The Exposure Notification framework allows to attach as "days since onset of symptoms" parameter to the diagnosis key while uploading them to the server. As this parameter strongly influences the infectiousness during an encounter, it is also used in the risk calculation. However, the ENF only allows a translation from the DSOS to either "no risk" (0), "low risk" (1) or "high risk" (2). To allow a more fine grained interpretation of the exposure windows, the additional parameter "report type" (four possible values) is used to derive an internal "Transmission Risk Level" with eight possible values. Of those eight values, two are dropped by the ENF automatically, as the report type "recursive" might be dropped in current implementations. It is important to understand, that the field "report type" does not correspond to the actual report type, but is only technically used as a 2 bit field. The mapping is also shown in Figure 13. + +![Figure 14: Risk calculation](images/solution_architecture/risk_calculation.svg "Figure 14: Risk calculation") + +*Figure 14* displays how the total risk score is being calculated. The application is provided with a set of parameters, which are marked in blue within the figure. Those parameters are regularly downloaded from the CWA Server, which means they can be modified without requiring a new version of the application (see [`exposure-config.yaml`](https://github.com/corona-warn-app/cwa-server/blob/master/services/distribution/src/main/resources/main-config/exposure-config.yaml) for details). -Each of the four risk categories (days since exposure, exposure duration, weighted signal attenuation, and the transmission risk factor) receives an input value from the event which is then mapped to a predefined input value interval. - Each of those input value intervals is then assigned a risk score from 0-8, where 0 represents a very low risk and 8 represents a very high risk. This means that from each of the rows in the figure, one value is selected according to the input value for the corresponding category. As an example: an exposure duration input value of D=15.3 minutes is mapped to the interval 15 < D <= 20, which in the current implementation has a value of 1 assigned to it, i.e. the *Exposure Risk* would be equal to 1 in this example. The product of the four risk scores is used as the **total risk score** of the individual exposure. -According to the [documentation of the framework](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration), "the attenuation is weighted by the duration at each risk level and averaged for the overall duration". In order to incorporate the time spent within the ranges of attenuation buckets mentioned before, each of those three buckets is assigned a weight value as shown in *Figure 13*. - The individual time values are multiplied with their according weight (*weight_1*, *weight_2* and *weight_3*). - Their sum and a default bucket offset (called *weight_4* in *Figure 13*) forms the *Exposure Score*. -Finally, the maximum of the *total risk score* over all the considered events, i.e. the largest risk score, is normalized and then multiplied with the above exposure score. The resulting product of the *exposure score* and the *normalized maximum total risk score* then forms the so called **combined risk score**. - -The combined risk score is used to determine which defined risk level should be displayed to the user, e.g. “low risk” or “high risk”. For this decision, [app-defined thresholds](https://github.com/corona-warn-app/cwa-server/blob/master/services/distribution/src/main/resources/main-config/risk-score-classification.yaml) for the individual risk levels apply. -As the values above are multiplied with each other, a single category with a risk score of 0 means that the overall risk score is also 0. -Additionally, a central threshold for the combined risk score specifies whether an exposure event should be considered or not. -Furthermore the Google/Apple framework allows to set a [```minimalRiskScore```](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration/3583692-minimumriskscore) to exclude exposure incidents with scores lower than the value of this property. -In the current version of the API the time spent within the ranges of attenuation buckets are accumulated over all exposure incidents during one matching session. -Since the number of requests is currently limited, it is not possible to get these values for each day and each exposure incident separately. -While by default there is no minimum value set, this value is being configured accordingly, so that presumably irrelevant exposure incidents are excluded. - -![Figure 13: Calculation of the combined risk score](images/solution_architecture/figure_13.svg "Figure 13: Calculation of the combined risk score") +As mentioned before, the individual scan instances from the exposure windows are weighted according to the weight attached to the individual bucket. When those individual instances are summed up, they can be multiplied with a transmission risk value (which in turn is derived from the TRL described before). The result is one normalized exposure time per day. If those times are summed up, the overall risk can be determined, as shown in *Figure 14*. Note that the transmission risk level plays a special role in the above calculations: It can be defined by the app and be associated with each individual diagnosis key (i.e. specific for each day of an infected person) that is being sent to the server. It contains a value from 1 to 8, which can be used to represent a calculated risk defined by the health authority. As an example it could contain an estimate of the infectiousness of the potential infector at the time of contact and, hence, the likelihood of transmitting the disease. The specific values are defined as part of the [app](https://github.com/corona-warn-app/cwa-app-android/blob/master/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoFormatConverterExtensions.kt) - a motivation of the parameter choices is found in the document [Epidemiological Motivation of the Transmission Risk Level](https://github.com/corona-warn-app/cwa-documentation/blob/master/transmission_risk.pdf). @@ -297,7 +308,7 @@ In order for the EFGS to function correctly, all users must specify their visite ## LIMITATIONS -Even though the system can support individuals in finding out whether they have been in proximity with a person that has been tested positively later on, the system also has limits (shown in *Figure 14*) that need to be considered. One of those limitations is that while the device constantly broadcasts its own Rolling Proximity Identifiers, it only listens for others in defined time slots. Those [listening windows are currently up to five minutes](https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf) apart and are defined as being only very short. In our considerations we expect the windows to have a length of two to four seconds. For the attenuation, the two buckets provided by the framework are being considered. A lower attenuation means that the other device is closer; we assume that an attenuation <58 dB translates to a distance below two meters. A higher attenuation might either mean that the other device is farther away (i.e. a distance of more than two meters) or that there is something between the devices blocking the signal. This could be objects such as a wall, but also humans or animals. +Even though the system can support individuals in finding out whether they have been in proximity with a person that has been tested positive later on, the system also has limits (shown in *Figure 14*) that need to be considered. One of those limitations is that while the device constantly broadcasts its own Rolling Proximity Identifiers, it only listens for others in defined time slots. Those [listening windows are currently up to five minutes](https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf) apart and are defined as being only very short. In our considerations we expect the windows to have a length of two to four seconds. For the attenuation, the two buckets provided by the framework are being considered. A lower attenuation means that the other device is closer; we assume that an attenuation <58 dB translates to a distance below two meters. A higher attenuation might either mean that the other device is farther away (i.e. a distance of more than two meters) or that there is something between the devices blocking the signal. This could be objects such as a wall, but also humans or animals. ![Figure 14: Limitations of the Bluetooth Low Energy approach](images/solution_architecture/figure_14.svg "Figure 14: Limitations of the Bluetooth Low Energy approach")