Created
June 10, 2024 09:26
-
-
Save dunderhay/1831bacf30928632309396d23618c5f0 to your computer and use it in GitHub Desktop.
Python script to encode / decode Gallagher card credentials
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import binascii | |
import argparse | |
class GallagherCredentials: | |
def __init__(self, region_code, facility_code, card_number, issue_level): | |
self.region_code = region_code | |
self.facility_code = facility_code | |
self.card_number = card_number | |
self.issue_level = issue_level | |
def scramble(arr): | |
lut = [ | |
0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c, 0x05, | |
0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04, 0x51, 0x2e, | |
0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a, 0x6d, 0xa4, 0x00, | |
0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65, 0x57, 0x7c, 0x20, 0xfa, | |
0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18, 0xbe, 0x21, 0x72, 0x48, 0xb6, | |
0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53, 0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, | |
0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98, 0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, | |
0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c, 0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, | |
0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5, 0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, | |
0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32, 0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, | |
0x52, 0x1d, 0xc3, 0x75, 0xcf, 0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, | |
0x06, 0xd9, 0x25, 0x9e, 0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, | |
0xd5, 0xb3, 0x68, 0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, | |
0x8e, 0x6a, 0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, | |
0x6b, 0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64, | |
0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3, 0x90 | |
] | |
return [lut[b] for b in arr] | |
def descramble(arr): | |
lut = [ | |
0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0x0f, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11, 0x6d, | |
0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1, 0x62, 0x1a, | |
0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3, 0xae, 0x1f, 0x9b, | |
0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19, 0x0e, 0xa9, 0xe2, 0x8d, | |
0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0, 0xe8, 0xa6, 0x20, 0xab, 0x87, | |
0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0x0c, 0xc3, 0x3c, 0x61, 0xcc, 0x40, 0x9e, 0x06, 0x52, 0x1b, 0x32, | |
0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0x0d, 0xc2, 0x88, 0xd1, 0xe0, 0x07, 0x2d, 0x70, 0xc6, | |
0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b, 0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, | |
0x02, 0x08, 0x30, 0xa7, 0x22, 0xc9, 0x65, 0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, | |
0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f, 0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, | |
0x51, 0xd4, 0x81, 0x00, 0x2e, 0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, | |
0x01, 0x48, 0x04, 0xc1, 0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0x0a, 0xac, 0x18, 0x4b, 0xcd, | |
0x98, 0xb8, 0x37, 0xa2, 0x83, 0xec, 0x03, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, | |
0x43, 0xe9, 0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, | |
0x89, 0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0x0b, 0xf1, 0xea, 0xfd, 0xdb, | |
0xbd, 0x09, 0xb5, 0x5b, 0x05, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e, 0x36 | |
] | |
return [lut[b] for b in arr] | |
def decode_creds(hex_card_number): | |
eight_bytes = bytearray(binascii.unhexlify(hex_card_number)) | |
eight_bytes = descramble(eight_bytes) | |
region_code = (eight_bytes[3] & 0x1E) >> 1 | |
facility_code = (eight_bytes[5] & 0x0F) << 12 | eight_bytes[1] << 4 | ((eight_bytes[7] >> 4) & 0x0F) | |
card_number = eight_bytes[0] << 16 | (eight_bytes[4] & 0x1F) << 11 | eight_bytes[2] << 3 | (eight_bytes[3] & 0xE0) >> 5 | |
issue_level = eight_bytes[7] & 0x0F | |
return GallagherCredentials(region_code, facility_code, card_number, issue_level) | |
def encode_creds(region_code, facility_code, card_number, issue_level): | |
eight_bytes = [0] * 8 | |
eight_bytes[0] = (card_number & 0xffffff) >> 16 | |
eight_bytes[1] = (facility_code & 0xfff) >> 4 | |
eight_bytes[2] = (card_number & 0x7ff) >> 3 | |
eight_bytes[3] = (card_number & 0x7) << 5 | (region_code & 0xf) << 1 | |
eight_bytes[4] = (card_number & 0xffff) >> 11 | |
eight_bytes[5] = (facility_code & 0xffff) >> 12 | |
eight_bytes[6] = 0 | |
eight_bytes[7] = (facility_code & 0xf) << 4 | (issue_level & 0xf) | |
eight_bytes = scramble(eight_bytes) | |
return binascii.hexlify(bytearray(eight_bytes)).decode('utf-8') | |
def inverse_hex(hex_str): | |
return ''.join(format(255 - int(hex_str[i:i+2], 16), '02x') for i in range(0, len(hex_str), 2)) | |
def main(): | |
parser = argparse.ArgumentParser(description='Gallagher Credential Encoder/Decoder') | |
parser.add_argument('-e', '--encode', action='store_true', help='Encode credential details to a hex card number') | |
parser.add_argument('-d', '--decode', action='store_true', help='Decode a hex card number') | |
parser.add_argument('-raw', '--hex-card-number', type=str, help='Hex card number to decode') | |
parser.add_argument('-rc', '--region-code', type=int, help='Region code (0-15)') | |
parser.add_argument('-fc', '--facility-code', type=int, help='Facility code (0-65535)') | |
parser.add_argument('-cn', '--card-number', type=int, help='Card number (0-16777215)') | |
parser.add_argument('-il', '--issue-level', type=int, help='Issue level (0-15)') | |
args = parser.parse_args() | |
if args.decode and args.hex_card_number: | |
creds = decode_creds(args.hex_card_number) | |
print(f"Region Code: {creds.region_code}") | |
print(f"Facility Code: {creds.facility_code}") | |
print(f"Card Number: {creds.card_number}") | |
print(f"Issue Level: {creds.issue_level}") | |
elif args.encode and all([args.region_code is not None, args.facility_code is not None, args.card_number is not None, args.issue_level is not None]): | |
hex_card_number = encode_creds(args.region_code, args.facility_code, args.card_number, args.issue_level) | |
print(f"Encoded Card Data: {hex_card_number.upper()}") | |
print(f"Write to mf block on proxmark: {hex_card_number.lower()}{inverse_hex(hex_card_number).lower()}") | |
else: | |
parser.print_help() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment