Skip to content

Instantly share code, notes, and snippets.

@bgeet
Created January 28, 2012 14:32
Show Gist options
  • Save bgeet/1694526 to your computer and use it in GitHub Desktop.
Save bgeet/1694526 to your computer and use it in GitHub Desktop.
Convert between binary mcas key files and human-readable mcf files
#!/usr/bin/env python
#Copyright (C) 2012 Anonymous
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
"""
Version 0.03
Convert between binary mcas and human-readable mcf files
Needs Python 2.7.x
Usage:
To convert binary new.key to human-readable new.mcf:
"mcas_mcf_converter.py -i new.key --to-mcf"
To convert human-readable new.mcf to binary new.key:
"mcas_mcf_converter.py -i new.mcf --to-mcas"
To use a different name for output:
"mcas_mcf_converter.py --to-mcf -i new.key -o test.mcf"
To display additional help:
"mcas_mcf_converter.py --help"
Note: Position of arguments does not matter
Warning: Will not check if output file already exists, but override
Speculated specifications for used formats:
mcas:
Hexfile padded to 64kB with 0x00, entries consist of these bytes:
1.Byte: Startcode(0x10)
2.Byte: Length of rest entry
3 till 5.Byte: Provider-ID
6 till 7.Byte: CAID
8.Byte: KeyType
9.Byte: KeyIndex
10 till (Length - 23).Byte: Key
(Length - 22) till (Length + 1).Byte: Comment(padded to 24 bytes with 0x00)
mcf:
Textfile in ANSI encoding;
An entry per line, consisting of these fields separated by spaces:
Provider-ID(3 Bytes Hex)
CAID(2 Bytes Hex)
Keytypedesc(as in keytypes)
KeyIndex(1 Byte Hex)
Key(Hex)
Comment(ASCII)
To-do:
Improve TypeError with more information
Handle Unicode encodings in mcf more gracefully
"""
import argparse
import binascii
import os
SIZE = 65536
keytypes = ["SYS__8B_KEY",
"SYS_16B_KEY",
"CNX_EXP_KEY",
"CNX_MOD_KEY",
"VIA_AES_KEY",
"DCW_ODD_KEY",
"DCW_EVN_KEY",
"CW_CE10_KEY",
"CW_CE20_KEY",
"CW_CE21_KEY",
"CW_CE30_KEY",
"CW_CE31_KEY",
"N1_PK_KEY",
"N2_IDEA_ECM",
"N2_IDEA_EMM",
"N2_RSA_ECM",
"N2_RSA_EMM",
"N2_VK_EMM_G",
"N2_VK_ECM",
"SYS_32B_KEY",
"CW_IPK_KEY",
"CW_CCCPK_KEY",
"CW_2F02_KEY",
"CNX_V40_KEY",
"CNX_V60_KEY",
"NAG2BOX_KEY",
"NAG2RSA_BOX",
"NAG3DES_KEY",
"IRDETOIV_KEY",
"IRDECMSEED",
"IRDEMMSEED",
"NDS_SEED_KEY"]
def override(my_input, my_output):
"""
Check if file exists and prompt user for action if
"""
if my_input == my_output:
raise IOError('Input and output file cannot be the same')
if os.path.isfile(my_output):
while True:
print (
'\nSpecified output {!r} already exists, override?'
.format(my_output))
print 'Press (y)es to continue, (n)o to abort'
action = raw_input('> ')
if action == 'y':
print 'Overriding'
os.remove(my_output)
break
elif action == 'n':
print 'Aborting'
exit()
def mcas_to_mcf(my_input, my_output):
"""
Converts binary mcas input to human-readable mcf output
"""
i = 0
string_out = ''
override(my_input, my_output)
if not os.path.getsize(my_input) == SIZE:
raise IOError(
'Invalid size of input: {}, expected {} bytes'
.format(os.path.getsize(my_input), SIZE))
with open(my_input, 'rb') as read_input:
hexdump = read_input.read()
while i < SIZE:
if hexdump[i] == '\x10':
length = ord(hexdump[i + 1])
provider_id = hexdump[i + 2:i + 5]
caid = hexdump[i + 5:i + 7]
keytypedesc_index = ord(hexdump[i + 7]) - 1
keytypedesc = keytypes[keytypedesc_index]
keyindex = hexdump[i + 8]
key = hexdump[i + 9:i + length - 22]
comment = hexdump[i + length - 22:i + length + 2]
if not i == 0:
string_out += '\n'
string_out += (
(binascii.hexlify(provider_id) + ' ' +
binascii.hexlify(caid) + ' ' +
keytypedesc + ' ' +
binascii.hexlify(keyindex) + ' ' +
binascii.hexlify(key)
).upper() + ' ' +
comment.strip('\x00'))
i += length + 2
elif hexdump[i] == '\x00':
for pos, j in enumerate(hexdump[i+1:]):
if not j == '\x00':
raise SyntaxError(
'Expected 0x00 padding, got 0x{} at position {} mixed in'
.format((binascii.hexlify(j)).upper(), i+1+pos))
i = SIZE
else:
raise SyntaxError(
'Invalid byte 0x{} at position {}'
.format((binascii.hexlify(hexdump[i])).upper(), i))
else:
with open(my_output, 'a') as write_output:
write_output.write(string_out)
print 'Done'
def mcf_to_mcas(my_input, my_output):
"""
Converts human-readable mcf input to binary mcas output
"""
total = 0
string_out = ''
override(my_input, my_output)
with open(my_input, 'r') as read_input:
for line_no, line in enumerate(read_input):
linelist = line.split()
if (line[0] is '#') or (not linelist):
continue
provider_id, caid, keytypedesc, keyindex, key = linelist[:5]
if not len(provider_id) == 6:
raise SyntaxError(
'Invalid length for Provider-ID in line {}, expected 6'
.format(line_no + 1))
if not len(caid) == 4:
raise SyntaxError(
'Invalid length for CAID in line {}, expected 4'
.format(line_no + 1))
if not len(keyindex) == 2:
raise SyntaxError(
'Invalid length for KeyIndex in line {}, expected 2'
.format(line_no + 1))
comment = ' '.join(linelist[5:])
keytype = chr(keytypes.index(keytypedesc) + 1)
length = 3 + 2 + 1 + 1 + len(key)/2 + 24
pad = 24 - len(comment)
if pad > 0:
padded_comment = comment + pad * '\x00'
elif pad < 1:
padded_comment = comment[:24]
try:
string_out += (
'\x10' +
chr(length) +
binascii.unhexlify(provider_id) +
binascii.unhexlify(caid) +
keytype +
binascii.unhexlify(keyindex) +
binascii.unhexlify(key) +
padded_comment)
except TypeError:
raise SyntaxError(
"Hex expected, but didn't get it somewhere in line {}"
.format(line_no + 1))
total += length + 2
if total == 0:
raise IOError('Invalid input')
elif SIZE < total:
raise IOError('Too many entries in input, please remove some')
elif SIZE >= total:
with open(my_output, 'wb') as write_output:
write_output.write(string_out)
write_output.write((SIZE - total) * '\x00')
print 'Done'
def process_command_line():
"""
Process command line and return arguments
"""
parser = argparse.ArgumentParser(description=
'%(prog)s - convert between binary mcas and human-readable mcf files')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--to-mcas', action='store_true',
help='converts mcf to mcas')
group.add_argument('--to-mcf', action='store_true',
help='converts mcas to mcf')
parser.add_argument('-i', '--input', required=True,
help='specifies input')
parser.add_argument('-o', '--output',
help="specifies output, if not set will use input's filename")
parser.add_argument('--version', action='version', version='%(prog)s 0.03')
return parser.parse_args()
def main():
args = process_command_line()
if args.to_mcas:
if args.output:
mcf_to_mcas(args.input, args.output)
else:
mcf_to_mcas(args.input, (os.path.splitext(args.input))[0] + '.key')
if args.to_mcf:
if args.output:
mcas_to_mcf(args.input, args.output)
else:
mcas_to_mcf(args.input, (os.path.splitext(args.input))[0] + '.mcf')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment