1
- #!/usr/bin/python
1
+ #!/usr/bin/env python3
2
2
3
3
import sys
4
4
import argparse
5
5
import contextlib
6
+ import json
6
7
7
8
CHECKSUM_MIN_VERSION = ('V1.0' , 'beta' , '1' )
8
9
@@ -13,12 +14,15 @@ def BYTE4(n):
13
14
def BYTE3 (n ):
14
15
return (n & 0x00FF0000 ) >> 16
15
16
17
+
16
18
def BYTE2 (n ):
17
19
return (n & 0x0000FF00 ) >> 8
18
20
21
+
19
22
def BYTE1 (n ):
20
23
return (n & 0x000000FF ) >> 0
21
24
25
+
22
26
def rotate_right (n ):
23
27
return ((n & 0x000000FF ) << 24 ) | (n >> 8 )
24
28
@@ -94,6 +98,9 @@ def eac_checksum(text):
94
98
95
99
96
100
def extract_info (text ):
101
+ if len (text ) == 0 :
102
+ return text , None , None
103
+
97
104
version = text .splitlines ()[0 ]
98
105
99
106
if not version .startswith ('Exact Audio Copy' ):
@@ -110,8 +117,12 @@ def extract_info(text):
110
117
return text , version , signature
111
118
112
119
113
- def eac_verify (data ):
114
- # Log is encoded as Little Endian UTF-16
120
+ def eac_verify (text ):
121
+ unsigned_text , version , old_signature = extract_info (text )
122
+ return unsigned_text , version , old_signature , eac_checksum (unsigned_text )
123
+
124
+
125
+ def get_logs (data ):
115
126
text = data .decode ('utf-16-le' )
116
127
117
128
# Strip off the BOM
@@ -121,14 +132,12 @@ def eac_verify(data):
121
132
# Null bytes screw it up
122
133
if '\x00 ' in text :
123
134
text = text [:text .index ('\x00 ' )]
124
-
135
+
125
136
# EAC crashes if there are more than 2^14 bytes in a line
126
137
if any (len (l ) + 1 > 2 ** 13 for l in text .split ('\n ' )):
127
138
raise RuntimeError ('EAC cannot handle lines longer than 2^13 chars' )
128
139
129
- unsigned_text , version , old_signature = extract_info (text )
130
-
131
- return unsigned_text , version , old_signature , eac_checksum (unsigned_text )
140
+ return [x .strip () for x in text .split ('-' * 60 )]
132
141
133
142
134
143
class FixedFileType (argparse .FileType ):
@@ -144,57 +153,43 @@ def __call__(self, string):
144
153
145
154
if __name__ == '__main__' :
146
155
parser = argparse .ArgumentParser (description = 'Verifies and resigns EAC logs' )
156
+ parser .add_argument ('--json' , action = 'store_true' , help = 'Output as JSON' )
157
+ parser .add_argument ('files' , type = FixedFileType (mode = 'rb' ), nargs = '+' , help = 'input log file(s)' )
147
158
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)' )
159
+ args = parser .parse_args ()
152
160
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' )
161
+ max_length = max (len (f .name ) for f in args .files )
157
162
158
- args = parser .parse_args ()
163
+ cnt = 1
164
+ output = {}
165
+ print ('Log Integrity Checker (C) 2010 by Andre Wiethoff' )
166
+ for file in args .files :
167
+ prefix = (file .name + ':' ).ljust (max_length + 2 )
159
168
160
- if args .command == 'sign' :
161
- with contextlib .closing (args .input_file ) as handle :
169
+ with contextlib .closing (file ) as open_file :
170
+ logs = get_logs (open_file .read ())
171
+ for log in logs :
162
172
try :
163
- data , version , old_signature , actual_signature = eac_verify (handle .read ())
173
+ data , version , old_signature , actual_signature = eac_verify (log )
174
+ except RuntimeError as e :
175
+ print (prefix , e )
176
+ continue
164
177
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 ):
169
- raise ValueError ('EAC version is too old to be signed' )
170
-
171
- data += f'\r \n \r \n ==== Log checksum { actual_signature } ====\r \n '
172
-
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
178
print (prefix , 'Not a log file' )
193
- elif old_signature is None :
194
- print (prefix , 'Log file without a signature' )
179
+ continue
180
+
181
+ if version is None or old_signature is None :
182
+ message = 'Log entry has no checksum!'
195
183
elif old_signature != actual_signature :
196
- print (prefix , 'Malformed' )
197
- elif version <= CHECKSUM_MIN_VERSION :
198
- print (prefix , 'Forged' )
184
+ message = 'Log entry was modified, checksum incorrect!'
199
185
else :
200
- print (prefix , 'OK' )
186
+ message = 'Log entry is fine!'
187
+
188
+ if args .json :
189
+ output [file .name ] = message
190
+ else :
191
+ print ('{:d}. {:s}' .format (cnt , message ))
192
+ cnt += 1
193
+
194
+ if args .json :
195
+ print (json .dumps (output ))
0 commit comments