-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exposure risk calculation algorithm #24
Conversation
Writing documentation helped me organize ideas, and I realised there is something strange: apparently the |
45b23d0
to
16a9c3e
Compare
Using AndroidX nullability annotations is perfectly fine. I think that was actually the only file in this project the Jetbrains annotations were used. As for the
|
I added the risk calculation algorithm: it is surely not in the correct place and with the correct semantics, but it isn't difficult to move code if needed ;-) |
How do we know the algorithm works correct? We don't have test vectors and can't simply extract them from the original app like we did with Sk and RPIs for the CryptoModul. Or can we? Would it be possible? |
Please change the
|
@BjoernPetersen done! ;-) |
Containing references to Google and Apple's developer websites
I found the ApplicationConfiguration
app_version {
android {
latest {
major: 1
patch: 4
}
min {
major: 1
patch: 4
}
}
ios {
latest {
minor: 8
patch: 2
}
min {
minor: 5
}
}
}
attenuation_duration {
risk_score_normalization_divisor: 25
thresholds {
lower: 55
upper: 63
}
weights {
low: 1.0
mid: 0.5
}
}
exposure_config {
attenuation {
gt10_le15_dbm: LOWEST
gt10_le15_dbm_value: 1
gt15_le27_dbm: LOWEST
gt15_le27_dbm_value: 1
gt27_le33_dbm: LOWEST
gt27_le33_dbm_value: 1
gt33_le51_dbm: LOWEST
gt33_le51_dbm_value: 1
gt51_le63_dbm: LOWEST
gt51_le63_dbm_value: 1
gt63_le73_dbm: LOWEST
gt63_le73_dbm_value: 1
lt10_dbm: LOWEST
lt10_dbm_value: 1
}
attenuation_weight: 50.0
days_since_last_exposure {
ge0_lt2_days: MEDIUM_HIGH
ge0_lt2_days_value: 5
ge10_lt12_days: MEDIUM_HIGH
ge10_lt12_days_value: 5
ge12_lt14_days: MEDIUM_HIGH
ge12_lt14_days_value: 5
ge14_days: MEDIUM_HIGH
ge14_days_value: 5
ge2_lt4_days: MEDIUM_HIGH
ge2_lt4_days_value: 5
ge4_lt6_days: MEDIUM_HIGH
ge4_lt6_days_value: 5
ge6_lt8_days: MEDIUM_HIGH
ge6_lt8_days_value: 5
ge8_lt10_days: MEDIUM_HIGH
ge8_lt10_days_value: 5
}
days_weight: 20.0
duration {
gt10_le15_min: LOWEST
gt10_le15_min_value: 1
gt15_le20_min: LOWEST
gt15_le20_min_value: 1
gt20_le25_min: LOWEST
gt20_le25_min_value: 1
gt25_le30_min: LOWEST
gt25_le30_min_value: 1
gt30_min: LOWEST
gt30_min_value: 1
}
duration_weight: 50.0
transmission {
app_defined1: LOWEST
app_defined1_value: 1
app_defined2: LOW
app_defined2_value: 2
app_defined3: LOW_MEDIUM
app_defined3_value: 3
app_defined4: MEDIUM
app_defined4_value: 4
app_defined5: MEDIUM_HIGH
app_defined5_value: 5
app_defined6: HIGH
app_defined6_value: 6
app_defined7: VERY_HIGH
app_defined7_value: 7
app_defined8: HIGHEST
app_defined8_value: 8
}
transmission_weight: 50.0
}
min_risk_score: 11
risk_score_classes {
risk_classes {
label: "LOW"
max: 15
url: "https://www.coronawarn.app"
}
risk_classes {
label: "HIGH"
max: 72
min: 15
url: "https://www.coronawarn.app"
}
} From here an
As you can see there is something wrong with the values: Kotlin code to obtain the above results
var exportBinary: ByteArray? = /* the bytes of the `export.bin` file contained in the downloaded zip*/;
var appConfig: ApplicationConfigurationOuterClass.ApplicationConfiguration =
ApplicationConfigurationOuterClass.ApplicationConfiguration.parseFrom(exportBinary)
println(appConfig.toString())
var config: ExposureConfiguration = ExposureConfiguration
.ExposureConfigurationBuilder()
.setTransmissionRiskScores(
appConfig.exposureConfig.transmission.appDefined1Value,
appConfig.exposureConfig.transmission.appDefined2Value,
appConfig.exposureConfig.transmission.appDefined3Value,
appConfig.exposureConfig.transmission.appDefined4Value,
appConfig.exposureConfig.transmission.appDefined5Value,
appConfig.exposureConfig.transmission.appDefined6Value,
appConfig.exposureConfig.transmission.appDefined7Value,
appConfig.exposureConfig.transmission.appDefined8Value
)
.setDurationScores(
appConfig.exposureConfig.duration.eq0MinValue,
appConfig.exposureConfig.duration.gt0Le5MinValue,
appConfig.exposureConfig.duration.gt5Le10MinValue,
appConfig.exposureConfig.duration.gt10Le15MinValue,
appConfig.exposureConfig.duration.gt15Le20MinValue,
appConfig.exposureConfig.duration.gt20Le25MinValue,
appConfig.exposureConfig.duration.gt25Le30MinValue,
appConfig.exposureConfig.duration.gt30MinValue
)
.setDaysSinceLastExposureScores(
appConfig.exposureConfig.daysSinceLastExposure.ge14DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge12Lt14DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge10Lt12DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge8Lt10DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge6Lt8DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge4Lt6DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge2Lt4DaysValue,
appConfig.exposureConfig.daysSinceLastExposure.ge0Lt2DaysValue
)
.setAttenuationScores(
appConfig.exposureConfig.attenuation.gt73DbmValue,
appConfig.exposureConfig.attenuation.gt63Le73DbmValue,
appConfig.exposureConfig.attenuation.gt51Le63DbmValue,
appConfig.exposureConfig.attenuation.gt33Le51DbmValue,
appConfig.exposureConfig.attenuation.gt27Le33DbmValue,
appConfig.exposureConfig.attenuation.gt15Le27DbmValue,
appConfig.exposureConfig.attenuation.gt10Le15DbmValue,
appConfig.exposureConfig.attenuation.lt10DbmValue
)
.setMinimumRiskScore(appConfig.minRiskScore)
.setDurationAtAttenuationThresholds(
appConfig.attenuationDuration.thresholds.lower,
appConfig.attenuationDuration.thresholds.upper
)
.build()
println(config); |
I think those values seem about right. At least from a technical standpoint, the values in your All in all your guess about the CWA devs not fully utilizing the nuances and possibilities of the framework is probably correct, but as long as we can handle their config, that doesn't seem like a problem to me. |
This is the ExposureConfiguration for Immuni (the italian app), and it looks as strange as the CWA one, so I guess govenments decided not to fine-tune risk score calculation. (obtained from https://get.immuni.gov.it/v1/settings?platform=android&build=1300000 ) "exposure_configuration": {
"attenuation_thresholds": [ 50, 70 ],
"attenuation_bucket_scores": [ 0, 0, 5, 5, 5, 5, 5, 5 ],
"attenuation_weight": 1,
"days_since_last_exposure_bucket_scores": [ 1, 1, 1, 1, 1, 1, 1, 1 ],
"days_since_last_exposure_weight": 1,
"duration_bucket_scores": [ 0, 0, 0, 0, 5, 5, 5, 5 ],
"duration_weight": 1,
"transmission_risk_bucket_scores": [ 1, 1, 1, 1, 1, 1, 1, 1 ],
"transmission_risk_weight": 1,
"minimum_risk_score": 1
}, |
What I still don't understand is why GMS can calculate an ExposureSummary based on the data from an exposure even though it never asks the framework user to provide a definition for the various |
@Stypox TRL is an input as well as an output to the matching. |
Note however that Google is modifying this, see https://developers.google.com/android/exposure-notifications/exposure-notifications-api#data-structures google/exposure-notifications-server#663 and https://developers.google.com/android/exposure-notifications/exposure-key-file-format BTW: Apple might also be modifying this, but it's not publicly visible - however there are strange artefacts: |
I just did some more reading. Hopefully the following information helps. First, some terminology from the EN framework docs:TransmissionRiskLevel(s):
TemporaryExposureKey:
TransmissionRiskScore(s):
Now implementation details from the CWA code:There exists an In the code, the After retreiving this TL;DR:The application configuration with the mapping between risk levels and scores is fetched from the server in the |
@haitrec the TRL is not used by CWA like it’s recommended in the spec you mentioned. Instead a profile is used that maps a value (1..8) to each key, based on its age. |
I think the values are correct, and the CWA developers simply implemented the RKI risk estimation concept. This concept places importance on the Transmission Risk - therefore the (currently) 13 uploaded Diagnosis Keys get TRL assigned based on their age. This TRL is just mapped 1->1, 2->2, 3->3 etc using the values above. Attenuation and duration are also important inputs, but they are handled inside the CWA app. This is explained here. So the app does not make the framework do the most important parts of the calculations for this, but does them itself. I think version v1.5 of the API with the new "ExposureWindow" concept goes into this direction: Let the apps do the complete risk calculations however they want to do them, and keep only the privacy-preserving-parts (like hiding exact timestamps) in the framework. |
Thank you @mh- and @haitrec for your explanations, now my ideas are clearer :-D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this certainly isn't perfect yet, partially due to lack of info from Google/Apple, I think this is good enough for now. I'll merge this now so we can start developing the SDK parts that rely on it, we can improve on it later.
@Stypox Thank you for the contribution, especially on such a hard-to-get-your-head-around topic.
Also thanks to everyone who provided the very valuable input in this thread!
Since @Stypox already mentioned the Italian Immuni app, I thought it may be worth pointing out that Ireland's contact tracing app that uses the Google/Apple API was also released as open source and donated to the Linux Foundation, with code available on GitHub, with, AIUI, separate code to communicate with the APIs. |
For now I have added javadocs to risk calculation parameters and functions, containing references to Google and Apple's developer websites.
I used androidx
@Nullable
and@NonNull
, is it ok? Jetbrains annotations were red since we are not using a jetbrains library afaik.I also added default values for
minimumRiskScore
anddurationAtAttenuationThresholds
inExposureConfiguration
, as described in Apple documentation.