mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add more TSEC key generation
This commit is contained in:
parent
c6127972f8
commit
bb13864030
6 changed files with 196 additions and 67 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
tsec_auth_signature_00 = A77B86586AE1B03D4FFBA3ADA8F8DE32
|
||||||
|
tsec_auth_signature_01 = A3FFB0F6BC49A06DF2FC791697D81D32
|
||||||
|
tsec_auth_signature_02 = 0B55CC0820E6307FD087479EAA2E7F98
|
||||||
|
|
||||||
keyblob_mac_key_source = 59C7FB6FBE9BBE87656B15C0537336A5
|
keyblob_mac_key_source = 59C7FB6FBE9BBE87656B15C0537336A5
|
||||||
keyblob_key_source_00 = DF206F594454EFDC7074483B0DED9FD3
|
keyblob_key_source_00 = DF206F594454EFDC7074483B0DED9FD3
|
||||||
keyblob_key_source_01 = 0C25615D684CEB421C2379EA822512AC
|
keyblob_key_source_01 = 0C25615D684CEB421C2379EA822512AC
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using static LibHac.Common.Keys.KeySet;
|
||||||
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys;
|
namespace LibHac.Common.Keys;
|
||||||
|
@ -103,40 +104,42 @@ internal static partial class DefaultKeySet
|
||||||
keys.Add(new KeyInfo(11, Type.DeviceRoot, "tsec_key", (set, _) => set.TsecKey));
|
keys.Add(new KeyInfo(11, Type.DeviceRoot, "tsec_key", (set, _) => set.TsecKey));
|
||||||
keys.Add(new KeyInfo(12, Type.DeviceDrvd, "device_key", (set, _) => set.DeviceKey));
|
keys.Add(new KeyInfo(12, Type.DeviceDrvd, "device_key", (set, _) => set.DeviceKey));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(20, Type.CommonRoot, "tsec_root_kek", (set, _) => set.TsecRootKek));
|
keys.Add(new KeyInfo(15, Type.CommonRootSame, "tsec_secret", 0, TsecSecretCount, (set, i) => set.TsecSecrets[i]));
|
||||||
keys.Add(new KeyInfo(21, Type.CommonRoot, "package1_mac_kek", (set, _) => set.Package1MacKek));
|
|
||||||
keys.Add(new KeyInfo(22, Type.CommonRoot, "package1_kek", (set, _) => set.Package1Kek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(30, Type.CommonRoot, "tsec_auth_signature", 0, 0x20, (set, i) => set.TsecAuthSignatures[i]));
|
keys.Add(new KeyInfo(20, Type.CommonRoot, "tsec_root_kek", 0, TsecKeyRevisionCount, (set, i) => set.TsecRootKeks[i]));
|
||||||
|
keys.Add(new KeyInfo(22, Type.CommonRoot, "package1_mac_kek", 0, TsecKeyRevisionCount, (set, i) => set.Package1MacKeks[i]));
|
||||||
|
keys.Add(new KeyInfo(24, Type.CommonRoot, "package1_kek", 0, TsecKeyRevisionCount, (set, i) => set.Package1Keks[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(40, Type.CommonRoot, "tsec_root_key", 0, 0x20, (set, i) => set.TsecRootKeys[i]));
|
keys.Add(new KeyInfo(30, Type.CommonSeed, "tsec_auth_signature", 0, TsecKeyRevisionCount, (set, i) => set.TsecAuthSignatures[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(40, Type.CommonRoot, "tsec_root_key", 0, TsecKeyRevisionCount, (set, i) => set.TsecRootKeys[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(50, Type.CommonSeed, "keyblob_mac_key_source", (set, _) => set.KeyBlobMacKeySource));
|
keys.Add(new KeyInfo(50, Type.CommonSeed, "keyblob_mac_key_source", (set, _) => set.KeyBlobMacKeySource));
|
||||||
keys.Add(new KeyInfo(51, Type.CommonSeed, "keyblob_key_source", 0, 6, (set, i) => set.KeyBlobKeySources[i]));
|
keys.Add(new KeyInfo(51, Type.CommonSeed, "keyblob_key_source", 0, UsedKeyBlobCount, (set, i) => set.KeyBlobKeySources[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(55, Type.DeviceDrvd, "keyblob_key", 0, 6, (set, i) => set.KeyBlobKeys[i]));
|
keys.Add(new KeyInfo(55, Type.DeviceDrvd, "keyblob_key", 0, UsedKeyBlobCount, (set, i) => set.KeyBlobKeys[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(60, Type.DeviceDrvd, "keyblob_mac_key", 0, 6, (set, i) => set.KeyBlobMacKeys[i]));
|
keys.Add(new KeyInfo(60, Type.DeviceDrvd, "keyblob_mac_key", 0, UsedKeyBlobCount, (set, i) => set.KeyBlobMacKeys[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(70, Type.DeviceRoot, "encrypted_keyblob", 0, 6, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
keys.Add(new KeyInfo(70, Type.DeviceRoot, "encrypted_keyblob", 0, UsedKeyBlobCount, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(80, Type.CommonRoot, "keyblob", 0, 6, (set, i) => set.KeyBlobs[i].Bytes));
|
keys.Add(new KeyInfo(80, Type.CommonRoot, "keyblob", 0, UsedKeyBlobCount, (set, i) => set.KeyBlobs[i].Bytes));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(90, Type.CommonSeed, "master_kek_source", 6, 0x20, (set, i) => set.MasterKekSources[i]));
|
keys.Add(new KeyInfo(90, Type.CommonSeed, "master_kek_source", UsedKeyBlobCount, KeyRevisionCount, (set, i) => set.MasterKekSources[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(100, Type.CommonRoot, "mariko_bek", (set, _) => set.MarikoBek));
|
keys.Add(new KeyInfo(100, Type.CommonRoot, "mariko_bek", (set, _) => set.MarikoBek));
|
||||||
keys.Add(new KeyInfo(101, Type.CommonRoot, "mariko_kek", (set, _) => set.MarikoKek));
|
keys.Add(new KeyInfo(101, Type.CommonRoot, "mariko_kek", (set, _) => set.MarikoKek));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(110, Type.CommonRoot, "mariko_aes_class_key", 0, 0xC, (set, i) => set.MarikoAesClassKeys[i]));
|
keys.Add(new KeyInfo(110, Type.CommonRoot, "mariko_aes_class_key", 0, MarikoAesClassKeyCount, (set, i) => set.MarikoAesClassKeys[i]));
|
||||||
keys.Add(new KeyInfo(120, Type.CommonSeedDiff, "mariko_master_kek_source", 0, 0x20, (set, i) => set.MarikoMasterKekSources[i]));
|
keys.Add(new KeyInfo(120, Type.CommonSeedDiff, "mariko_master_kek_source", 0, KeyRevisionCount, (set, i) => set.MarikoMasterKekSources[i]));
|
||||||
keys.Add(new KeyInfo(130, Type.CommonDrvd, "master_kek", 0, 0x20, (set, i) => set.MasterKeks[i]));
|
keys.Add(new KeyInfo(130, Type.CommonDrvd, "master_kek", 0, KeyRevisionCount, (set, i) => set.MasterKeks[i]));
|
||||||
keys.Add(new KeyInfo(140, Type.CommonSeed, "master_key_source", (set, _) => set.MasterKeySource));
|
keys.Add(new KeyInfo(140, Type.CommonSeed, "master_key_source", (set, _) => set.MasterKeySource));
|
||||||
keys.Add(new KeyInfo(150, Type.CommonDrvd, "master_key", 0, 0x20, (set, i) => set.MasterKeys[i]));
|
keys.Add(new KeyInfo(150, Type.CommonDrvd, "master_key", 0, KeyRevisionCount, (set, i) => set.MasterKeys[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(160, Type.CommonDrvd, "package1_key", 0, 0x20, (set, i) => set.Package1Keys[i]));
|
keys.Add(new KeyInfo(160, Type.CommonDrvd, "package1_key", 0, KeyRevisionCount, (set, i) => set.Package1Keys[i]));
|
||||||
keys.Add(new KeyInfo(170, Type.CommonDrvd, "package1_mac_key", 6, 0x20, (set, i) => set.Package1MacKeys[i]));
|
keys.Add(new KeyInfo(170, Type.CommonDrvd, "package1_mac_key", UsedKeyBlobCount, KeyRevisionCount, (set, i) => set.Package1MacKeys[i]));
|
||||||
keys.Add(new KeyInfo(180, Type.CommonSeed, "package2_key_source", (set, _) => set.Package2KeySource));
|
keys.Add(new KeyInfo(180, Type.CommonSeed, "package2_key_source", (set, _) => set.Package2KeySource));
|
||||||
keys.Add(new KeyInfo(190, Type.CommonDrvd, "package2_key", 0, 0x20, (set, i) => set.Package2Keys[i]));
|
keys.Add(new KeyInfo(190, Type.CommonDrvd, "package2_key", 0, KeyRevisionCount, (set, i) => set.Package2Keys[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(200, Type.CommonSeed, "bis_kek_source", (set, _) => set.BisKekSource));
|
keys.Add(new KeyInfo(200, Type.CommonSeed, "bis_kek_source", (set, _) => set.BisKekSource));
|
||||||
keys.Add(new KeyInfo(201, Type.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
keys.Add(new KeyInfo(201, Type.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
||||||
|
@ -149,7 +152,7 @@ internal static partial class DefaultKeySet
|
||||||
keys.Add(new KeyInfo(213, Type.CommonSeed, "aes_key_generation_source", (set, _) => set.AesKeyGenerationSource));
|
keys.Add(new KeyInfo(213, Type.CommonSeed, "aes_key_generation_source", (set, _) => set.AesKeyGenerationSource));
|
||||||
keys.Add(new KeyInfo(214, Type.CommonSeed, "titlekek_source", (set, _) => set.TitleKekSource));
|
keys.Add(new KeyInfo(214, Type.CommonSeed, "titlekek_source", (set, _) => set.TitleKekSource));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(220, Type.CommonDrvd, "titlekek", 0, 0x20, (set, i) => set.TitleKeks[i]));
|
keys.Add(new KeyInfo(220, Type.CommonDrvd, "titlekek", 0, KeyRevisionCount, (set, i) => set.TitleKeks[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(230, Type.CommonSeed, "header_kek_source", (set, _) => set.HeaderKekSource));
|
keys.Add(new KeyInfo(230, Type.CommonSeed, "header_kek_source", (set, _) => set.HeaderKekSource));
|
||||||
keys.Add(new KeyInfo(231, Type.CommonSeed, "header_key_source", (set, _) => set.HeaderKeySource));
|
keys.Add(new KeyInfo(231, Type.CommonSeed, "header_key_source", (set, _) => set.HeaderKeySource));
|
||||||
|
@ -175,15 +178,15 @@ internal static partial class DefaultKeySet
|
||||||
keys.Add(new KeyInfo(263, Type.CommonSeed, "sd_card_nca_key_source", (set, _) => set.SdCardKeySources[1]));
|
keys.Add(new KeyInfo(263, Type.CommonSeed, "sd_card_nca_key_source", (set, _) => set.SdCardKeySources[1]));
|
||||||
keys.Add(new KeyInfo(264, Type.CommonSeed, "sd_card_custom_storage_key_source", (set, _) => set.SdCardKeySources[2]));
|
keys.Add(new KeyInfo(264, Type.CommonSeed, "sd_card_custom_storage_key_source", (set, _) => set.SdCardKeySources[2]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(270, Type.CommonSeedDiff, "xci_header_key", (set, _) => set.XciHeaderKey));
|
keys.Add(new KeyInfo(270, Type.CommonRoot, "xci_header_key", (set, _) => set.XciHeaderKey));
|
||||||
keys.Add(new KeyInfo(271, Type.CommonRoot, "xci_t1_titlekey_kek", 0, 0x10, (set, i) => set.GcTitleKeyKeks[i]));
|
keys.Add(new KeyInfo(271, Type.CommonRoot, "xci_t1_titlekey_kek", 0, 0x10, (set, i) => set.GcTitleKeyKeks[i]));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(280, Type.CommonRoot, "eticket_rsa_kek", (set, _) => set.ETicketRsaKek));
|
keys.Add(new KeyInfo(280, Type.CommonRoot, "eticket_rsa_kek", (set, _) => set.ETicketRsaKek));
|
||||||
keys.Add(new KeyInfo(281, Type.CommonRoot, "ssl_rsa_kek", (set, _) => set.SslRsaKek));
|
keys.Add(new KeyInfo(281, Type.CommonRoot, "ssl_rsa_kek", (set, _) => set.SslRsaKek));
|
||||||
|
|
||||||
keys.Add(new KeyInfo(290, Type.CommonDrvd, "key_area_key_application", 0, 0x20, (set, i) => set.KeyAreaKeys[i][0]));
|
keys.Add(new KeyInfo(290, Type.CommonDrvd, "key_area_key_application", 0, KeyRevisionCount, (set, i) => set.KeyAreaKeys[i][0]));
|
||||||
keys.Add(new KeyInfo(300, Type.CommonDrvd, "key_area_key_ocean", 0, 0x20, (set, i) => set.KeyAreaKeys[i][1]));
|
keys.Add(new KeyInfo(300, Type.CommonDrvd, "key_area_key_ocean", 0, KeyRevisionCount, (set, i) => set.KeyAreaKeys[i][1]));
|
||||||
keys.Add(new KeyInfo(310, Type.CommonDrvd, "key_area_key_system", 0, 0x20, (set, i) => set.KeyAreaKeys[i][2]));
|
keys.Add(new KeyInfo(310, Type.CommonDrvd, "key_area_key_system", 0, KeyRevisionCount, (set, i) => set.KeyAreaKeys[i][2]));
|
||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,26 +12,34 @@ namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
public static class ExternalKeyWriter
|
public static class ExternalKeyWriter
|
||||||
{
|
{
|
||||||
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, Type filter, bool isDev)
|
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, Type filter, bool onlyPrintDifferentDevKeys)
|
||||||
{
|
{
|
||||||
if (keys.Count == 0) return;
|
if (keys.Count == 0) return;
|
||||||
|
|
||||||
string devSuffix = isDev ? "_dev" : string.Empty;
|
string devSuffix = onlyPrintDifferentDevKeys ? "_dev" : string.Empty;
|
||||||
int maxNameLength = keys.Max(x => x.NameLength);
|
int maxNameLength = keys.Max(x => x.NameLength);
|
||||||
int currentGroup = 0;
|
int currentGroup = 0;
|
||||||
|
|
||||||
// Todo: Better filtering
|
|
||||||
bool FilterMatches(KeyInfo keyInfo)
|
bool FilterMatches(KeyInfo keyInfo)
|
||||||
{
|
{
|
||||||
Type filter1 = filter & (Type.Common | Type.Device);
|
// If we're only printing dev-only keys, skip keys that are the same in both prod and dev environments.
|
||||||
Type filter2 = filter & (Type.Root | Type.Seed | Type.Derived);
|
if (onlyPrintDifferentDevKeys && !keyInfo.Type.HasFlag(Type.DifferentDev))
|
||||||
Type filter3 = filter & Type.DifferentDev;
|
return false;
|
||||||
|
|
||||||
// Skip sub-filters that have no flags set
|
// A KeyType contains two sub-types that specify how a key is used in the cryptosystem, and whether a key
|
||||||
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
|
// is shared between all consoles or is console-unique. Each of these types are filtered separately.
|
||||||
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
|
// A key must match both of these sub-types to pass through the filter.
|
||||||
filter3 == (filter3 & keyInfo.Type) ||
|
// A value of 0 for a sub-filter means that any value of the filtered sub-type is allowed.
|
||||||
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
|
const Type distributionTypeMask = Type.Common | Type.Device;
|
||||||
|
const Type derivationTypeMask = Type.Root | Type.Seed | Type.Derived;
|
||||||
|
|
||||||
|
Type distributionTypeFilter = filter & distributionTypeMask;
|
||||||
|
Type derivationTypeFilter = filter & derivationTypeMask;
|
||||||
|
|
||||||
|
bool matchesDistributionTypeFilter = distributionTypeFilter == 0 || (distributionTypeFilter & keyInfo.Type) != 0;
|
||||||
|
bool matchesDerivationTypeFilter = derivationTypeFilter == 0 || (derivationTypeFilter & keyInfo.Type) != 0;
|
||||||
|
|
||||||
|
return matchesDistributionTypeFilter && matchesDerivationTypeFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFirstPrint = true;
|
bool isFirstPrint = true;
|
||||||
|
@ -118,8 +126,7 @@ public static class ExternalKeyWriter
|
||||||
public static string PrintCommonKeys(KeySet keySet)
|
public static string PrintCommonKeys(KeySet keySet)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common, false);
|
||||||
false);
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +153,7 @@ public static class ExternalKeyWriter
|
||||||
{
|
{
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, true);
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,12 +166,11 @@ public static class ExternalKeyWriter
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common, false);
|
||||||
false);
|
|
||||||
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common, true);
|
||||||
|
|
||||||
keySet.SetMode(originalMode);
|
keySet.SetMode(originalMode);
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys;
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
internal static class KeyDerivation
|
internal static class KeyDerivation
|
||||||
{
|
{
|
||||||
|
private const int HoviSeedSize = 0x10;
|
||||||
|
|
||||||
public static void DeriveAllKeys(KeySet keySet, IProgressReport logger = null)
|
public static void DeriveAllKeys(KeySet keySet, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
DeriveKeyBlobKeys(keySet);
|
DeriveKeyBlobKeys(keySet);
|
||||||
DecryptKeyBlobs(keySet, logger);
|
DecryptKeyBlobs(keySet, logger);
|
||||||
ReadKeyBlobs(keySet);
|
ReadKeyBlobs(keySet);
|
||||||
|
|
||||||
Derive620Keys(keySet);
|
DeriveTsecKeys(keySet);
|
||||||
Derive620MasterKeks(keySet);
|
Derive620MasterKeks(keySet);
|
||||||
DeriveMarikoMasterKeks(keySet);
|
DeriveMarikoMasterKeks(keySet);
|
||||||
DeriveMasterKeys(keySet);
|
DeriveMasterKeys(keySet);
|
||||||
|
@ -46,6 +49,9 @@ internal static class KeyDerivation
|
||||||
|
|
||||||
private static void DecryptKeyBlobs(KeySet s, IProgressReport logger = null)
|
private static void DecryptKeyBlobs(KeySet s, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
|
if (s.CurrentMode == KeySet.Mode.Dev)
|
||||||
|
return;
|
||||||
|
|
||||||
var cmac = new AesCmac();
|
var cmac = new AesCmac();
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
|
@ -78,33 +84,115 @@ internal static class KeyDerivation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Derive620Keys(KeySet s)
|
private enum HoviSeedType
|
||||||
{
|
{
|
||||||
bool haveTsecRootKek = !s.TsecRootKek.IsZeros();
|
Encryption,
|
||||||
bool havePackage1MacKek = !s.Package1MacKek.IsZeros();
|
Signature,
|
||||||
bool havePackage1Kek = !s.Package1Kek.IsZeros();
|
Kek,
|
||||||
|
|
||||||
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
// TSEC directly uses the value generated by this (no key derivation) to encrypt pk11's AES IV that's found
|
||||||
|
// at 0x6FF0 in package1. When decrypting pk11, TSEC will overwrite the unencrypted IV in package1 with the
|
||||||
|
// encrypted IV.
|
||||||
|
IvEncryption
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveTsecKeys(KeySet s)
|
||||||
|
{
|
||||||
|
AesKey secret26 = s.TsecSecrets[0x26];
|
||||||
|
bool haveSecret26 = !secret26.IsZeros();
|
||||||
|
bool isProd = s.CurrentMode == KeySet.Mode.Prod;
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.TsecKeyRevisionCount; i++)
|
||||||
{
|
{
|
||||||
if (s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount].IsZeros())
|
if (s.TsecAuthSignatures[i].IsZeros()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (haveTsecRootKek)
|
if (haveSecret26)
|
||||||
{
|
{
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
|
DeriveTsecKeyFromSecret(i, HoviSeedType.Encryption, isProd, secret26, s.Package1Keks[i]);
|
||||||
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
|
DeriveTsecKeyFromSecret(i, HoviSeedType.Signature, isProd, secret26, s.Package1MacKeks[i]);
|
||||||
|
DeriveTsecKeyFromSecret(i, HoviSeedType.Kek, isProd, secret26, s.TsecRootKeks[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (havePackage1MacKek)
|
if (!s.Package1Keks[i].IsZeros())
|
||||||
{
|
{
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i],
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i], s.Package1Keys[KeySet.UsedKeyBlobCount + i], s.Package1Keks[i]);
|
||||||
s.Package1MacKek);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (havePackage1Kek)
|
if (!s.Package1MacKeks[i].IsZeros())
|
||||||
{
|
{
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1Keys[i], s.Package1Kek);
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i], s.Package1MacKeys[KeySet.UsedKeyBlobCount + i], s.Package1MacKeks[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!s.TsecRootKeks[i].IsZeros())
|
||||||
|
{
|
||||||
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i], s.TsecRootKeys[i], s.TsecRootKeks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DeriveTsecKeyFromSecret(int keyVersion, HoviSeedType type, bool isProd, ReadOnlySpan<byte> secret, Span<byte> output)
|
||||||
|
{
|
||||||
|
Span<byte> hoviSeed = stackalloc byte[HoviSeedSize];
|
||||||
|
GenerateHoviSeed(hoviSeed, type, isProd);
|
||||||
|
|
||||||
|
// The third version of the post-6.2.0 TSEC firmwares changed how these keys are derived. Instead of
|
||||||
|
// encrypting the seed, they now decrypt the seed.
|
||||||
|
if (keyVersion <= 1)
|
||||||
|
{
|
||||||
|
Aes.EncryptEcb128(hoviSeed, output, secret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(hoviSeed, output, secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateHoviSeed(Span<byte> outSeed, HoviSeedType type, bool isProd)
|
||||||
|
{
|
||||||
|
const uint part1Hov = 0x49564F48; // HOVI
|
||||||
|
const uint part2Sig = 0x4749535F; // _SIG
|
||||||
|
const uint part2Kek = 0x4B454B5F; // _KEK
|
||||||
|
const uint part2Enc = 0x434E455F; // _ENC
|
||||||
|
const uint part3Key = 0x59454B5F; // _KEY
|
||||||
|
const uint part4Prd = 0x4452505F; // _PRD
|
||||||
|
const uint part4Dev = 0x5645445F; // _DEV
|
||||||
|
const uint part4Iv1 = 0x3156495F; // _IV1
|
||||||
|
|
||||||
|
Assert.GreaterEqual(outSeed.Length, HoviSeedSize);
|
||||||
|
|
||||||
|
Span<uint> seed = MemoryMarshal.Cast<byte, uint>(outSeed);
|
||||||
|
|
||||||
|
seed[0] = part1Hov;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case HoviSeedType.Encryption:
|
||||||
|
seed[1] = part2Enc;
|
||||||
|
break;
|
||||||
|
case HoviSeedType.Signature:
|
||||||
|
seed[1] = part2Sig;
|
||||||
|
break;
|
||||||
|
case HoviSeedType.Kek:
|
||||||
|
seed[1] = part2Kek;
|
||||||
|
break;
|
||||||
|
case HoviSeedType.IvEncryption:
|
||||||
|
seed[1] = part2Enc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
seed[2] = part3Key;
|
||||||
|
|
||||||
|
if (type == HoviSeedType.IvEncryption)
|
||||||
|
{
|
||||||
|
seed[3] = part4Iv1;
|
||||||
|
}
|
||||||
|
else if (isProd)
|
||||||
|
{
|
||||||
|
seed[3] = part4Prd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
seed[3] = part4Dev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,17 @@ public readonly struct KeyInfo
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum KeyType : byte
|
public enum KeyType : byte
|
||||||
{
|
{
|
||||||
|
// These two flags specify whether a key is common to all consoles or is console-unique.
|
||||||
|
// Only one of these flags should be set at a time.
|
||||||
Common = 1 << 0,
|
Common = 1 << 0,
|
||||||
Device = 1 << 1,
|
Device = 1 << 1,
|
||||||
|
|
||||||
|
// These three flags specify how a key is obtained. Only one of these flags should be set at a time.
|
||||||
|
// Root keys are generally used to transform the key seeds into actual keys.
|
||||||
|
// These root keys are usually not supposed to be exposed outside of TrustZone or the TSEC firmware.
|
||||||
|
// Other root keys might be stored directly in system software and used without transforming the key first.
|
||||||
|
// Seeds are generally stored as plaintext in system software and are used as seeds to generate the actual keys.
|
||||||
|
// Derived keys are the keys that are generated from the key seeds.
|
||||||
Root = 1 << 2,
|
Root = 1 << 2,
|
||||||
Seed = 1 << 3,
|
Seed = 1 << 3,
|
||||||
Derived = 1 << 4,
|
Derived = 1 << 4,
|
||||||
|
@ -26,10 +35,11 @@ public readonly struct KeyInfo
|
||||||
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
||||||
DifferentDev = 1 << 5,
|
DifferentDev = 1 << 5,
|
||||||
|
|
||||||
CommonRoot = Common | Root,
|
CommonRoot = Common | Root | DifferentDev,
|
||||||
|
CommonRootSame = Common | Root,
|
||||||
CommonSeed = Common | Seed,
|
CommonSeed = Common | Seed,
|
||||||
CommonSeedDiff = Common | Seed | DifferentDev,
|
CommonSeedDiff = Common | Seed | DifferentDev,
|
||||||
CommonDrvd = Common | Derived,
|
CommonDrvd = Common | Derived | DifferentDev,
|
||||||
DeviceRoot = Device | Root,
|
DeviceRoot = Device | Root,
|
||||||
DeviceDrvd = Device | Derived
|
DeviceDrvd = Device | Derived
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,15 @@ public class KeySet
|
||||||
internal const int UsedKeyBlobCount = 6;
|
internal const int UsedKeyBlobCount = 6;
|
||||||
internal const int SdCardKeyIdCount = 3;
|
internal const int SdCardKeyIdCount = 3;
|
||||||
internal const int KeyRevisionCount = 0x20;
|
internal const int KeyRevisionCount = 0x20;
|
||||||
|
internal const int TsecSecretCount = 0x40;
|
||||||
|
internal const int MarikoAesClassKeyCount = 0xC;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of slots reserved for current and future TSEC FW revisions.
|
||||||
|
/// 8 was semi-arbitrarily chosen because there are only 3 FW revisions used for >= 6.2.0 crypto as of Jan 2023,
|
||||||
|
/// and it's unlikely that many more will be issued.
|
||||||
|
/// </summary>
|
||||||
|
internal const int TsecKeyRevisionCount = 8;
|
||||||
|
|
||||||
private AllKeys _keys;
|
private AllKeys _keys;
|
||||||
private Mode _mode = Mode.Prod;
|
private Mode _mode = Mode.Prod;
|
||||||
|
@ -30,6 +39,7 @@ public class KeySet
|
||||||
public ref AllKeys KeyStruct => ref _keys;
|
public ref AllKeys KeyStruct => ref _keys;
|
||||||
public Mode CurrentMode => _mode;
|
public Mode CurrentMode => _mode;
|
||||||
|
|
||||||
|
private ref TsecSecrets Secrets => ref _keys.TsecSecrets;
|
||||||
private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys.RootKeysDev : ref _keys.RootKeysProd;
|
private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys.RootKeysDev : ref _keys.RootKeysProd;
|
||||||
private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys.StoredKeysDev : ref _keys.StoredKeysProd;
|
private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys.StoredKeysDev : ref _keys.StoredKeysProd;
|
||||||
private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys.DerivedKeysDev : ref _keys.DerivedKeysProd;
|
private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys.DerivedKeysDev : ref _keys.DerivedKeysProd;
|
||||||
|
@ -49,10 +59,12 @@ public class KeySet
|
||||||
public Span<KeyBlob> KeyBlobs => RootKeys.KeyBlobs.Items;
|
public Span<KeyBlob> KeyBlobs => RootKeys.KeyBlobs.Items;
|
||||||
public Span<AesKey> KeyBlobKeySources => _keys.KeySeeds.KeyBlobKeySources.Items;
|
public Span<AesKey> KeyBlobKeySources => _keys.KeySeeds.KeyBlobKeySources.Items;
|
||||||
public ref AesKey KeyBlobMacKeySource => ref _keys.KeySeeds.KeyBlobMacKeySource;
|
public ref AesKey KeyBlobMacKeySource => ref _keys.KeySeeds.KeyBlobMacKeySource;
|
||||||
public ref AesKey TsecRootKek => ref RootKeys.TsecRootKek;
|
|
||||||
public ref AesKey Package1MacKek => ref RootKeys.Package1MacKek;
|
public Span<AesKey> TsecSecrets => Secrets.Secrets.Items;
|
||||||
public ref AesKey Package1Kek => ref RootKeys.Package1Kek;
|
public Span<AesKey> TsecRootKeks => RootKeys.TsecRootKeks.Items;
|
||||||
public Span<AesKey> TsecAuthSignatures => RootKeys.TsecAuthSignatures.Items;
|
public Span<AesKey> Package1MacKeks => RootKeys.Package1MacKeks.Items;
|
||||||
|
public Span<AesKey> Package1Keks => RootKeys.Package1Keks.Items;
|
||||||
|
public Span<AesKey> TsecAuthSignatures => _keys.KeySeeds.TsecAuthSignatures.Items;
|
||||||
public Span<AesKey> TsecRootKeys => RootKeys.TsecRootKeys.Items;
|
public Span<AesKey> TsecRootKeys => RootKeys.TsecRootKeys.Items;
|
||||||
public Span<AesKey> MasterKekSources => _keys.KeySeeds.MasterKekSources.Items;
|
public Span<AesKey> MasterKekSources => _keys.KeySeeds.MasterKekSources.Items;
|
||||||
public Span<AesKey> GcTitleKeyKeks => RootKeys.GcTitleKeyKeks.Items;
|
public Span<AesKey> GcTitleKeyKeks => RootKeys.GcTitleKeyKeks.Items;
|
||||||
|
@ -263,6 +275,7 @@ public class KeySet
|
||||||
|
|
||||||
public struct AllKeys
|
public struct AllKeys
|
||||||
{
|
{
|
||||||
|
public TsecSecrets TsecSecrets;
|
||||||
public RootKeys RootKeysDev;
|
public RootKeys RootKeysDev;
|
||||||
public RootKeys RootKeysProd;
|
public RootKeys RootKeysProd;
|
||||||
public KeySeeds KeySeeds;
|
public KeySeeds KeySeeds;
|
||||||
|
@ -278,6 +291,11 @@ public struct AllKeys
|
||||||
public RsaKeys RsaKeys;
|
public RsaKeys RsaKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct TsecSecrets
|
||||||
|
{
|
||||||
|
public Array64<AesKey> Secrets;
|
||||||
|
}
|
||||||
|
|
||||||
public struct RootKeys
|
public struct RootKeys
|
||||||
{
|
{
|
||||||
// Mariko keys. The AES class keys are currently unused.
|
// Mariko keys. The AES class keys are currently unused.
|
||||||
|
@ -291,13 +309,12 @@ public struct RootKeys
|
||||||
public Array32<KeyBlob> KeyBlobs;
|
public Array32<KeyBlob> KeyBlobs;
|
||||||
|
|
||||||
// Used by TSEC in >= 6.2.0 Erista firmware
|
// Used by TSEC in >= 6.2.0 Erista firmware
|
||||||
public Array32<AesKey> TsecAuthSignatures;
|
public Array8<AesKey> TsecRootKeks;
|
||||||
public AesKey TsecRootKek;
|
public Array8<AesKey> Package1MacKeks;
|
||||||
public AesKey Package1MacKek;
|
public Array8<AesKey> Package1Keks;
|
||||||
public AesKey Package1Kek;
|
|
||||||
|
|
||||||
// Derived by TSEC. This is the first public root key for >= 6.2.0 Erista
|
// Derived by TSEC. This is the first public root key for >= 6.2.0 Erista
|
||||||
public Array32<AesKey> TsecRootKeys;
|
public Array8<AesKey> TsecRootKeys;
|
||||||
|
|
||||||
// Used to decrypt the title keys found in an XCI's initial data
|
// Used to decrypt the title keys found in an XCI's initial data
|
||||||
public Array16<AesKey> GcTitleKeyKeks;
|
public Array16<AesKey> GcTitleKeyKeks;
|
||||||
|
@ -305,6 +322,7 @@ public struct RootKeys
|
||||||
|
|
||||||
public struct KeySeeds
|
public struct KeySeeds
|
||||||
{
|
{
|
||||||
|
public Array8<AesKey> TsecAuthSignatures;
|
||||||
public Array32<AesKey> KeyBlobKeySources;
|
public Array32<AesKey> KeyBlobKeySources;
|
||||||
public AesKey KeyBlobMacKeySource;
|
public AesKey KeyBlobMacKeySource;
|
||||||
public Array32<AesKey> MasterKekSources;
|
public Array32<AesKey> MasterKekSources;
|
||||||
|
|
Loading…
Reference in a new issue