Add more TSEC key generation

This commit is contained in:
Alex Barney 2023-01-29 22:26:34 -07:00
parent c6127972f8
commit bb13864030
6 changed files with 196 additions and 67 deletions

View file

@ -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

View file

@ -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;
} }

View file

@ -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();

View file

@ -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
if (s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount].IsZeros()) // encrypted IV.
continue; IvEncryption
if (haveTsecRootKek)
{
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
} }
if (havePackage1MacKek) private static void DeriveTsecKeys(KeySet s)
{ {
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i], AesKey secret26 = s.TsecSecrets[0x26];
s.Package1MacKek); bool haveSecret26 = !secret26.IsZeros();
bool isProd = s.CurrentMode == KeySet.Mode.Prod;
for (int i = 0; i < KeySet.TsecKeyRevisionCount; i++)
{
if (s.TsecAuthSignatures[i].IsZeros()) continue;
if (haveSecret26)
{
DeriveTsecKeyFromSecret(i, HoviSeedType.Encryption, isProd, secret26, s.Package1Keks[i]);
DeriveTsecKeyFromSecret(i, HoviSeedType.Signature, isProd, secret26, s.Package1MacKeks[i]);
DeriveTsecKeyFromSecret(i, HoviSeedType.Kek, isProd, secret26, s.TsecRootKeks[i]);
} }
if (havePackage1Kek) if (!s.Package1Keks[i].IsZeros())
{ {
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1Keys[i], s.Package1Kek); Aes.EncryptEcb128(s.TsecAuthSignatures[i], s.Package1Keys[KeySet.UsedKeyBlobCount + i], s.Package1Keks[i]);
} }
if (!s.Package1MacKeks[i].IsZeros())
{
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;
} }
} }

View file

@ -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
} }

View file

@ -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 &gt;= 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;