This document describes the file formats passed into the Exposure Notification
API. Applications are responsible for downloading these files from backends and
invoking provideDiagnosisKeys()
. This document also explains how the files are
verified by the API.
File format
Files consist of a ZIP archive containing two entries:
export.bin
: The binary containing the exposure keys.export.sig
: A signature to verify the export binary.
Exposure file binary format
The export.bin
file of the archive contains the Temporary Exposure Keys
that are broadcast by the devices of people who are diagnosed with COVID-19.
It’s an incremental file containing the latest keys the server received in a
given window of time. This window is typically 24 hours, allowing devices to
perform nightly matching.
The binary format file consists of a special header followed by a
protocol buffer. The header
must be exactly “EK Export v1” right-padded to 16 bytes with UTF-8 whitespaces.
After the header is the serialization of a protocol buffer message named
TemporaryExposureKeyExport
defined as follows:
syntax = "proto2";
message TemporaryExposureKeyExport {
// Time window of keys in this batch based on arrival to server, in UTC seconds.
optional fixed64 start_timestamp = 1;
optional fixed64 end_timestamp = 2;
// Region for which these keys came from, such as country.
optional string region = 3;
// For example, file 2 in batch size of 10. Ordinal, 1-based numbering.
// Note: Not yet supported on iOS.
optional int32 batch_num = 4;
optional int32 batch_size = 5;
// Information about associated signatures
repeated SignatureInfo signature_infos = 6;
// The TemporaryExposureKeys for initial release of keys.
// Keys should be included in this list for initial release,
// whereas revised or revoked keys should go in revised_keys.
repeated TemporaryExposureKey keys = 7;
// TemporaryExposureKeys that have changed status.
// Keys should be included in this list if they have changed status
// or have been revoked.
repeated TemporaryExposureKey revised_keys = 8;
}
message SignatureInfo {
// The first two fields have been deprecated
reserved 1, 2;
reserved "app_bundle_id", "android_package";
// Key version for rollovers
// Must be in character class [a-zA-Z0-9_]. For example, 'v1'
optional string verification_key_version = 3;
// Alias with which to identify public key to be used for verification
// Must be in character class [a-zA-Z0-9_.]
// For cross-compatibility with Apple, you can use your region's three-digit
// mobile country code (MCC). If your region has more than one MCC, choose the
// one that Apple has configured.
optional string verification_key_id = 4;
// ASN.1 OID for Algorithm Identifier. For example, `1.2.840.10045.4.3.2'
optional string signature_algorithm = 5;
}
message TemporaryExposureKey {
// Key of infected user
optional bytes key_data = 1;
// Varying risk associated with a key depending on diagnosis method
optional int32 transmission_risk_level = 2 [deprecated = true];
// The interval number since epoch for which a key starts
optional int32 rolling_start_interval_number = 3;
// Increments of 10 minutes describing how long a key is valid
optional int32 rolling_period = 4
[default = 144]; // defaults to 24 hours
// Data type representing why this key was published.
enum ReportType {
UNKNOWN = 0; // Never returned by the client API.
CONFIRMED_TEST = 1;
CONFIRMED_CLINICAL_DIAGNOSIS = 2;
SELF_REPORT = 3;
RECURSIVE = 4; // Reserved for future use.
REVOKED = 5; // Used to revoke a key, never returned by client API.
}
// Type of diagnosis associated with a key.
optional ReportType report_type = 5;
// Number of days elapsed between symptom onset and the TEK being used.
// E.g. 2 means TEK is 2 days after onset of symptoms.
optional sint32 days_since_onset_of_symptoms = 6;
// Tag 7 is reserved for future use.
// Data type representing a variant of concern.
enum VariantOfConcern {
VARIANT_TYPE_UNKNOWN = 0;
VARIANT_TYPE_1 = 1; // Vaccine is effective
VARIANT_TYPE_2 = 2; // Highly transmissive
VARIANT_TYPE_3 = 3; // High severity
VARIANT_TYPE_4 = 4; // Vaccine breakthrough
}
// Type of variant of concern associated with a key.
optional VariantOfConcern variant_of_concern = 8 [default = VARIANT_TYPE_UNKNOWN];
}
If the server stores exposure keys in the order that they were uploaded from devices, shuffle them before you export them. The shuffle ensures that there is no linkage between keys from a device. Since the keys are essentially random, a simple way to do this is to sort by key.
Exposure file signature
The server creates an asymmetric key pair for signing exports. When the app is
allowlisted, the public key is provided to Google. The server signs each
export.bin
with a private key. The export.sig
file of the archive contains
the raw signature as well as additional information needed for verification.
This key is different from the signing key for the APK.
The signature file is the serialization of the TEKSignatureList
protobuf
message defined as follows:
message TEKSignatureList {
repeated TEKSignature signatures = 1;
}
message TEKSignature {
// Info about the signing key, version, algorithm, and so on.
optional SignatureInfo signature_info = 1;
// For example, file 2 in batch size of 10. Ordinal, 1-based numbering.
optional int32 batch_num = 2;
optional int32 batch_size = 3;
// Signature in X9.62 format (ASN.1 SEQUENCE of two INTEGER fields)
optional bytes signature = 4;
}
Example
For the most common case of a backend exporting files for consumption by a handful of apps in some region, these archive entries could look as shown in the following example. Assume for the example that the backend serves apps in the U.S. and that splitting the export into multiple files is not needed yet. The files in the archive might look as follows:
export.bin
"EK Export v1 " // the special 16 byte header
TemporaryExposureKeyExport {
start_timestamp: 1589068800
end_timestamp: 1589155200
region: "US"
batch_num: 1
batch_size: 1
signature_infos: [
SignatureInfo {
verification_key_id: "310"
verification_key_version: "v1"
signature_algorithm: "1.2.840.10045.4.3.2"
},
]
keys = [...]
}
export.sig
message TEKSignatureList {
signatures: [
TEKSignature {
signature_info:
SignatureInfo {
verification_key_id: "310"
verification_key_version: "v1"
signature_algorithm: "1.2.840.10045.4.3.2"
}
signature: [...] // signature byte contents using key version 1
},
]
}
Create batches
These files can grow so big that they become prohibitive to download. This is
especially true for devices that don't have Wi-Fi access. You can break up files
into batches to keep the file size below 16MB—about 750K keys. Each chunk of
data in the batch is its own ZIP archive with its own signature. Each
archive covers just a portion of the batch. The serialized protos populate
the batch_num
and batch_size
fields.
Forward and backward compatibility
The export file format is designed to ensure forward and backward compatibility:
clients and servers can process and generate export files according to any published
(or future) version of
export.proto
and no file will be rejected or be misinterpreted.
A breaking change to the format (which is considered extremely unlikely) will be signalled by a change to the "EK Export v1" header. In this case, the entire export file may be rejected. Note that mobile apps are not responsible for checking the contents of this header; this will be done by the SDK, which will return an error in the event of a mismatch.
Verification
The API verifies the signature against the content of the exposure binary file. This can be optionally disabled on test devices using the setting in Debug Mode. However, prior to handing over your public key to Google for use in verification it is important to confirm that files are being successfully verified.
The API uses the fields in the signature file to identify which verification key to use for the application. You must verify at least one signature for each ZIP archive in a batch.
The API will also verify either that batch_size
is equal to 1, or, if greater
than 1:
- Pre v1.5:
- There are
batch_size
number of files in the batch and eachbatch_num
is present. That is, only one batch has been passed in and it is whole.
- There are
- Post v1.5:
- For every batch passed in (identified by grouping by
start_timestamp
,end_timestamp
,region
, andbatch_size
), all parts of the batch are present.
- For every batch passed in (identified by grouping by
- If the files should be cosigned by multiple keys, all are present in the
signatures
.
The API completes all verification before it invokes and releases the results. You should see "Signature verification succeeded" in the logs. If you do not, matching is not invoked and your app will not properly show exposure notifications.
Reference server export utilities
The reference server design offers a set of utilities to create and verify the file format and the signature.