From 310664970c27adac599631bd7498321909ad17db Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 7 Sep 2018 16:27:33 -0500 Subject: [PATCH] More key derivation Decrypt keyblobs and derive master keys Derive device_key and bis_key_XX --- LibHac/Crypto.cs | 77 +++++++++++++++++++++++++++++++- LibHac/Keyset.cs | 112 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 183 insertions(+), 6 deletions(-) diff --git a/LibHac/Crypto.cs b/LibHac/Crypto.cs index 2c9b0442..8a37a1d2 100644 --- a/LibHac/Crypto.cs +++ b/LibHac/Crypto.cs @@ -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; + } } } diff --git a/LibHac/Keyset.cs b/LibHac/Keyset.cs index 89a202a0..7081a406 100644 --- a/LibHac/Keyset.cs +++ b/LibHac/Keyset.cs @@ -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(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++)