Skip to content

Instantly share code, notes, and snippets.

@MCMrARM
Last active June 17, 2018 19:14
Show Gist options
  • Save MCMrARM/87a3e71032b24e3bf43ece0490ef2c58 to your computer and use it in GitHub Desktop.
Save MCMrARM/87a3e71032b24e3bf43ece0490ef2c58 to your computer and use it in GitHub Desktop.
A Python script to decrypt Switch's SSL private key
# A Python script to decrypt Switch's SSL private key
# Thanks to roblabla for help
# Heavily based on Atmosphere's exosphere
# Usage: kek.py <source> <destination>
# Make sure to fill in the proper keys in the script
import struct
import sys
from Crypto.Cipher import AES
from Crypto.Util import Counter
master_keys = [
bytes.fromhex("## master_key_00 ##")
]
generate_aes_kek_seed = bytes.fromhex("## seed here ##")
decrypt_rsa_private_key_seed = bytes.fromhex("## seed here ##")
keygen_key = None
def aes_ecb_encrypt(data, key):
return AES.new(key, AES.MODE_ECB).encrypt(data)
def aes_ecb_decrypt(data, key):
return AES.new(key, AES.MODE_ECB).decrypt(data)
def aes_ctr_decrypt(data, ctr, key):
iv1, iv2 = struct.unpack(">QQ", ctr)
iv = (iv1 << 64) | iv2
return AES.new(key, AES.MODE_CTR, counter=Counter.new(128, initial_value=iv)).decrypt(data)
MASTERKEY_NUM_NEW_DEVICE_KEYS = 2
device_keys = None
# these seeds came from: https://github.com/Atmosphere-NX/Atmosphere/blob/05b8b4216404c9b73c03f15bfc2f0c22bf4aacb7/exosphere/src/package2.c#L26
new_device_key_sources = [
bytes.fromhex("8B4E1C224207C8735694088BCC470F5D"),
bytes.fromhex("6CEFC6278BEC8A9199AB24AC4F1C8F1C")
]
new_device_keygen_sources = [
bytes.fromhex("8862346EFAF7D83FE1303950F0B75D5D"),
bytes.fromhex("061E7BE96D478C77C5C8E7949AA85F2E")
]
def derive_new_device_keys(keygen_key):
global device_keys
device_keys = []
for revision in range(0, MASTERKEY_NUM_NEW_DEVICE_KEYS):
work_buffer = aes_ecb_decrypt(new_device_key_sources[revision], keygen_key)
temp_key = aes_ecb_decrypt(new_device_keygen_sources[revision], master_keys[0])
work_buffer = aes_ecb_decrypt(work_buffer, temp_key)
device_keys[revision] = work_buffer
# these seeds came from: https://github.com/Atmosphere-NX/Atmosphere/blob/c2eed3caf6b7d04e7e51f1b07ccf019eb4ef6fb6/exosphere/src/smc_user.c#L166
kek_seeds = [
bytes.fromhex("00000000000000000000000000000000"),
bytes.fromhex("A2ABBF9C922FBBE378799BC0CCEAA574"),
bytes.fromhex("57E2D945E492F4FDC3F9863889789F3C"),
bytes.fromhex("E54D9A02F04F5FA8AD760AF6329559BB")
]
kek_masks = [
bytes.fromhex("4D870986C45D20722FBA1053DA92E8A9"),
bytes.fromhex("250331FB25260B798C80D26998E22277"),
bytes.fromhex("76141D34932DE184247B666555046581"),
bytes.fromhex("AF3DB7F308A2D8A208CA18A86946C90B")
]
def generate_aes_kek(wrapped_kek, mkey_rev, packed_options):
mask_id = (packed_options >> 1) & 3
usecase = (packed_options >> 5) & 3
is_personalized = packed_options & 1
kek_source = bytes([a^b for (a, b) in zip(kek_seeds[usecase], kek_masks[mask_id])])
key = None
if is_personalized:
key = device_keys[mkey_rev]
else:
key = master_keys[mkey_rev]
temp_key = aes_ecb_decrypt(kek_source, key)
kek = aes_ecb_decrypt(wrapped_kek, temp_key)
return kek
def gf128_mul(x, y):
x1, x2 = struct.unpack("<QQ", x[0x10::-1])
y1, y2 = struct.unpack("<QQ", y[0x10::-1])
x = (x2 << 64) | x1
y = (y2 << 64) | y1
# https://github.com/bozhu/AES-GCM-Python/blob/master/aes_gcm.py#L32
res = 0
for i in range(127, -1, -1):
res ^= x * ((y >> i) & 1)
x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
return struct.pack("<QQ", res & ((1 << 64) - 1), (res >> 64) & ((1 << 64) - 1))[::-1]
def ghash(key, data, j_block, encrypt):
x = bytearray(0x10)
h = aes_ecb_encrypt(bytes(x), key)
data = bytearray(data) # make it mutable
for i in range(0, len(data) - 0xf, 0x10):
for j in range(0, 0x10):
x[j] ^= data[i + j]
x = bytearray(gf128_mul(x, h))
if (len(data) - (len(data) - 0xf) // 0x10) & 0xf:
x = bytearray(gf128_mul(x, h))
xor_size = struct.pack(">Q", len(data) << 3)
for j in range(0, 8):
x[j + (0 if encrypt else 8)] ^= xor_size[j]
x = bytearray(gf128_mul(x, h))
if encrypt:
h = aes_ecb_encrypt(j_block, key)
for j in range(0, 0x10):
x[j] ^= h[j]
return bytes(x)
def gcm_decrypt_key(src, kek, wrapped_key, usecase, is_personalized):
temp_key = aes_ecb_decrypt(wrapped_key, kek)
intermediate_buf = aes_ctr_decrypt(src[0x10:], src[:0x10], temp_key)
if not is_personalized:
return intermediate_buf
j_block = ghash(temp_key, src[:0x10], None, False)
calc_mac = ghash(temp_key, intermediate_buf[:-0x10], j_block, True)
different = 0
for b in range(0, 0x10):
different |= src[-0x10 + b] ^ calc_mac[b]
if different != 0:
return None
return intermediate_buf[0:-0x20]
def decrypt_rsa_private_key(kek, is_personalized, user_data, wrapped_key):
return gcm_decrypt_key(user_data, kek, wrapped_key, 1, is_personalized)
src_data = None
with open(sys.argv[1], 'rb') as src_file:
src_data = src_file.read()
# derive_new_device_keys(bytes.fromhex(keygen_key))
kek = generate_aes_kek(generate_aes_kek_seed, 0, 0x20)
dest_data = decrypt_rsa_private_key(kek, True, src_data, decrypt_rsa_private_key_seed)
if dest_data == None:
print("Error")
exit(0)
with open(sys.argv[2], 'wb') as dest_file:
dest_file.write(dest_data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment