1010from base45 import b45decode
1111from cose .messages import Sign1Message
1212from datetime import datetime
13+ from hashlib import sha256
14+ from base64 import b64encode
1315
1416def main (args ):
1517 if args .algorithm :
16- with_all_dccs ( print_algorithm , error_handler = _throw , exclude = 'venv*' )
18+ with_all_dccs ( print_algorithm , error_handler = _throw )
1719 if not args .validity_at is None :
1820 validation_clock = datetime .fromisoformat (args .validity_at )
19- with_all_dccs ( lambda f : validity_at (validation_clock , f ), error_handler = _throw , exclude = 'venv*' )
21+ with_all_dccs ( lambda f : validity_at (validation_clock , f ), error_handler = _throw )
22+ if args .hash :
23+ with_all_dccs ( print_hashes , error_handler = _display )
2024
2125def with_all_dccs ( function , error_handler = None , exclude = "" ):
2226 ''' Walks through the current directory and all subdirectories and calls
@@ -29,13 +33,12 @@ def with_all_dccs( function, error_handler=None, exclude="" ):
2933 fqfn = str (Path (base , file )) # fully qualified file name
3034 try :
3135 if not fnmatch ( fqfn , exclude ):
32- function (fqfn )
36+ function ( fqfn )
3337 except Exception as error :
3438 if not error_handler is None :
3539 error_handler ( fqfn , error )
3640
3741
38-
3942def load_dcc ( file ):
4043 '''Load QR code from image file and return Sign1Message and Payload
4144 Usage: s1msg, payload = load_dcc('my_dcc.png')
@@ -54,10 +57,6 @@ def load_dcc_with_rawdata( file ):
5457 logging .debug ('Decoding/Decompressing Base45 data' )
5558 decompressed = zlib .decompress (b45decode (qr_code_data [4 :]))
5659 s1msg = Sign1Message .decode (decompressed )
57- # print(cbor2.loads(decompressed))
58- # print(f'Unprotected Header: {cose_data.uhdr}')
59- # print(f'Protected Header: {cose_data.phdr}')
60- # print(f'KID = {get_kid_b64(cose_data)}')
6160 payload = cbor2 .loads (s1msg .payload )
6261 return s1msg , payload , qr_code_data
6362
@@ -69,6 +68,36 @@ def _throw( file, error ):
6968 print ('Error reading' , file )
7069 raise error
7170
71+ def _display ( file , error ):
72+ print ('Error reading' , file )
73+
74+
75+ def get_hashes (file ):
76+ def hashfunc ( value ):
77+ 'First 16 bytes only, base64-encoded'
78+ if isinstance ( value , str ):
79+ value = value .encode ('utf-8' )
80+ return b64encode (sha256 (value ).digest ()[:16 ]).decode ('utf-8' )
81+
82+
83+ s1msg , payload = load_dcc (file )
84+ country_code = payload [1 ]
85+ inner = payload [- 260 ][1 ]
86+ uci = inner ['v' if 'v' in inner .keys () else 't' if 't' in inner .keys () else 'r' ][0 ]['ci' ]
87+ if get_algorithm (s1msg ) == 'ES256' :
88+ signature = s1msg .signature [:len (s1msg .signature )// 2 ]
89+ else :
90+ signature = s1msg .signature
91+
92+ return {
93+ 'UCI' : hashfunc (uci ),
94+ 'COUNTRYCODEUCI' : hashfunc (country_code + uci ),
95+ 'SIGNATURE' : hashfunc (signature )
96+ }
97+
98+ def print_hashes (file ):
99+ print (f'{ file } \t { get_hashes (file )} ' )
100+
72101def validity_at ( validation_clock , file ):
73102 s1msg , payload = load_dcc (file )
74103 dcc_from = datetime .fromtimestamp (payload [6 ])
@@ -78,22 +107,24 @@ def validity_at( validation_clock, file ):
78107 print ( '\t ' .join ([file , validity , validation_clock .isoformat ()]))
79108
80109
81- def print_algorithm ( file ):
110+ def get_algorithm ( s1msg ):
82111 '''Print the algorithm of a DCC
83112 ES256 = SHA256 with ECDSA
84113 PS256 = RSASSA-PSS using SHA-256
85114 '''
86- s1msg , payload = load_dcc (file )
87115 for key ,value in s1msg .phdr .items ():
88116 _key = key .fullname
89117 if _key == 'ALG' :
90- _value = value .fullname
91- print ( '\t ' .join ([file , _value ]))
92-
118+ return value .fullname
119+
93120
121+ def print_algorithm ( file ):
122+ s1msg , payload = load_dcc (file )
123+ print ( '\t ' .join ([file , get_algorithm (s1msg )]))
94124
95125if __name__ == '__main__' :
96126 parser = ArgumentParser (description = 'Scan all DCCs for something' )
127+ parser .add_argument ('--hash' , action = 'store_true' , help = 'Print hashes' )
97128 parser .add_argument ('--algorithm' , action = 'store_true' , help = 'Print algorithm' )
98129 parser .add_argument ('--validity-at' , action = 'store' , default = None , help = 'Check validity at ISO date' )
99130 args = parser .parse_args ()
0 commit comments