whitespace fixed
This commit is contained in:
parent
3495efc8be
commit
db50027ea8
252
msr90.py
252
msr90.py
@ -7,162 +7,162 @@ import sys
|
|||||||
|
|
||||||
|
|
||||||
def format_pan(pan: str) -> str:
|
def format_pan(pan: str) -> str:
|
||||||
'''
|
'''
|
||||||
Format the Primary Account Number (PAN) by grouping the digits in sets of 4
|
Format the Primary Account Number (PAN) by grouping the digits in sets of 4
|
||||||
|
|
||||||
:param pan: The Primary Account Number (PAN) to format
|
|
||||||
'''
|
|
||||||
|
|
||||||
return ' '.join(pan[i:i+4] for i in range(0, len(pan), 4))
|
:param pan: The Primary Account Number (PAN) to format
|
||||||
|
'''
|
||||||
|
|
||||||
|
return ' '.join(pan[i:i+4] for i in range(0, len(pan), 4))
|
||||||
|
|
||||||
|
|
||||||
def format_exp_date(exp_date: str) -> str:
|
def format_exp_date(exp_date: str) -> str:
|
||||||
'''
|
'''
|
||||||
Format the expiration date by to be MM/YY
|
Format the expiration date by to be MM/YY
|
||||||
|
|
||||||
:param exp_date: The expiration date to format'''
|
|
||||||
|
|
||||||
return exp_date[2:4] + '/' + exp_date[0:2]
|
:param exp_date: The expiration date to format'''
|
||||||
|
|
||||||
|
return exp_date[2:4] + '/' + exp_date[0:2]
|
||||||
|
|
||||||
|
|
||||||
def service_code_descriptions(digit, code):
|
def service_code_descriptions(digit, code):
|
||||||
'''
|
'''
|
||||||
Get the description for the service code digit
|
Get the description for the service code digit
|
||||||
|
|
||||||
:param digit: The digit number (1, 2, or 3)
|
|
||||||
:param code: The code value for the digit
|
|
||||||
'''
|
|
||||||
|
|
||||||
descriptions = {
|
:param digit: The digit number (1, 2, or 3)
|
||||||
'1': {
|
:param code: The code value for the digit
|
||||||
'1': 'International interchange OK',
|
'''
|
||||||
'2': 'International interchange, use IC (chip) where feasible',
|
|
||||||
'5': 'National interchange only except under bilateral agreement',
|
|
||||||
'6': 'National interchange only, use IC (chip) where feasible',
|
|
||||||
'7': 'No interchange except under bilateral agreement (closed loop)',
|
|
||||||
'9': 'Test'
|
|
||||||
},
|
|
||||||
'2': {
|
|
||||||
'0': 'Normal',
|
|
||||||
'2': 'Contact issuer via online means',
|
|
||||||
'4': 'Contact issuer via online means except under bilateral agreement'
|
|
||||||
},
|
|
||||||
'3': {
|
|
||||||
'0': 'No restrictions, PIN required',
|
|
||||||
'1': 'No restrictions',
|
|
||||||
'2': 'Goods and services only (no cash)',
|
|
||||||
'3': 'ATM only, PIN required',
|
|
||||||
'4': 'Cash only',
|
|
||||||
'5': 'Goods and services only (no cash), PIN required',
|
|
||||||
'6': 'No restrictions, use PIN where feasible',
|
|
||||||
'7': 'Goods and services only (no cash), use PIN where feasible'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptions[str(digit)].get(code, 'Unknown')
|
descriptions = {
|
||||||
|
'1': {
|
||||||
|
'1': 'International interchange OK',
|
||||||
|
'2': 'International interchange, use IC (chip) where feasible',
|
||||||
|
'5': 'National interchange only except under bilateral agreement',
|
||||||
|
'6': 'National interchange only, use IC (chip) where feasible',
|
||||||
|
'7': 'No interchange except under bilateral agreement (closed loop)',
|
||||||
|
'9': 'Test'
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
'0': 'Normal',
|
||||||
|
'2': 'Contact issuer via online means',
|
||||||
|
'4': 'Contact issuer via online means except under bilateral agreement'
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
'0': 'No restrictions, PIN required',
|
||||||
|
'1': 'No restrictions',
|
||||||
|
'2': 'Goods and services only (no cash)',
|
||||||
|
'3': 'ATM only, PIN required',
|
||||||
|
'4': 'Cash only',
|
||||||
|
'5': 'Goods and services only (no cash), PIN required',
|
||||||
|
'6': 'No restrictions, use PIN where feasible',
|
||||||
|
'7': 'Goods and services only (no cash), use PIN where feasible'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptions[str(digit)].get(code, 'Unknown')
|
||||||
|
|
||||||
|
|
||||||
def parse_magnetic_stripe(data: str):
|
def parse_magnetic_stripe(data: str):
|
||||||
'''
|
'''
|
||||||
Parse the magnetic stripe data and print the results
|
Parse the magnetic stripe data and print the results
|
||||||
|
|
||||||
:param data: The raw magnetic stripe data to parse
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Patterns for specific track data parsing
|
:param data: The raw magnetic stripe data to parse
|
||||||
track1_pattern = r'^%([AB])(\d{1,19})\^([^\^]{2,26})\^(\d{4})(\d{3})([^\?]*?)\?(\w?)'
|
'''
|
||||||
track2_pattern = r';(\d{1,19})=(\d{4})(\d{3})(.*?)\?$'
|
|
||||||
|
|
||||||
# Generic patterns to capture raw data if specific parsing fails
|
# Patterns for specific track data parsing
|
||||||
generic_track1_pattern = r'(%[AB][^\?]+\?)'
|
track1_pattern = r'^%([AB])(\d{1,19})\^([^\^]{2,26})\^(\d{4})(\d{3})([^\?]*?)\?(\w?)'
|
||||||
generic_track2_pattern = r'(;[^\?]+\?)'
|
track2_pattern = r';(\d{1,19})=(\d{4})(\d{3})(.*?)\?$'
|
||||||
generic_track3_pattern = r'(\+[^\?]+\?)'
|
|
||||||
|
|
||||||
# Attempt to match the track data
|
# Generic patterns to capture raw data if specific parsing fails
|
||||||
track1_match = re.search(track1_pattern, data)
|
generic_track1_pattern = r'(%[AB][^\?]+\?)'
|
||||||
track2_match = re.search(track2_pattern, data)
|
generic_track2_pattern = r'(;[^\?]+\?)'
|
||||||
track3_match = re.search(generic_track3_pattern, data)
|
generic_track3_pattern = r'(\+[^\?]+\?)'
|
||||||
|
|
||||||
# Track 1 specific parsing
|
# Attempt to match the track data
|
||||||
if track1_match:
|
track1_match = re.search(track1_pattern, data)
|
||||||
format_code, pan, name, exp_date, service_code, discretionary_data, lrc = track1_match.groups()
|
track2_match = re.search(track2_pattern, data)
|
||||||
if format_code == 'A':
|
track3_match = re.search(generic_track3_pattern, data)
|
||||||
print('Error: Unsupported format code \'A\'. Exiting.')
|
|
||||||
sys.exit(1)
|
|
||||||
formatted_pan = format_pan(pan)
|
|
||||||
formatted_exp_date = format_exp_date(exp_date)
|
|
||||||
desc1 = service_code_descriptions(1, service_code[0])
|
|
||||||
desc2 = service_code_descriptions(2, service_code[1])
|
|
||||||
desc3 = service_code_descriptions(3, service_code[2])
|
|
||||||
print_fields('Track 1 Data', [
|
|
||||||
('Format Code', f'{format_code} - Financial cards (ISO/IEC 7813)'),
|
|
||||||
('Primary Account Number (PAN)', formatted_pan),
|
|
||||||
('Cardholder Name', name),
|
|
||||||
('Expiration Date', formatted_exp_date),
|
|
||||||
('Service Code Digit 1', f'{service_code[0]}: {desc1}'),
|
|
||||||
('Service Code Digit 2', f'{service_code[1]}: {desc2}'),
|
|
||||||
('Service Code Digit 3', f'{service_code[2]}: {desc3}'),
|
|
||||||
('Discretionary Data', discretionary_data),
|
|
||||||
('LRC (optional)', lrc),
|
|
||||||
('Raw Track Data', track1_match.group(0))
|
|
||||||
], '\033[93m')
|
|
||||||
|
|
||||||
# Fallback generic track 1
|
# Track 1 specific parsing
|
||||||
elif re.search(generic_track1_pattern, data):
|
if track1_match:
|
||||||
print('Track 1 Data:')
|
format_code, pan, name, exp_date, service_code, discretionary_data, lrc = track1_match.groups()
|
||||||
print('Raw: ' + re.search(generic_track1_pattern, data).group(1))
|
if format_code == 'A':
|
||||||
|
print('Error: Unsupported format code \'A\'. Exiting.')
|
||||||
|
sys.exit(1)
|
||||||
|
formatted_pan = format_pan(pan)
|
||||||
|
formatted_exp_date = format_exp_date(exp_date)
|
||||||
|
desc1 = service_code_descriptions(1, service_code[0])
|
||||||
|
desc2 = service_code_descriptions(2, service_code[1])
|
||||||
|
desc3 = service_code_descriptions(3, service_code[2])
|
||||||
|
print_fields('Track 1 Data', [
|
||||||
|
('Format Code', f'{format_code} - Financial cards (ISO/IEC 7813)'),
|
||||||
|
('Primary Account Number (PAN)', formatted_pan),
|
||||||
|
('Cardholder Name', name),
|
||||||
|
('Expiration Date', formatted_exp_date),
|
||||||
|
('Service Code Digit 1', f'{service_code[0]}: {desc1}'),
|
||||||
|
('Service Code Digit 2', f'{service_code[1]}: {desc2}'),
|
||||||
|
('Service Code Digit 3', f'{service_code[2]}: {desc3}'),
|
||||||
|
('Discretionary Data', discretionary_data),
|
||||||
|
('LRC (optional)', lrc),
|
||||||
|
('Raw Track Data', track1_match.group(0))
|
||||||
|
], '\033[93m')
|
||||||
|
|
||||||
print('\n')
|
# Fallback generic track 1
|
||||||
|
elif re.search(generic_track1_pattern, data):
|
||||||
|
print('Track 1 Data:')
|
||||||
|
print('Raw: ' + re.search(generic_track1_pattern, data).group(1))
|
||||||
|
|
||||||
# Track 2 specific parsing
|
print('\n')
|
||||||
if track2_match:
|
|
||||||
pan, exp_date, service_code, discretionary_data = track2_match.groups()
|
|
||||||
formatted_pan = format_pan(pan)
|
|
||||||
formatted_exp_date = format_exp_date(exp_date)
|
|
||||||
print_fields('Track 2 Data', [
|
|
||||||
('Primary Account Number (PAN)', formatted_pan),
|
|
||||||
('Expiration Date', formatted_exp_date),
|
|
||||||
('Service Code', service_code),
|
|
||||||
('Discretionary Data', discretionary_data),
|
|
||||||
('Raw Track Data', track2_match.group(0))
|
|
||||||
], '\033[96m')
|
|
||||||
|
|
||||||
# Fallback generic track 2
|
# Track 2 specific parsing
|
||||||
elif re.search(generic_track2_pattern, data):
|
if track2_match:
|
||||||
print('Track 2 Data:')
|
pan, exp_date, service_code, discretionary_data = track2_match.groups()
|
||||||
print('Raw: ' + re.search(generic_track2_pattern, data).group(1))
|
formatted_pan = format_pan(pan)
|
||||||
|
formatted_exp_date = format_exp_date(exp_date)
|
||||||
|
print_fields('Track 2 Data', [
|
||||||
|
('Primary Account Number (PAN)', formatted_pan),
|
||||||
|
('Expiration Date', formatted_exp_date),
|
||||||
|
('Service Code', service_code),
|
||||||
|
('Discretionary Data', discretionary_data),
|
||||||
|
('Raw Track Data', track2_match.group(0))
|
||||||
|
], '\033[96m')
|
||||||
|
|
||||||
print('\n')
|
# Fallback generic track 2
|
||||||
|
elif re.search(generic_track2_pattern, data):
|
||||||
|
print('Track 2 Data:')
|
||||||
|
print('Raw: ' + re.search(generic_track2_pattern, data).group(1))
|
||||||
|
|
||||||
# Track 3 generic data if found
|
print('\n')
|
||||||
if track3_match:
|
|
||||||
print('Track 3 Data:')
|
# Track 3 generic data if found
|
||||||
print('Raw: ' + track3_match.group(1))
|
if track3_match:
|
||||||
else:
|
print('Track 3 Data:')
|
||||||
print('Track 3 Data: No data found.')
|
print('Raw: ' + track3_match.group(1))
|
||||||
|
else:
|
||||||
|
print('Track 3 Data: No data found.')
|
||||||
|
|
||||||
|
|
||||||
def print_fields(title: str, fields: list, color_code: str):
|
def print_fields(title: str, fields: list, color_code: str):
|
||||||
'''
|
'''
|
||||||
Print the fields in a formatted table
|
Print the fields in a formatted table
|
||||||
|
|
||||||
:param title: The title of the table
|
|
||||||
:param fields: The fields to print
|
|
||||||
:param color_code: The color code to use for the table
|
|
||||||
'''
|
|
||||||
|
|
||||||
max_len = max(len(name) for name, _ in fields)
|
:param title: The title of the table
|
||||||
|
:param fields: The fields to print
|
||||||
|
:param color_code: The color code to use for the table
|
||||||
|
'''
|
||||||
|
|
||||||
print(title)
|
max_len = max(len(name) for name, _ in fields)
|
||||||
|
|
||||||
for name, value in fields:
|
print(title)
|
||||||
print(color_code + name.ljust(max_len) + '\033[90m | \033[0m' + value)
|
|
||||||
|
for name, value in fields:
|
||||||
|
print(color_code + name.ljust(max_len) + '\033[90m | \033[0m' + value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
swipe_data = getpass.getpass(prompt='Please swipe the card: ')
|
swipe_data = getpass.getpass(prompt='Please swipe the card: ')
|
||||||
parse_magnetic_stripe(swipe_data)
|
parse_magnetic_stripe(swipe_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Error:', e)
|
print('Error:', e)
|
||||||
|
Loading…
Reference in New Issue
Block a user