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_i
andi
- 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]=0x000000000000
PaddedData_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 Key
as a confirmed case - Report: a set of diagnosed
TEK
s 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
TEK
that is confirmed to correspond to a positive diagnosis - Exposure Key: a
TEK
that has been exposed to adiagnosis key
: e.g. an entity notices an overlap in RPI-space with a diagnosedTEK
and publishes its ownTEK
for that period. What lighthouses need - Self-diagnosis Key: a
TEK
that 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
... etc
The 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