from datetime import datetime, timezone
from json import loads
from os import unlink
from random import random, randrange
from tempfile import NamedTemporaryFile
from time import time
from ssl import CERT_NONE, create_default_context
from urllib import request
from zipfile import ZipFile
print( '''
https://blinkything.org/diagnosis-keys-parsing
download the attached TemporaryExposureKeyExport.proto and run
# protoc --python_out=$(pwd) TemporaryExposureKeyExport.proto
after that, tun this script to download all available exposure keys,
parse the protobuf format and print "human readable" contents
''' )
from TemporaryExposureKeyExport_pb2 import TemporaryExposureKeyExport
results = TemporaryExposureKeyExport()
# stupid Apple stupid Mac software
insecure = create_default_context()
insecure.check_hostname = False
insecure.verify_mode = CERT_NONE
# https://blinkything.org/diagnosis-keys-distribution
address = "https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date"
# https://developer.apple.com/documentation/exposurenotification/enexposureinfo/3583716-transmissionrisklevel
# https://developers.google.com/android/exposure-notifications/exposure-notifications-api#data-structures
levels = [ "Unused", "Confirmed / Low risk", "Confirmed / Standard risk", "Confirmed / High risk", "Clinical diagnosis", "Self report", "Negative case", "Recursive case", "Custom" ]
for day in sorted( [ datetime.fromtimestamp( int( time() - day * 24 * 60 * 60 ) ).strftime( "%Y-%m-%d" ) for day in range( 1, 14 ) ], key = lambda entry : randrange( -1, 2 ) ) :
timeslices = loads( request.urlopen( "%s/%s/hour" % ( address, day ), context = insecure ).read() )
for hour in sorted( timeslices, key = lambda entry : randrange( -1, 2 ) ) :
file = NamedTemporaryFile( suffix = ".zip" )
ignore = file.write( request.urlopen( "%s/%s/hour/%s" % ( address, day, hour ), context = insecure ).read() )
ignore = results.ParseFromString( ZipFile( file ).read( "export.bin" )[ 16: ] )
for entry in results.keys :
start = datetime.fromtimestamp( entry.rolling_start_interval_number * 600, tz = timezone.utc )
key = str( "" ).join( [ "%02X" % int( value ) for value in entry.key_data ] )
risk = entry.transmission_risk_level
if entry.transmission_risk_level < len( levels ) :
risk = levels[ entry.transmission_risk_level ]
print( start.strftime( "%Y-%m-%d" ), key, risk )
2020-07-28 DBD4FBDB25E192331403C01856E28E4C Custom
2020-07-31 DD1126751C3F7D0C94E78313478EC079 Negative case
2020-07-30 DDA92095040304E8F3194398B1756E5F Custom
2020-07-24 DE7A1BEFA3647A1711EF86405E146A35 Confirmed / Low risk
2020-07-22 DEFD32D1A693DAB2C5DF84967AD268EF Confirmed / Low risk
2020-07-21 DF794C176F664E3EDD6634295EA5823A Confirmed / Low risk
2020-07-29 DF91AD4A61756C9AFAE85CE68F908AEF Custom
2020-07-27 E1A1818AEAB09F3765C6F4750DD2A28D Self report
2020-07-31 E2596B474956B0680F7DF97A471256FA Negative case
2020-07-31 E32B95A338D293F37B2CA335532BC716 Negative case
2020-07-20 E47CDA3C8780BF5162BA7BF59EA852AE Confirmed / Low risk
2020-07-29 E490C2D8464CDF2AC3845AD39E455C90 Custom
2020-07-30 E58BEA941CCDA715CA7D89A51B46AF57 Custom
2020-07-31 E58CB52DE34B548F526DCF6F9317A31D Negative case
2020-07-21 E712149A08A4089A8FADBBDCBA247CBA Confirmed / Low risk
2020-07-23 E831310321B5635F45372FC803A41D79 Confirmed / Low risk
2020-07-28 E9927B7A1C1B499676D1800813197788 Custom
2020-07-30 E9CFA64B7540AD606DEA33EC796102EC Custom
2020-07-29 EA97B428133D82CA545BD7C7ED77482D Custom
2020-07-30 EC0636792FD774462F3415D881FF7AB3 Custom
2020-07-21 ED5EDFC74E3F9A0B290D1CE3F83BAC17 Confirmed / Low risk
2020-07-31 ED82A6FD074E06BDE14C040A7CAFA89F Negative case
2020-07-24 EE155EDEF5B24510D5979124A9CFC2BC Confirmed / Low risk
2020-07-20 EE7246DF8AAAC8CF0CC87327E1D316C3 Confirmed / Low risk
2020-07-20 EF854F51CB5E74F29BD55962771D8DAA Confirmed / Low risk
2020-07-31 EFAD52A8303390513804CA110D3E355A Negative case
2020-07-30 F01BC8D86B49A7F6DF9E027D451E6EA2 Custom
2020-07-20 F19C4467C6D897B7D89CEC827F93E70A Confirmed / Low risk
2020-07-27 F1A28E1BD67C722A1F38306B99F1EC40 Self report
2020-07-23 F2C2721978F11724763E7559D709F849 Confirmed / Low risk
2020-07-30 F2F56B1C03CDE73DD09B86EABDCF8FE7 Custom
2020-07-24 F3906C853A9860A385E45A9D25AA47E7 Confirmed / Low risk
2020-07-28 F4FD5834C929828330A0964E7B828635 Custom
2020-07-20 F5171741FA589E85FA503D9DB9D49268 Confirmed / Low risk
2020-07-20 F6BA2A9DEEAA5CCBB41121BA25DA2F4B Confirmed / Low risk
2020-07-25 F6C913867DEBD152AC5BF7465CE747E0 Confirmed / Low risk
2020-07-25 F854B1ACD4642DABEFBED10DB70F1141 Confirmed / Low risk
2020-07-21 F866DE1B3537C2ACE8EF3708350F4D6F Confirmed / Low risk
2020-07-27 F8EF13EDD4D1A785CBA927588A9FDFF9 Self report
2020-07-22 F8EF4192FC288A25B8D5EF4D8C50FC05 Confirmed / Low risk
2020-07-22 F94D16245E962E3301004D459B177E7E Confirmed / Low risk
2020-07-31 F96328E487724BC805B8BD49F6ED4DC2 Negative case
2020-07-30 FA84B29369EABF0E9C8758722DB40F7E Custom
2020-07-26 FBDAB57DAB26581FE6D0590BDA2D4C6B Confirmed / High risk
2020-07-27 FC08BD6A9CC59DE8C12CDD1388971D35 Self report