2
2
3
3
import sys
4
4
import argparse
5
+ import contextlib
5
6
6
7
CHECKSUM_MIN_VERSION = ('V1.0' , 'beta' , '1' )
7
8
@@ -113,6 +114,10 @@ def eac_verify(data):
113
114
# Log is encoded as Little Endian UTF-16
114
115
text = data .decode ('utf-16-le' )
115
116
117
+ # Strip off the BOM
118
+ if text .startswith ('\ufeff ' ):
119
+ text = text [1 :]
120
+
116
121
# Null bytes screw it up
117
122
if '\x00 ' in text :
118
123
text = text [:text .index ('\x00 ' )]
@@ -126,46 +131,70 @@ def eac_verify(data):
126
131
return unsigned_text , version , old_signature , eac_checksum (unsigned_text )
127
132
128
133
134
+ class FixedFileType (argparse .FileType ):
135
+ def __call__ (self , string ):
136
+ file = super ().__call__ (string )
137
+
138
+ # Properly handle stdin/stdout with 'b' mode
139
+ if 'b' in self ._mode and file in (sys .stdin , sys .stdout ):
140
+ return file .buffer
141
+
142
+ return file
143
+
144
+
129
145
if __name__ == '__main__' :
130
146
parser = argparse .ArgumentParser (description = 'Verifies and resigns EAC logs' )
131
- parser .add_argument ('file' , metavar = 'FILE' , help = 'path to the log file' )
132
147
133
- group = parser .add_mutually_exclusive_group (required = True )
134
- group .add_argument ('--verify' , action = 'store_true' , help = 'verify a log' )
135
- group .add_argument ('--sign' , action = 'store_true' , help = 'sign or fix an existing log' )
148
+ subparsers = parser .add_subparsers (dest = 'command' , required = True )
149
+
150
+ verify_parser = subparsers .add_parser ('verify' , help = 'verify a log' )
151
+ verify_parser .add_argument ('files' , type = FixedFileType (mode = 'rb' ), nargs = '+' , help = 'input log file(s)' )
152
+
153
+ sign_parser = subparsers .add_parser ('sign' , help = 'sign or fix an existing log' )
154
+ sign_parser .add_argument ('--force' , action = 'store_true' , help = 'forces signing even if EAC version is too old' )
155
+ sign_parser .add_argument ('input_file' , type = FixedFileType (mode = 'rb' ), help = 'input log file' )
156
+ sign_parser .add_argument ('output_file' , type = FixedFileType (mode = 'wb' ), help = 'output log file' )
136
157
137
158
args = parser .parse_args ()
138
159
139
- if args .file == '-' :
140
- handle = sys .stdin
141
- else :
142
- handle = open (args .file , 'rb' )
143
-
144
- try :
145
- data , version , old_signature , actual_signature = eac_verify (handle .read ())
146
- except RuntimeError as e :
147
- print (e )
148
- sys .exit (1 )
149
- finally :
150
- handle .close ()
151
-
152
- if args .sign :
153
- if version <= CHECKSUM_MIN_VERSION :
160
+ if args .command == 'sign' :
161
+ with contextlib .closing (args .input_file ) as handle :
162
+ try :
163
+ data , version , old_signature , actual_signature = eac_verify (handle .read ())
164
+ except ValueError as e :
165
+ print (args .input_file , ': ' , e , sep = '' )
166
+ sys .exit (1 )
167
+
168
+ if not args .force and (version is None or version <= CHECKSUM_MIN_VERSION ):
154
169
raise ValueError ('EAC version is too old to be signed' )
155
170
156
171
data += f'\r \n \r \n ==== Log checksum { actual_signature } ====\r \n '
157
172
158
- sys .stdout .buffer .write (b'\xff \xfe ' + data .encode ('utf-16le' ))
159
-
160
- if args .verify :
161
- if old_signature is None :
162
- print ('Not a signed log file' )
163
- sys .exit (1 )
164
- elif old_signature != actual_signature :
165
- print ('Malformed' )
166
- sys .exit (1 )
167
- elif version <= CHECKSUM_MIN_VERSION :
168
- print ('Forged' )
169
- sys .exit (1 )
170
- else :
171
- print ('OK' )
173
+ with contextlib .closing (args .output_file or args .input_file ) as handle :
174
+ handle .write (b'\xff \xfe ' + data .encode ('utf-16le' ))
175
+ elif args .command == 'verify' :
176
+ max_length = max (len (f .name ) for f in args .files )
177
+
178
+ for file in args .files :
179
+ prefix = (file .name + ':' ).ljust (max_length + 2 )
180
+
181
+ with contextlib .closing (file ) as handle :
182
+ try :
183
+ data , version , old_signature , actual_signature = eac_verify (handle .read ())
184
+ except RuntimeError as e :
185
+ print (prefix , e )
186
+ continue
187
+ except ValueError as e :
188
+ print (prefix , 'Not a log file' )
189
+ continue
190
+
191
+ if version is None :
192
+ print (prefix , 'Not a log file' )
193
+ elif old_signature is None :
194
+ print (prefix , 'Log file without a signature' )
195
+ elif old_signature != actual_signature :
196
+ print (prefix , 'Malformed' )
197
+ elif version <= CHECKSUM_MIN_VERSION :
198
+ print (prefix , 'Forged' )
199
+ else :
200
+ print (prefix , 'OK' )
0 commit comments