More key derivation

Decrypt keyblobs and derive master keys
Derive device_key and bis_key_XX
This commit is contained in:
Alex Barney 2018-09-07 16:27:33 -05:00
parent 643d603732
commit 310664970c
2 changed files with 183 additions and 6 deletions

View file

@ -54,6 +54,28 @@ namespace LibHac
public static void DecryptCbc(byte[] key, byte[] iv, byte[] src, byte[] dest, int length) =>
DecryptCbc(key, iv, src, 0, dest, 0, length);
public static void EncryptCbc(byte[] key, byte[] iv, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
{
using (var aes = Aes.Create())
{
if (aes == null) throw new CryptographicException("Unable to create AES object");
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
var dec = aes.CreateEncryptor();
using (var ms = new MemoryStream(dest, destIndex, length))
using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Write))
{
cs.Write(src, srcIndex, length);
cs.FlushFinalBlock();
}
}
}
public static void EncryptCbc(byte[] key, byte[] iv, byte[] src, byte[] dest, int length) =>
EncryptCbc(key, iv, src, 0, dest, 0, length);
public static void GenerateKek(byte[] dst, byte[] src, byte[] masterKey, byte[] kekSeed, byte[] keySeed)
{
var kek = new byte[Aes128Size];
@ -109,7 +131,7 @@ namespace LibHac
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(keyParams);
var test = new byte[] {12, 34, 56, 78};
var test = new byte[] { 12, 34, 56, 78 };
byte[] testEnc = rsa.Encrypt(test, false);
byte[] testDec = rsa.Decrypt(testEnc, false);
@ -282,5 +304,58 @@ namespace LibHac
return t;
}
// https://stackoverflow.com/questions/29163493/aes-cmac-calculation-c-sharp
public static void CalculateAesCmac(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
{
byte[] l = new byte[16];
EncryptCbc(key, new byte[16], new byte[16], l, 0x10);
byte[] firstSubkey = Rol(l);
if ((l[0] & 0x80) == 0x80)
firstSubkey[15] ^= 0x87;
byte[] secondSubkey = Rol(firstSubkey);
if ((firstSubkey[0] & 0x80) == 0x80)
secondSubkey[15] ^= 0x87;
int paddingBytes = 16 - length % 16;
byte[] srcPadded = new byte[length + paddingBytes];
Array.Copy(src, srcIndex, srcPadded, 0, length);
if (paddingBytes > 0)
{
srcPadded[length] = 0x80;
for (int j = 0; j < firstSubkey.Length; j++)
srcPadded[length - 16 + j] ^= firstSubkey[j];
}
else
{
for (int j = 0; j < secondSubkey.Length; j++)
srcPadded[length - 16 + j] ^= secondSubkey[j];
}
byte[] encResult = new byte[length];
EncryptCbc(key, new byte[16], srcPadded, encResult, length);
Array.Copy(encResult, length - 0x10, dest, destIndex, 0x10);
}
private static byte[] Rol(byte[] b)
{
byte[] r = new byte[b.Length];
byte carry = 0;
for (int i = b.Length - 1; i >= 0; i--)
{
ushort u = (ushort)(b[i] << 1);
r[i] = (byte)((u & 0xff) + carry);
carry = (byte)((u & 0xff00) >> 8);
}
return r;
}
}
}

View file

