links:
http://repo.or.cz/w/python-cryptoplus.git
http://pastie.org/private/ykyyrbim7rkotvcnw8m8w
http://ps3devwiki.com/wiki/Talk:Keys...3.60_-_3.61.29
https://pypi.python.org/pypi/pycrypto/2.0.1
doubts:
1st doubt: solved. use 4th link. CryptoPlus works with that version of pycrypto ;)
doubts inpending :)
the unicorns:
alright. let me introduce myself, for those who don't know me from hax :) i'm zecoxao and i'm learning python with these codes :)
i encountered a few problems along the way but i hope we can get along and solve them. the community looks a lot more open and cared than it was a year ago. and i like that :).
i'll start posting doubts and hints right now. all the help is welcome.
Thank you guys (and girls ;) )
the code:
^ flat_z's magic unicorns
^ globaltroll's magic unicorns (unscrambler and scrambler respectively) :)
http://repo.or.cz/w/python-cryptoplus.git
http://pastie.org/private/ykyyrbim7rkotvcnw8m8w
http://ps3devwiki.com/wiki/Talk:Keys...3.60_-_3.61.29
https://pypi.python.org/pypi/pycrypto/2.0.1
doubts:
1st doubt: solved. use 4th link. CryptoPlus works with that version of pycrypto ;)
doubts inpending :)
the unicorns:
alright. let me introduce myself, for those who don't know me from hax :) i'm zecoxao and i'm learning python with these codes :)
i encountered a few problems along the way but i hope we can get along and solve them. the community looks a lot more open and cared than it was a year ago. and i like that :).
i'll start posting doubts and hints right now. all the help is welcome.
Thank you guys (and girls ;) )
the code:
Click here to see full text
Code:
# -*- coding: utf-8 -*-
import sys, os, math, datetime
import struct, hashlib, hmac, ecdsa
from CryptoPlus.Cipher import AES
#------------------------------------------------------------------------------
def aes_encrypt_ecb(key, data):
crypto = AES.new(key, AES.MODE_ECB)
return crypto.encrypt(data)
def aes_decrypt_ecb(key, data):
crypto = AES.new(key, AES.MODE_ECB)
return crypto.decrypt(data)
def aes_encrypt_cbc(key, iv, data):
crypto = AES.new(key, AES.MODE_CBC, iv)
return crypto.encrypt(data)
def aes_decrypt_cbc(key, iv, data):
crypto = AES.new(key, AES.MODE_CBC, iv)
return crypto.decrypt(data)
def aes_cmac(key, data):
crypto = AES.new(key, AES.MODE_CMAC)
return crypto.encrypt(data)
def calculate_sha1(data):
return hashlib.sha1(data).digest()
def sha1_hmac(key, data):
return hmac.new(key=key, msg=data, digestmod=hashlib.sha1).digest()
def load_file_contents(file_path):
with open(file_path, 'rb') as in_file:
data = in_file.read()
return data
def store_file_contents(file_path, data):
with open(file_path, 'wb') as out_file:
out_file.write(data)
def xor_data(data1, data2, size):
return ''.join(chr(ord(data1[i]) ^ ord(data2[i])) for i in xrange(size))
def s2i(s):
result = 0L
for c in s:
result = 256 * result + ord(c)
return result
def round_up(x, n):
return (x + (n - 1)) & ~(n - 1);
def get_ecdsa_curve_parameters(type, index):
if type == 'vsh':
curves = []
curve_fmt = '>20s20s20s20s20s20s'
with open('vsh.curves', 'rb') as in_file:
file_data = in_file.read()
file_size = len(file_data)
num_curves = int(file_size / struct.calcsize(curve_fmt))
for i in xrange(num_curves):
data = file_data[i * struct.calcsize(curve_fmt):(i + 1) * struct.calcsize(curve_fmt)]
inv_data = ''.join([chr((~ord(x)) & 0xff) for x in data])
p, a, b, n, gx, gy = struct.unpack(curve_fmt, inv_data)
curves.append({ 'p': s2i(p), 'a': s2i(a), 'b': s2i(b), 'n': s2i(n), 'gx': s2i(gx), 'gy': s2i(gy) })
params = curves[index]
return params['p'], params['a'], params['b'], params['n'], params['gx'], params['gy']
else:
return None
def dump(data, data_size, block_size=16):
if data_size == 0:
data_size = len(data)
num_blocks = int(math.floor(data_size / block_size))
for i in num_blocks:
print '\t', data[i * block_size:(i + 1) * block_size].encode('hex')
if len(sys.argv) < 3:
print 'usage: ps2.py <ISO.BIN.ENC or CONFIG or SCEVMC*.VME> <klicensee file>'
sys.exit()
CMAC_KEY_SIZE = 0x10
KLICENSEE_KEY_SIZE = 0x10
KLICENSEE_PART_OFFSET = 0x04
HEADER_OFFSET, HEADER_SIZE = 0x00, 0x60
EXTENDED_HEADER_OFFSET, EXTENDED_HEADER_SIZE = 0x00, 0xA0
SIGNATURE_DATA_OFFSET, SIGNATURE_DATA_SIZE = 0x00, 0xD8
META_DATA_SECTION_HASH_SIZE, META_DATA_SECTION_DATA_SIZE, META_DATA_SECTION_EXTENDED_HASH_SIZE = 0x10, 0x10, 0x14
PS2_DEFAULT_SEGMENT_SIZE, PS2_META_ENTRY_SIZE, PS2_VMC_HEADER_SIZE = 0x4000, 0x20, 0x20
NPDRM_OMAC_KEY1 = '????????????????????????????????'.decode('hex') # FIXME: SHA-1: 1DD100602ABC688AC7520EE8168B8B23B0563CF8
NPDRM_OMAC_KEY2 = '????????????????????????????????'.decode('hex') # FIXME: SHA-1: 07DAD1183CEC7ED834AFE8C7590E225484618D7F
NPDRM_OMAC_KEY3 = '????????????????????????????????'.decode('hex') # FIXME: SHA-1: 838633A7E03256F6CCE4E53D0842816013F8AECA
FALLBACK_HEADER_HASH = '00000000000000000000000000000001'.decode('hex')
ATA_KEY_SEED = '????????????????????????????????????????????????????????????????'.decode('hex') # FIXME: SHA-1: 362AA6F834600464B76ECC4E8CBFBEF99A76C1AF
PS2_KEYS = {
'cex': {
'meta': '????????????????????????????????'.decode('hex'), # FIXME: SHA-1: B9CACFF9E126F63634DC38AF61040BDF6F370A26
'data': '????????????????????????????????'.decode('hex'), # FIXME: SHA-1: CB0BAECAAADF9E5C629522B11757F78C7CD5B23C
'vmc': '????????????????????????????????'.decode('hex') # FIXME: SHA-1: EB03D83F96E3394A05BCE68F8645DA134CDA5545
},
'dex': {
'meta': '????????????????????????????????'.decode('hex'), # FIXME: SHA-1: 4FCFB6683AC46E73FFFCE49895E3F303A117BE8C
'data': '????????????????????????????????'.decode('hex'), # FIXME: SHA-1: AEC7A9C13A4023FE268A163FFDC8382F45496928
'vmc': '????????????????????????????????'.decode('hex') # FIXME: SHA-1: B41AEE9D3B6C54292469C9C754AE8FE75ACBE958
}
}
PS2_IV = '00000000000000000000000000000000'.decode('hex')
ECDSA_CURVE_TYPE = 'vsh'
ECDSA_CURVE_INDEX = 2
ECDSA_PUBLIC_KEY = '????????????????????????????????????????????????????????????????????????????????'.decode('hex') # FIXME: SHA-1: 7B365A6A821FC03B1A9A764E5E695DB3599FF7BC
BASE_TICKS = 62135596800000000 # 01/01/1970
#------------------------------------------------------------------------------
input_file_path = sys.argv[1]
klicensee_file_path = sys.argv[2]
klicensee_key = load_file_contents(klicensee_file_path)
if len(klicensee_key) != KLICENSEE_KEY_SIZE:
print 'Incorrect klicensee key size'
sys.exit()
console_type = 'cex'
ecdsa_signature_fmt = '>20s20s'
ecdsa_curve_p, ecdsa_curve_a, ecdsa_curve_b, ecdsa_curve_n, ecdsa_curve_gx, ecdsa_curve_gy = get_ecdsa_curve_parameters(ECDSA_CURVE_TYPE, ECDSA_CURVE_INDEX)
ecdsa_curve = ecdsa.ellipticcurve.CurveFp(ecdsa_curve_p, ecdsa_curve_a, ecdsa_curve_b)
ecdsa_generator = ecdsa.ellipticcurve.Point(ecdsa_curve, ecdsa_curve_gx, ecdsa_curve_gy, ecdsa_curve_n)
ecdsa_public_qx, ecdsa_public_qy = struct.unpack(ecdsa_signature_fmt, ECDSA_PUBLIC_KEY)
ecdsa_public_qx, ecdsa_public_qy = s2i(ecdsa_public_qx), s2i(ecdsa_public_qy)
ecdsa_public_point = ecdsa.ellipticcurve.Point(ecdsa_curve, ecdsa_public_qx, ecdsa_public_qy)
ecdsa_public_key = ecdsa.ecdsa.Public_key(ecdsa_generator, ecdsa_public_point)
#------------------------------------------------------------------------------
NPD_MAGIC = 'NPD\x00'
PS2_MAGIC = 'PS2\x00'
PS2_VMC_MAGIC = 'Sony PS2 Memory Card Format'
FILE_FLAGS_DEBUG = 0x80000000
magic_fmt = '>4s'
header_fmt = '>4sHHII48s16s16s16sQQIIQ16s16s40s20s20s'
meta_data_section_fmt = '>16s'
meta_data_section_extended_fmt = '>16sQIII'
input_file_dir, input_file_name = os.path.split(input_file_path)
input_file_extension = os.path.splitext(input_file_path)[1]
if input_file_extension.upper() != '.VME':
with open(input_file_path, 'rb') as npd_file:
magic, = struct.unpack(magic_fmt, npd_file.read(struct.calcsize(magic_fmt)))
if magic != NPD_MAGIC and magic != PS2_MAGIC:
print 'Not a NPD/PS2 file'
sys.exit()
npd_file.seek(HEADER_OFFSET)
header = npd_file.read(HEADER_SIZE)
npd_file.seek(EXTENDED_HEADER_OFFSET)
extended_header = npd_file.read(EXTENDED_HEADER_SIZE)
npd_file.seek(SIGNATURE_DATA_OFFSET)
signature_data = npd_file.read(SIGNATURE_DATA_SIZE)
npd_file.seek(0)
magic, version_major, version_minor, license_type, type, content_id, qa_digest, cid_fn_hash, header_hash, time_period_start, time_period_end, file_flags, segment_size, data_size, meta_data_sections_hash, extended_header_hash, unknown_data, signature_r, signature_s = struct.unpack(header_fmt, npd_file.read(struct.calcsize(header_fmt)))
version = (version_major << 16) | version_minor
is_ps2_format = magic == PS2_MAGIC
# Validate hashes
if (file_flags & FILE_FLAGS_DEBUG) == 0:
computed_cid_fn_hash = aes_cmac(NPDRM_OMAC_KEY3, content_id + input_file_name)
computed_header_hash = aes_cmac(xor_data(NPDRM_OMAC_KEY1, NPDRM_OMAC_KEY2, CMAC_KEY_SIZE), header)
else:
if type == 1:
computed_header_hash = map(ord, FALLBACK_HEADER_HASH)
else:
computed_header_hash = map(ord, klicensee_key)
for i in xrange(struct.calcsize(header_hash_fmt)):
computed_header_hash[i] = computed_header_hash[i] ^ ord(qa_digest[i]) ^ 0x55
computed_header_hash = ''.join(map(chr, computed_header_hash))
computed_cid_fn_hash = ''.join(chr(ord(x) ^ 0xFF) for x in qa_digest)
if (file_flags & FILE_FLAGS_DEBUG) == 0:
if cid_fn_hash != computed_cid_fn_hash:
print 'CID FN hash is not correct'
sys.exit()
if header_hash != computed_header_hash:
print 'Header hash is not correct'
sys.exit()
content_id = content_id.rstrip('\x00')
if time_period_start != 0:
time_period_start = datetime.datetime(1, 1, 1) + datetime.timedelta(microseconds=BASE_TICKS + time_period_start * 1000)
if time_period_end != 0:
time_period_end = datetime.datetime(1, 1, 1) + datetime.timedelta(microseconds=BASE_TICKS + time_period_end * 1000)
if not is_ps2_format:
# TODO
meta_data_size = 0
else:
meta_data_size = segment_size
num_segments = int(math.ceil((data_size + segment_size - 1) / segment_size))
last_segment_size = round_up(data_size - segment_size * (num_segments - 1), 16)
# Validate ECDSA signature
if (file_flags & FILE_FLAGS_DEBUG) == 0:
signature_hash = calculate_sha1(signature_data)
if not ecdsa_public_key.verifies(s2i(signature_hash), ecdsa.ecdsa.Signature(s2i(signature_r), s2i(signature_s))):
print 'ECDSA signature is not correct'
sys.exit()
if magic == NPD_MAGIC:
print ' Usage: NPD'
elif magic == PS2_MAGIC:
print ' Usage: PS2'
print ' Version: {0:02}.{1:02}'.format(version_major, version_minor)
if license_type == 1:
print 'License type: Network'
elif license_type == 2:
print 'License type: Local'
elif license_type == 3:
print 'License type: Free'
print ' Type: {0}'.format(type)
print ' Content ID: {0}'.format(content_id)
print ' QA digest: 0x{0}'.format(qa_digest.encode('hex').upper())
print ' File flags: 0x{0:08X}'.format(file_flags)
print 'Segment size: {0}'.format(segment_size)
print ' Data size: {0}'.format(data_size)
print 'Num segments: {0} (last segment size: {1})'.format(num_segments, last_segment_size)
if time_period_start != 0:
print ' Time start: {0}'.format(str(time_period_start))
if time_period_end != 0:
print ' Time end: {0}'.format(str(time_period_end))
if not is_ps2_format:
# TODO
pass
else:
ps2_meta_key = aes_encrypt_cbc(PS2_KEYS[console_type]['meta'], PS2_IV, klicensee_key)
ps2_data_key = aes_encrypt_cbc(PS2_KEYS[console_type]['data'], PS2_IV, klicensee_key)
output_meta_file_path = os.path.join(input_file_dir, input_file_name + '.meta')
output_data_file_path = os.path.join(input_file_dir, input_file_name + '.data')
npd_file.seek(segment_size)
with open(output_meta_file_path, 'wb') as meta_file:
with open(output_data_file_path, 'wb') as data_file:
while True:
meta_data = npd_file.read(meta_data_size)
if not meta_data:
break
meta_data = aes_decrypt_cbc(ps2_meta_key, PS2_IV, meta_data)
meta_file.write(meta_data)
num_child_segments = int(meta_data_size / PS2_META_ENTRY_SIZE)
for i in xrange(num_child_segments):
file_data = npd_file.read(segment_size)
if not file_data:
break
file_data = aes_decrypt_cbc(ps2_data_key, PS2_IV, file_data)
data_file.write(file_data)
data_file.truncate(data_size)
else:
with open(input_file_path, 'rb') as npd_file:
segment_size = PS2_DEFAULT_SEGMENT_SIZE
offset = npd_file.tell()
file_data = npd_file.read(PS2_VMC_HEADER_SIZE)
if not file_data:
print 'Unable to read file'
sys.exit()
magic = aes_decrypt_cbc(PS2_KEYS[console_type]['vmc'], PS2_IV, file_data)
if magic[:len(PS2_VMC_MAGIC)] != PS2_VMC_MAGIC:
print 'Invalid virtual memory card file'
sys.exit()
npd_file.seek(offset)
output_file_path = os.path.join(input_file_dir, input_file_name + '.vmc')
with open(output_file_path, 'wb') as out_file:
while True:
file_data = npd_file.read(segment_size)
if not file_data:
break
file_data = aes_decrypt_cbc(PS2_KEYS[console_type]['vmc'], PS2_IV, file_data)
out_file.write(file_data)
^ flat_z's magic unicorns
Click here to see full text
Code:
from CryptoPlus.Cipher import AES
import hashlib, hmac
def sha1_hmac(key, data):
return hmac.new(key=key, msg=data, digestmod=hashlib.sha1).digest()
def aes_decrypt_cbc(key, iv, data):
crypto = AES.new(key, AES.MODE_CBC, iv)
return crypto.decrypt(data)
def unscramble(key, iv, data):
key = sha1_hmac(root_scramble_key, key)
return aes_decrypt_cbc(key[:16], iv, data)
def unscramble_1(key):
return unscramble(sk1_key, sk1_iv, key)
def unscramble_2(key):
return unscramble(sk2_key, sk2_iv, key)
root_scramble_key = '844F80F3C57C455C7F09'.decode('hex')
sk1_key = 'F92C8666EFFBAC7EB583E54A257F7C05'.decode('hex')
sk1_iv = 'F2E70BC4BAC10C3D8DDBB7DC23053F9A'.decode('hex')
sk2_key = 'DDF6A5B143C1141FEED01CDA719705C3'.decode('hex')
sk2_iv = '018E695C3A29AF6E746A73CBF73DBDFD'.decode('hex')
erk_obf = '18097966C3DE8A0D82BF956CB39FAF782295C6CA7F1E547AB30EDFD7EE5CB812'.decode('hex')
riv_obf = '9B32B20FA77280F1095EA13F1C2D5C99'.decode('hex')
erk_dec = unscramble_1(erk_obf)
riv_dec = unscramble_2(riv_obf)
print 'erk_dec:', erk_dec.encode('hex')
print 'riv_dec:', riv_dec.encode('hex')
Click here to see full text
Code:
from CryptoPlus.Cipher import AES
import hashlib, hmac
def sha1_hmac(key, data):
return hmac.new(key=key, msg=data, digestmod=hashlib.sha1).digest()
def aes_encrypt_cbc(key, iv, data):
crypto = AES.new(key, AES.MODE_CBC, iv)
return crypto.encrypt(data)
def scramble(key, iv, data):
key = sha1_hmac(root_scramble_key, key)
return aes_encrypt_cbc(key[:16], iv, data)
def scramble_1(key):
return scramble(sk1_key, sk1_iv, key)
def scramble_2(key):
return scramble(sk2_key, sk2_iv, key)
root_scramble_key = '844F80F3C57C455C7F09'.decode('hex')
sk1_key = 'F92C8666EFFBAC7EB583E54A257F7C05'.decode('hex')
sk1_iv = 'F2E70BC4BAC10C3D8DDBB7DC23053F9A'.decode('hex')
sk2_key = 'DDF6A5B143C1141FEED01CDA719705C3'.decode('hex')
sk2_iv = '018E695C3A29AF6E746A73CBF73DBDFD'.decode('hex')
erk_dec = '5FF17D836E2C4AD69476E2614F64BDD05B9115389A9A6D055B5B544B1C34E3D5'.decode('hex')
riv_dec = 'DF0F50EC3C4743C5B17839D7B49F24A4'.decode('hex')
erk_obf = scramble_1(erk_dec)
riv_obf = scramble_2(riv_dec)
print 'erk_obf:', erk_obf.encode('hex')
print 'riv_obf:', riv_obf.encode('hex')
^ globaltroll's magic unicorns (unscrambler and scrambler respectively) :)