There are 2 database + backend setups in this repo:
- Diagnosis DB (
diagnosis-{api,db}) - Exposure DB (not done yet)
- TEK (Temporary Exposure Key): revolving key generated by user; covers a day?
tek_i←CRNG(16)- app stores
tek_iandi
- RPI (Rotating Proximity Indicator): rotated every ~15 minutes. Generated from
TEK, andENIN:RPIK_i←HKDF(tek_i,NULL,UTF8("EN-RPIK"),16)RPI_i,j←AES128(RPIK_i,PaddedData_j)PaddedData_j[0...5]=UTF8("EN-RPI")PaddedData_j[6...11]=0x000000000000PaddedData_j[12...15]=ENIN_j
- ENIN (ENIntervalNumber): UTC timestamp / 10:
- ENIntervalNumber(Timestamp)←Timestamp/60×10
- HA (Health Authority): an administrative entity with the ability to diagnose a
Report Keyas a confirmed case - Report: a set of diagnosed
TEKs uploaded to the diagnosis database
Key types:
- Report Key: a key that is reported to backend or HA (usually in a batch)
- Diagnosis Key: a
TEKthat is confirmed to correspond to a positive diagnosis - Exposure Key: a
TEKthat has been exposed to adiagnosis key: e.g. an entity notices an overlap in RPI-space with a diagnosedTEKand publishes its ownTEKfor that period. What lighthouses need - Self-diagnosis Key: a
TEKthat corresponds to an unconfirmed positive diagnosis - HAK (Health Authority Key): a public key associated with a health authority
- LHK (Lighthouse Key): a unique identifier associated with a lighthouse
To run the development setup (postgres backend defined in *-db/, API backend defined in *-db/), use docker-compose:
docker-compose up
This should bring up:
- Diagnosis Postgres backend (port 5434)
- Diagnosis Postgres GRPC API (port 5000)
We have a simple simulation demonstarting the workflow involved. N entities randomly interact and query the database for a configurable number of days. To run:
$ cd simulate
$ time python simulation.py --entities 100 --days 7
2020-05-02 00:00:00+00:00
2020-05-03 00:00:00+00:00
entity-75 was exposed at 2020-05-02 00:00:00
2020-05-04 00:00:00+00:00
entity-73 was exposed at 2020-05-03 00:00:00
entity-75 was exposed at 2020-05-02 00:00:00
entity-79 was exposed at 2020-05-03 00:00:00
2020-05-05 00:00:00+00:00
entity-35 was exposed at 2020-05-04 00:00:00
entity-67 was exposed at 2020-05-04 00:00:00
... etcThe diagnosis backend receives TEKs (with timestamps marking the beginning of their 'valid' period) that have been "tainted" by an authority. This authority is trusted to perform a diagnosis of the entity who possesses the TEKs.
Reports use the AddReport(Report) GRPC call
// message sent by user
message Report {
// a unique authorization key given to the user upon
// interaction with an authorized (healthcare) professional
bytes authorization_key = 3;
// a set of timestamp-enin pairs (from the user)
repeated TimestampedTEK reports = 2;
}
message TimestampedTEK {
// user-generated TEK
bytes TEK = 1;
// corresponding ENIN for the TEK
uint32 ENIN = 2;
}
// response received
message AddReportResponse {
string error = 1;
}Example: see diagnosis-api/usage.py
In order to upload their TEK, ENINs to the backend (e.g. upon a diagnosis by a healthcare provider), a user needs an authorization key. An authorization key is a random 16-byte key that is given to the user by an authorized professional.
The authorized professional gets an authorization key by invoking the GetAuthorizationToken(TokenRequest) method on the server API. The caller authenticates to this method by providing an API key which is provided to them out-of-band and is stored in the backend database. The current prototype has a default API key of c3b9b61b687b895aff09eb072fb07d33
When generating a new authorization key, the professional must specify how the key is intended to be used through use of the KeyType field. Currently there is just one option (DIAGNOSED) but it must be specified in the request.
message TokenRequest {
// secret API key that uniquely identifies an authorized organization
bytes api_key = 1;
// the kind of key being requested; this is stored in the backend along
// with the generated authorization_key
KeyType key_type = 2;
}
message TokenResponse {
string error = 1;
// unique 16-byte key generated to be given to a user. The generation
// of this key means that the association of <authority, auth_key> is
// stored in the backend
bytes authorization_key = 2;
}
enum KeyType {
UNKNOWN = 0;
DIAGNOSED = 1;
}Use the GetDiagnosisKeys(GetKeyRequest) GRPC call. This will eventually allow filtering by time and health authority source, among other filters.
Response is a stream of GetDiagnosisKeyResponse
// user query to the API
message GetKeyRequest {
// retrieve keys for the given health authority
bytes HAK = 1;
// retrieve keys for the given day (ENIN rounded 'down'
// to the nearest day)
uint32 ENIN = 2;
// alternatively fetch a temporal range of keys
HistoricalRange hrange = 3;
}
// stream of messages from the server
message GetDiagnosisKeyResponse {
string error = 1;
TimestampedTEK record = 2;
}
message HistoricalRange {
// YYYY-MM-DD of *end* of day range; defaults to the current day
string start_date = 1;
// how many days back to retrieve records; defaults to 1
uint32 days = 2;
}
message TimestampedTEK {
bytes TEK = 1;
uint32 ENIN = 2;
}Example: see diagnosis-api/usage.py