@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using LibHac.Streams;
namespace LibHac
{
@ -42,6 +43,10 @@ namespace LibHac
public byte[] acid_fixed_key_modulus { get; set; } = new byte[0x100];
public byte[] package2_fixed_key_modulus { get; set; } = new byte[0x100];
public byte[] eticket_rsa_kek { get; set; } = new byte[0x10];
public byte[] retail_specific_aes_key_source { get; set; } = new byte[0x10];
public byte[] per_console_key_source { get; set; } = new byte[0x10];
public byte[] bis_kek_source { get; set; } = new byte[0x10];
public byte[][] bis_key_source { get; set; } = Util.CreateJaggedArray<byte[][]>(3, 0x20);
public byte[] secure_boot_key { get; set; } = new byte[0x10];
public byte[] tsec_key { get; set; } = new byte[0x10];
@ -58,8 +63,96 @@ namespace LibHac
DeriveKeys();
}
internal void DeriveKeys()
internal void DeriveKeys(IProgressReport logger = null)
{
for (int i = 0; i < 0x20; i++)
{
if (secure_boot_key.IsEmpty() || tsec_key.IsEmpty() || keyblob_key_sources[i].IsEmpty())
{
continue;
}
var temp = new byte[0x10];
Crypto.DecryptEcb(tsec_key, keyblob_key_sources[i], temp, 0x10);
Crypto.DecryptEcb(secure_boot_key, temp, keyblob_keys[i], 0x10);
if (keyblob_mac_key_source.IsEmpty()) continue;
Crypto.DecryptEcb(keyblob_keys[i], keyblob_mac_key_source, keyblob_mac_keys[i], 0x10);
}
var cmac = new byte[0x10];
var expectedCmac = new byte[0x10];
var counter = new byte[0x10];
for (int i = 0; i < 0x20; i++)
{
if (keyblob_keys[i].IsEmpty() || keyblob_mac_keys[i].IsEmpty() || encrypted_keyblobs[i].IsEmpty())
{
continue;
}
Array.Copy(encrypted_keyblobs[i], expectedCmac, 0x10);
Crypto.CalculateAesCmac(keyblob_mac_keys[i], encrypted_keyblobs[i], 0x10, cmac, 0, 0xa0);
if (!Util.ArraysEqual(cmac, expectedCmac))
{
logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?");
}
Array.Copy(encrypted_keyblobs[i], 0x10, counter, 0, 0x10);
using (var keyblobDec = new RandomAccessSectorStream(new Aes128CtrStream(new MemoryStream(encrypted_keyblobs[i], 0x20, keyblobs[i].Length), keyblob_keys[i], counter)))
{
keyblobDec.Read(keyblobs[i], 0, keyblobs[i].Length);
}
}
for (int i = 0; i < 0x20; i++)
{
if (keyblobs[i].IsEmpty()) continue;
Array.Copy(keyblobs[i], 0x80, package1_keys[i], 0, 0x10);
}
for (int i = 0; i < 0x20; i++)
{
if (master_key_source.IsEmpty() || keyblobs[i].IsEmpty()) continue;
var masterKek = new byte[0x10];
Array.Copy(keyblobs[i], masterKek, 0x10);
Crypto.DecryptEcb(masterKek, master_key_source, master_keys[i], 0x10);
}
if (!per_console_key_source.IsEmpty() && !keyblob_keys[0].IsEmpty())
{
Crypto.DecryptEcb(keyblob_keys[0], per_console_key_source, device_key, 0x10);
}
if (!device_key.IsEmpty()
&& !bis_key_source[0].IsEmpty()
&& !bis_key_source[1].IsEmpty()
&& !bis_key_source[2].IsEmpty()
&& !bis_kek_source.IsEmpty()
&& !aes_kek_generation_source.IsEmpty()
&& !aes_key_generation_source.IsEmpty()
&& !retail_specific_aes_key_source.IsEmpty())
{
var kek = new byte[0x10];
Crypto.DecryptEcb(device_key, retail_specific_aes_key_source, kek, 0x10);
Crypto.DecryptEcb(kek, bis_key_source[0], bis_keys[0], 0x20);
Crypto.GenerateKek(kek, bis_kek_source, device_key, aes_kek_generation_source, aes_key_generation_source);
Crypto.DecryptEcb(kek, bis_key_source[1], bis_keys[1], 0x20);
Crypto.DecryptEcb(kek, bis_key_source[2], bis_keys[2], 0x20);
Array.Copy(bis_keys[2], bis_keys[3], 0x20);
}
for (int i = 0; i < 0x20; i++)
{
if (master_keys[i].IsEmpty())
@ -93,7 +186,7 @@ namespace LibHac
}
}
if (!header_kek_source.IsEmpty() && !header_key_source.IsEmpty())
if (!header_kek_source.IsEmpty() && !header_key_source.IsEmpty() && !master_keys[0].IsEmpty())
{
var headerKek = new byte[0x10];
Crypto.GenerateKek(headerKek, header_kek_source, master_keys[0], aes_kek_generation_source, aes_key_generation_source);
@ -142,7 +235,7 @@ namespace LibHac
if (filename != null) ReadMainKeys(keyset, filename, AllKeyDict, logger);
if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, AllKeyDict, logger);
if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, logger);
keyset.DeriveKeys();
keyset.DeriveKeys(logger);
return keyset;
}
@ -263,7 +356,10 @@ namespace LibHac
new KeyValue("sd_card_save_key_source", 0x20, set => set.sd_card_key_sources[0]),
new KeyValue("master_key_source", 0x10, set => set.master_key_source),
new KeyValue("keyblob_mac_key_source", 0x10, set => set.keyblob_mac_key_source),
new KeyValue("eticket_rsa_kek", 0x10, set => set.eticket_rsa_kek )
new KeyValue("eticket_rsa_kek", 0x10, set => set.eticket_rsa_kek),
new KeyValue("retail_specific_aes_key_source", 0x10, set => set.retail_specific_aes_key_source),
new KeyValue("per_console_key_source", 0x10, set => set.per_console_key_source),
new KeyValue("bis_kek_source", 0x10, set => set.bis_kek_source)
};
for (int slot = 0; slot < 0x20; slot++)
@ -280,6 +376,12 @@ namespace LibHac
keys.Add(new KeyValue($"key_area_key_system_{i:x2}", 0x10, set => set.key_area_keys[i][2]));
}
for (int slot = 0; slot < 3; slot++)
{
int i = slot;
keys.Add(new KeyValue($"bis_key_source_{i:x2}", 0x20, set => set.bis_key_source[i]));
}
return keys;
}
@ -290,7 +392,7 @@ namespace LibHac
new KeyValue("secure_boot_key", 0x10, set => set.secure_boot_key),
new KeyValue("tsec_key", 0x10, set => set.tsec_key),
new KeyValue("device_key", 0x10, set => set.device_key),
new KeyValue("sd_seed", 0x10, set => set.sd_seed),
new KeyValue("sd_seed", 0x10, set => set.sd_seed)
};
for (int slot = 0; slot < 0x20; slot++)