From 770406e9c25d338703ab9c1f2ac151349d37d31e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 5 Oct 2020 12:25:39 -0700 Subject: [PATCH] Remove the old Keyset class --- src/LibHac/Boot/Package1.cs | 21 +- src/LibHac/Boot/Package2StorageReader.cs | 21 +- src/LibHac/Common/Keys/KeySet.cs | 4 + .../Creators/EncryptedFileSystemCreator.cs | 12 +- .../Creators/SaveDataFileSystemCreator.cs | 9 +- .../FsSrv/Creators/StorageOnNcaCreator.cs | 9 +- src/LibHac/FsSrv/DefaultFsServerObjects.cs | 13 +- src/LibHac/FsSrv/EmulatedGameCard.cs | 9 +- src/LibHac/FsSystem/NcaUtils/Nca.cs | 37 +- src/LibHac/FsSystem/NcaUtils/NcaHeader.cs | 11 +- src/LibHac/FsSystem/Save/Header.cs | 9 +- .../FsSystem/Save/SaveDataFileSystem.cs | 19 +- src/LibHac/KeyType.cs | 10 + src/LibHac/Keyset.cs | 885 ------------------ src/LibHac/Npdm/Acid.cs | 8 +- src/LibHac/Npdm/NpdmBinary.cs | 5 +- src/LibHac/Package1.cs | 5 +- src/LibHac/Package2.cs | 20 +- src/LibHac/SwitchFs.cs | 27 +- src/LibHac/Ticket.cs | 5 +- src/LibHac/Xci.cs | 5 +- src/LibHac/XciHeader.cs | 7 +- src/hactoolnet/Options.cs | 5 +- src/hactoolnet/ProcessDelta.cs | 2 +- src/hactoolnet/ProcessNax0.cs | 15 +- src/hactoolnet/ProcessNca.cs | 13 +- src/hactoolnet/ProcessPackage.cs | 4 +- src/hactoolnet/ProcessSave.cs | 19 +- src/hactoolnet/ProcessSwitchFs.cs | 6 +- src/hactoolnet/ProcessXci.cs | 4 +- src/hactoolnet/Program.cs | 35 +- .../FileSystemServerFactory.cs | 5 +- tests/LibHac.Tests/HorizonFactory.cs | 5 +- 33 files changed, 224 insertions(+), 1040 deletions(-) create mode 100644 src/LibHac/KeyType.cs delete mode 100644 src/LibHac/Keyset.cs diff --git a/src/LibHac/Boot/Package1.cs b/src/LibHac/Boot/Package1.cs index f8e04200..7b39fd7b 100644 --- a/src/LibHac/Boot/Package1.cs +++ b/src/LibHac/Boot/Package1.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Common.Keys; using LibHac.Diag; namespace LibHac.Boot @@ -80,7 +81,7 @@ namespace LibHac.Boot private const int MarikoWarmBootPlainTextSectionSize = 0x330; private IStorage BaseStorage { get; set; } - private Keyset Keyset { get; set; } + private KeySet KeySet { get; set; } public bool IsModern { get; private set; } public bool IsMariko { get; private set; } @@ -107,9 +108,9 @@ namespace LibHac.Boot public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header; public ref readonly Buffer16 Pk11Mac => ref _pk11Mac; - public Result Initialize(Keyset keyset, IStorage storage) + public Result Initialize(KeySet keySet, IStorage storage) { - Keyset = keyset; + KeySet = keySet; BaseStorage = storage; // Read what might be a mariko header and check if it actually is a mariko header @@ -199,13 +200,13 @@ namespace LibHac.Boot } // If encrypted, check if the body can be decrypted - Crypto.Aes.DecryptCbc128(metaData2, metaData2, Keyset.MarikoBek, _metaData.Iv); + Crypto.Aes.DecryptCbc128(metaData2, metaData2, KeySet.MarikoBek, _metaData.Iv); IsDecrypted = metaData2.SequenceEqual(SpanHelpers.AsByteSpan(ref _metaData)); // Get a decrypted body storage if we have the correct key if (IsDecrypted) { - var decStorage = new AesCbcStorage(bodySubStorage, Keyset.MarikoBek, _metaData.Iv, true); + var decStorage = new AesCbcStorage(bodySubStorage, KeySet.MarikoBek, _metaData.Iv, true); BodyStorage = new CachedStorage(decStorage, 0x4000, 1, true); } @@ -282,13 +283,13 @@ namespace LibHac.Boot if (IsModern) { - decPk11Storage = new AesCbcStorage(encPk11Storage, Keyset.Package1Keys[KeyRevision], + decPk11Storage = new AesCbcStorage(encPk11Storage, KeySet.Package1Keys[KeyRevision], _stage1Footer.Iv, true); } else { - decPk11Storage = new Aes128CtrStorage(encPk11Storage, Keyset.Package1Keys[KeyRevision], - _stage1Footer.Iv.ToArray(), true); + decPk11Storage = new Aes128CtrStorage(encPk11Storage, + KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true); } Pk11Storage = new CachedStorage(decPk11Storage, 0x4000, 1, true); @@ -318,7 +319,7 @@ namespace LibHac.Boot for (int i = start; i < end; i++) { decryptor(SpanHelpers.AsByteSpan(ref _pk11Header), SpanHelpers.AsByteSpan(ref decHeader), - Keyset.Package1Keys[i], _stage1Footer.Iv); + KeySet.Package1Keys[i], _stage1Footer.Iv); if (decHeader.Magic == Package1Pk11Header.ExpectedMagic) { @@ -452,7 +453,7 @@ namespace LibHac.Boot new SubStorage(warmBootStorage, MarikoWarmBootPlainTextSectionSize, encryptedSectionSize); var zeroIv = new Buffer16(); - IStorage decryptedSection = new AesCbcStorage(encryptedSubStorage, Keyset.MarikoBek, zeroIv.Bytes, true); + IStorage decryptedSection = new AesCbcStorage(encryptedSubStorage, KeySet.MarikoBek, zeroIv.Bytes, true); decryptedSection = new CachedStorage(decryptedSection, 0x200, 1, true); diff --git a/src/LibHac/Boot/Package2StorageReader.cs b/src/LibHac/Boot/Package2StorageReader.cs index cfd2709e..187256ac 100644 --- a/src/LibHac/Boot/Package2StorageReader.cs +++ b/src/LibHac/Boot/Package2StorageReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; using LibHac.FsSystem; @@ -19,27 +20,27 @@ namespace LibHac.Boot private IStorage _storage; private Package2Header _header; - private Keyset _keyset; - private byte[] _key; + private KeySet _keySet; + private AesKey _key; public ref readonly Package2Header Header => ref _header; /// /// Initializes the . /// - /// The keyset to use for decrypting the package. + /// The keyset to use for decrypting the package. /// An of the encrypted package2. /// The of the operation. - public Result Initialize(Keyset keyset, IStorage storage) + public Result Initialize(KeySet keySet, IStorage storage) { Result rc = storage.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) return rc; - _key = keyset.Package2Keys[_header.Meta.KeyGeneration]; + _key = keySet.Package2Keys[_header.Meta.KeyGeneration]; DecryptHeader(_key, ref _header.Meta, ref _header.Meta); _storage = storage; - _keyset = keyset; + _keySet = keySet; return Result.Success; } @@ -69,7 +70,7 @@ namespace LibHac.Boot } byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray(); - payloadStorage = new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key, iv, true), 0x4000, 1, true); + payloadStorage = new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true); return Result.Success; } @@ -142,7 +143,7 @@ namespace LibHac.Boot Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes); if (rc.IsFailure()) return rc; - return _header.VerifySignature(_keyset.Package2FixedKeyModulus, metaBytes); + return _header.VerifySignature(_keySet.Package2SigningKey.Modulus, metaBytes); } /// @@ -230,7 +231,7 @@ namespace LibHac.Boot byte[] iv = _header.Meta.HeaderIv.Bytes.ToArray(); Utilities.IncrementByteArray(iv); - storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key, iv, true), 0x100, 1, true)); + storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true)); // Open all the payloads for (int i = 0; i < Package2Header.PayloadCount; i++) @@ -252,7 +253,7 @@ namespace LibHac.Boot return Result.Success; } - private void DecryptHeader(byte[] key, ref Package2Meta source, ref Package2Meta dest) + private void DecryptHeader(ReadOnlySpan key, ref Package2Meta source, ref Package2Meta dest) { Buffer16 iv = source.HeaderIv; diff --git a/src/LibHac/Common/Keys/KeySet.cs b/src/LibHac/Common/Keys/KeySet.cs index 1eec5f3b..b8011338 100644 --- a/src/LibHac/Common/Keys/KeySet.cs +++ b/src/LibHac/Common/Keys/KeySet.cs @@ -101,12 +101,16 @@ namespace LibHac.Common.Keys public Span DeviceUniqueSaveMacKeys => _keys._deviceKeys.DeviceUniqueSaveMacKeys.Items; public ref AesKey SeedUniqueSaveMacKey => ref _keys._deviceKeys.SeedUniqueSaveMacKey; public ref AesKey SdCardEncryptionSeed => ref _keys._deviceKeys.SdCardEncryptionSeed; + + // Todo: Make a separate type? Not actually an AES-XTS key, but it's still the same shape. public Span SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items; private RsaSigningKeyParameters _rsaSigningKeyParamsDev; private RsaSigningKeyParameters _rsaSigningKeyParamsProd; private RsaKeyParameters _rsaKeyParams; + public RSAParameters ETicketExtKeyRsa { get; set; } + public Span NcaHeaderSigningKeys { get diff --git a/src/LibHac/FsSrv/Creators/EncryptedFileSystemCreator.cs b/src/LibHac/FsSrv/Creators/EncryptedFileSystemCreator.cs index 04790031..b92be85c 100644 --- a/src/LibHac/FsSrv/Creators/EncryptedFileSystemCreator.cs +++ b/src/LibHac/FsSrv/Creators/EncryptedFileSystemCreator.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -7,11 +8,11 @@ namespace LibHac.FsSrv.Creators { public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator { - private Keyset Keyset { get; } + private KeySet KeySet { get; } - public EncryptedFileSystemCreator(Keyset keyset) + public EncryptedFileSystemCreator(KeySet keySet) { - Keyset = keyset; + KeySet = keySet; } public Result Create(out IFileSystem encryptedFileSystem, IFileSystem baseFileSystem, EncryptedFsKeyId keyId, @@ -25,9 +26,10 @@ namespace LibHac.FsSrv.Creators } // todo: "proper" key generation instead of a lazy hack - Keyset.SetSdSeed(encryptionSeed.ToArray()); + KeySet.SetSdSeed(encryptionSeed.ToArray()); - encryptedFileSystem = new AesXtsFileSystem(baseFileSystem, Keyset.SdCardKeys[(int)keyId], 0x4000); + encryptedFileSystem = new AesXtsFileSystem(baseFileSystem, + KeySet.SdCardEncryptionKeys[(int) keyId].DataRo.ToArray(), 0x4000); return Result.Success; } diff --git a/src/LibHac/FsSrv/Creators/SaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/Creators/SaveDataFileSystemCreator.cs index 495cacfc..96a337d0 100644 --- a/src/LibHac/FsSrv/Creators/SaveDataFileSystemCreator.cs +++ b/src/LibHac/FsSrv/Creators/SaveDataFileSystemCreator.cs @@ -1,5 +1,6 @@ using System; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -9,11 +10,11 @@ namespace LibHac.FsSrv.Creators { public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator { - private Keyset Keyset { get; } + private KeySet KeySet { get; } - public SaveDataFileSystemCreator(Keyset keyset) + public SaveDataFileSystemCreator(KeySet keySet) { - Keyset = keyset; + KeySet = keySet; } public Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode) @@ -61,7 +62,7 @@ namespace LibHac.FsSrv.Creators if (rc.IsFailure()) return rc; var saveDataStorage = new DisposingFileStorage(saveDataFile); - fileSystem = new SaveDataFileSystem(Keyset, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false); + fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false); // Todo: ISaveDataExtraDataAccessor diff --git a/src/LibHac/FsSrv/Creators/StorageOnNcaCreator.cs b/src/LibHac/FsSrv/Creators/StorageOnNcaCreator.cs index f5ddb71c..422d91e9 100644 --- a/src/LibHac/FsSrv/Creators/StorageOnNcaCreator.cs +++ b/src/LibHac/FsSrv/Creators/StorageOnNcaCreator.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -11,11 +12,11 @@ namespace LibHac.FsSrv.Creators { // ReSharper disable once UnusedMember.Local private bool IsEnabledProgramVerification { get; set; } - private Keyset Keyset { get; } + private KeySet KeySet { get; } - public StorageOnNcaCreator(Keyset keyset) + public StorageOnNcaCreator(KeySet keySet) { - Keyset = keyset; + KeySet = keySet; } // todo: Implement NcaReader and other Nca classes @@ -52,7 +53,7 @@ namespace LibHac.FsSrv.Creators public Result OpenNca(out Nca nca, IStorage ncaStorage) { - nca = new Nca(Keyset, ncaStorage); + nca = new Nca(KeySet, ncaStorage); return Result.Success; } diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs index 357a77e3..d9f0e7e0 100644 --- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs +++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs @@ -1,4 +1,5 @@ -using LibHac.Fs.Fsa; +using LibHac.Common.Keys; +using LibHac.Fs.Fsa; using LibHac.FsSrv.Creators; namespace LibHac.FsSrv @@ -10,23 +11,23 @@ namespace LibHac.FsSrv public EmulatedGameCard GameCard { get; set; } public EmulatedSdCard SdCard { get; set; } - public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, Keyset keyset) + public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet) { var creators = new FileSystemCreators(); - var gameCard = new EmulatedGameCard(keyset); + var gameCard = new EmulatedGameCard(keySet); var sdCard = new EmulatedSdCard(); var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard); creators.RomFileSystemCreator = new RomFileSystemCreator(); creators.PartitionFileSystemCreator = new PartitionFileSystemCreator(); - creators.StorageOnNcaCreator = new StorageOnNcaCreator(keyset); + creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet); creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); - creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset); + creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keySet); creators.GameCardStorageCreator = gcStorageCreator; creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard); - creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset); + creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet); creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem); creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(sdCard, rootFileSystem); diff --git a/src/LibHac/FsSrv/EmulatedGameCard.cs b/src/LibHac/FsSrv/EmulatedGameCard.cs index 48266de9..2c22a7ae 100644 --- a/src/LibHac/FsSrv/EmulatedGameCard.cs +++ b/src/LibHac/FsSrv/EmulatedGameCard.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common.Keys; using LibHac.Fs; namespace LibHac.FsSrv @@ -9,13 +10,13 @@ namespace LibHac.FsSrv private int Handle { get; set; } private XciHeader CardHeader { get; set; } private Xci CardImage { get; set; } - private Keyset Keyset { get; set; } + private KeySet KeySet { get; set; } public EmulatedGameCard() { } - public EmulatedGameCard(Keyset keyset) + public EmulatedGameCard(KeySet keySet) { - Keyset = keyset; + KeySet = keySet; } public GameCardHandle GetGameCardHandle() { @@ -38,7 +39,7 @@ namespace LibHac.FsSrv CardImageStorage = cardImageStorage; - CardImage = new Xci(Keyset, cardImageStorage); + CardImage = new Xci(KeySet, cardImageStorage); CardHeader = CardImage.Header; } diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs index a1931d5d..99257c78 100644 --- a/src/LibHac/FsSystem/NcaUtils/Nca.cs +++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Diag; using LibHac.Fs; @@ -14,7 +15,7 @@ namespace LibHac.FsSystem.NcaUtils { public class Nca { - private Keyset Keyset { get; } + private KeySet KeySet { get; } private bool IsEncrypted => Header.IsEncrypted; private byte[] Nca0KeyArea { get; set; } @@ -24,11 +25,11 @@ namespace LibHac.FsSystem.NcaUtils public NcaHeader Header { get; } - public Nca(Keyset keyset, IStorage storage) + public Nca(KeySet keySet, IStorage storage) { - Keyset = keyset; + KeySet = keySet; BaseStorage = storage; - Header = new NcaHeader(keyset, storage); + Header = new NcaHeader(keySet, storage); } public byte[] GetDecryptedKey(int index) @@ -42,11 +43,11 @@ namespace LibHac.FsSystem.NcaUtils } int keyRevision = Utilities.GetMasterKeyRevision(Header.KeyGeneration); - byte[] keyAreaKey = Keyset.KeyAreaKeys[keyRevision][Header.KeyAreaKeyIndex]; + byte[] keyAreaKey = KeySet.KeyAreaKeys[keyRevision][Header.KeyAreaKeyIndex].DataRo.ToArray(); if (keyAreaKey.IsEmpty()) { - string keyName = $"key_area_key_{Keyset.KakNames[Header.KeyAreaKeyIndex]}_{keyRevision:x2}"; + string keyName = $"key_area_key_{KakNames[Header.KeyAreaKeyIndex]}_{keyRevision:x2}"; throw new MissingKeyException("Unable to decrypt NCA section.", keyName, KeyType.Common); } @@ -58,14 +59,16 @@ namespace LibHac.FsSystem.NcaUtils return decryptedKey; } + private static readonly string[] KakNames = { "application", "ocean", "system" }; + public byte[] GetDecryptedTitleKey() { int keyRevision = Utilities.GetMasterKeyRevision(Header.KeyGeneration); - byte[] titleKek = Keyset.TitleKeks[keyRevision]; + byte[] titleKek = KeySet.TitleKeks[keyRevision].DataRo.ToArray(); var rightsId = new RightsId(Header.RightsId); - if (Keyset.ExternalKeySet.Get(rightsId, out AccessKey accessKey).IsFailure()) + if (KeySet.ExternalKeySet.Get(rightsId, out AccessKey accessKey).IsFailure()) { throw new MissingKeyException("Missing NCA title key.", rightsId.ToString(), KeyType.Title); } @@ -108,11 +111,11 @@ namespace LibHac.FsSystem.NcaUtils if (Header.HasRightsId) { - return Keyset.ExternalKeySet.Contains(new RightsId(Header.RightsId)) && - !Keyset.TitleKeks[keyRevision].IsEmpty(); + return KeySet.ExternalKeySet.Contains(new RightsId(Header.RightsId)) && + !KeySet.TitleKeks[keyRevision].IsEmpty(); } - return !Keyset.KeyAreaKeys[keyRevision][Header.KeyAreaKeyIndex].IsEmpty(); + return !KeySet.KeyAreaKeys[keyRevision][Header.KeyAreaKeyIndex].IsEmpty(); } public bool SectionExists(NcaSectionType type) @@ -586,13 +589,13 @@ namespace LibHac.FsSystem.NcaUtils switch (Header.Version) { case 3: - header = new CachedStorage(new Aes128XtsStorage(rawHeaderStorage, Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); + header = new CachedStorage(new Aes128XtsStorage(rawHeaderStorage, KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; case 2: header = OpenNca2Header(headerSize, !openEncrypted); break; case 0: - header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); + header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; default: throw new NotSupportedException("Unsupported NCA version"); @@ -606,11 +609,11 @@ namespace LibHac.FsSystem.NcaUtils const int sectorSize = NcaHeader.HeaderSectorSize; var sources = new List(); - sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), Keyset.HeaderKey, sectorSize, true, decrypting), 1, true)); + sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), KeySet.HeaderKey, sectorSize, true, decrypting), 1, true)); for (int i = 0x400; i < size; i += sectorSize) { - sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(i, sectorSize), Keyset.HeaderKey, sectorSize, true, decrypting), 1, true)); + sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(i, sectorSize), KeySet.HeaderKey, sectorSize, true, decrypting), 1, true)); } return new ConcatenationStorage(sources, true); @@ -630,7 +633,7 @@ namespace LibHac.FsSystem.NcaUtils Span keyArea = Header.GetKeyArea(); var decKeyArea = new byte[0x100]; - if (CryptoOld.DecryptRsaOaep(keyArea, decKeyArea, Keyset.Nca0RsaKeyAreaKey, out _)) + if (CryptoOld.DecryptRsaOaep(keyArea, decKeyArea, KeySet.BetaNca0KeyAreaKey, out _)) { Nca0KeyArea = decKeyArea; } @@ -708,7 +711,7 @@ namespace LibHac.FsSystem.NcaUtils public Validity VerifyHeaderSignature() { - return Header.VerifySignature1(Keyset.NcaHdrFixedKeyModulus); + return Header.VerifySignature1(KeySet.NcaHeaderSigningKeys[0].Modulus); } internal void GenerateAesCounter(int sectionIndex, Ncm.ContentType type, int minorVersion) diff --git a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs index f52d3d22..60945f30 100644 --- a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs +++ b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs @@ -3,6 +3,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Diag; using LibHac.Fs; @@ -21,9 +22,9 @@ namespace LibHac.FsSystem.NcaUtils public NcaVersion FormatVersion { get; } public bool IsEncrypted { get; } - public NcaHeader(Keyset keyset, IStorage headerStorage) + public NcaHeader(KeySet keySet, IStorage headerStorage) { - (byte[] header, bool isEncrypted) = DecryptHeader(keyset, headerStorage); + (byte[] header, bool isEncrypted) = DecryptHeader(keySet, headerStorage); _header = header; IsEncrypted = isEncrypted; @@ -195,7 +196,7 @@ namespace LibHac.FsSystem.NcaUtils return (long)blockIndex * BlockSize; } - private static (byte[] header, bool isEncrypted) DecryptHeader(Keyset keyset, IStorage storage) + private static (byte[] header, bool isEncrypted) DecryptHeader(KeySet keySet, IStorage storage) { var buf = new byte[HeaderSize]; storage.Read(0, buf).ThrowIfFailure(); @@ -212,8 +213,8 @@ namespace LibHac.FsSystem.NcaUtils return (buf, false); } - byte[] key1 = keyset.HeaderKey.AsSpan(0, 0x10).ToArray(); - byte[] key2 = keyset.HeaderKey.AsSpan(0x10, 0x10).ToArray(); + byte[] key1 = keySet.HeaderKey.SubKeys[0].DataRo.ToArray(); + byte[] key2 = keySet.HeaderKey.SubKeys[1].DataRo.ToArray(); var transform = new Aes128XtsTransform(key1, key2, true); diff --git a/src/LibHac/FsSystem/Save/Header.cs b/src/LibHac/FsSystem/Save/Header.cs index 6c8b4250..55ecca34 100644 --- a/src/LibHac/FsSystem/Save/Header.cs +++ b/src/LibHac/FsSystem/Save/Header.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; @@ -37,7 +38,7 @@ namespace LibHac.FsSystem.Save public byte[] Data { get; } - public Header(IStorage storage, Keyset keyset) + public Header(IStorage storage, KeySet keySet) { MainStorage = storage; MainHeader = MainStorage.Slice(0x100, 0x200); @@ -86,14 +87,14 @@ namespace LibHac.FsSystem.Save Sha256.GenerateSha256Hash(Data.AsSpan(0x300, 0x3d00), actualHeaderHash); HeaderHashValidity = Utilities.SpansEqual(Layout.Hash, actualHeaderHash) ? Validity.Valid : Validity.Invalid; - SignatureValidity = ValidateSignature(keyset); + SignatureValidity = ValidateSignature(keySet); } - private Validity ValidateSignature(Keyset keyset) + private Validity ValidateSignature(KeySet keySet) { Span calculatedCmac = stackalloc byte[0x10]; - Aes.CalculateCmac(calculatedCmac, Data.AsSpan(0x100, 0x200), keyset.SaveMacKey); + Aes.CalculateCmac(calculatedCmac, Data.AsSpan(0x100, 0x200), keySet.DeviceUniqueSaveMacKeys[0]); return CryptoUtil.IsSameBytes(calculatedCmac, Cmac, Aes.BlockSize) ? Validity.Valid : Validity.Invalid; } diff --git a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs index f6d4a37a..ffdb6029 100644 --- a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; using LibHac.Fs.Fsa; @@ -28,16 +29,16 @@ namespace LibHac.FsSystem.Save public HierarchicalIntegrityVerificationStorage CoreDataIvfcStorage { get; } public HierarchicalIntegrityVerificationStorage FatIvfcStorage { get; } - private Keyset Keyset { get; } + private KeySet KeySet { get; } - public SaveDataFileSystem(Keyset keyset, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen) + public SaveDataFileSystem(KeySet keySet, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen) { BaseStorage = storage; LeaveOpen = leaveOpen; - Keyset = keyset; + KeySet = keySet; - var headerA = new Header(BaseStorage, keyset); - var headerB = new Header(BaseStorage.Slice(0x4000), keyset); + var headerA = new Header(BaseStorage, keySet); + var headerB = new Header(BaseStorage.Slice(0x4000), keySet); if (headerA.HeaderHashValidity == Validity.Valid) { @@ -238,12 +239,12 @@ namespace LibHac.FsSystem.Save protected override Result DoCommit() { - Result result = Commit(Keyset); + Result result = Commit(KeySet); return SaveResults.ConvertToExternalResult(result).LogConverted(result); } - public Result Commit(Keyset keyset) + public Result Commit(KeySet keySet) { CoreDataIvfcStorage.Flush(); FatIvfcStorage?.Flush(); @@ -261,7 +262,7 @@ namespace LibHac.FsSystem.Save headerStream.Position = 0x108; headerStream.Write(hash, 0, hash.Length); - if (keyset == null || keyset.SaveMacKey.IsEmpty()) return ResultFs.PreconditionViolation.Log(); + if (keySet == null || keySet.DeviceUniqueSaveMacKeys[0].IsEmpty()) return ResultFs.PreconditionViolation.Log(); var cmacData = new byte[0x200]; var cmac = new byte[0x10]; @@ -269,7 +270,7 @@ namespace LibHac.FsSystem.Save headerStream.Position = 0x100; headerStream.Read(cmacData, 0, 0x200); - Aes.CalculateCmac(cmac, cmacData, keyset.SaveMacKey); + Aes.CalculateCmac(cmac, cmacData, keySet.DeviceUniqueSaveMacKeys[0]); headerStream.Position = 0; headerStream.Write(cmac, 0, 0x10); diff --git a/src/LibHac/KeyType.cs b/src/LibHac/KeyType.cs new file mode 100644 index 00000000..be68f96e --- /dev/null +++ b/src/LibHac/KeyType.cs @@ -0,0 +1,10 @@ +namespace LibHac +{ + public enum KeyType + { + None, + Common, + Unique, + Title + } +} diff --git a/src/LibHac/Keyset.cs b/src/LibHac/Keyset.cs deleted file mode 100644 index d56c25b0..00000000 --- a/src/LibHac/Keyset.cs +++ /dev/null @@ -1,885 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using LibHac.Crypto; -using LibHac.Fs; -using LibHac.FsSrv; -using LibHac.Spl; -using Aes = LibHac.Crypto.Aes; - -namespace LibHac -{ - public class Keyset - { - /// - /// The number of keyblobs that were used for < 6.2.0 crypto - /// - private const int UsedKeyblobCount = 6; - - private const int SdCardKeyIdCount = 3; - - public byte[][] KeyblobKeys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] KeyblobMacKeys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] EncryptedKeyblobs { get; } = Utilities.CreateJaggedByteArray(0x20, 0xB0); - public byte[][] Keyblobs { get; } = Utilities.CreateJaggedByteArray(0x20, 0x90); - public byte[][] KeyblobKeySources { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[] KeyblobMacKeySource { get; } = new byte[0x10]; - public byte[][] TsecRootKeys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] MasterKekSources { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] MarikoMasterKekSources { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] MasterKeks { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[] MasterKeySource { get; } = new byte[0x10]; - public byte[][] MasterKeys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] Package1Keys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][] Package2Keys { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[] Package2KeySource { get; } = new byte[0x10]; - public byte[] AesKekGenerationSource { get; } = new byte[0x10]; - public byte[] AesKeyGenerationSource { get; } = new byte[0x10]; - public byte[] KeyAreaKeyApplicationSource { get; } = new byte[0x10]; - public byte[] KeyAreaKeyOceanSource { get; } = new byte[0x10]; - public byte[] KeyAreaKeySystemSource { get; } = new byte[0x10]; - public byte[] SaveMacKekSource { get; } = new byte[0x10]; - public byte[] SaveMacSdCardKekSource { get; } = new byte[0x10]; - public byte[] SaveMacKeySource { get; } = new byte[0x10]; - public byte[] SaveMacSdCardKeySource { get; } = new byte[0x10]; - public byte[] TitleKekSource { get; } = new byte[0x10]; - public byte[] HeaderKekSource { get; } = new byte[0x10]; - public byte[] SdCardKekSource { get; } = new byte[0x10]; - public byte[][] SdCardKeySources { get; } = Utilities.CreateJaggedByteArray(SdCardKeyIdCount, 0x20); - public byte[] HeaderKeySource { get; } = new byte[0x20]; - public byte[] HeaderKey { get; } = new byte[0x20]; - public byte[] XciHeaderKey { get; } = new byte[0x10]; - public byte[][] TitleKeks { get; } = Utilities.CreateJaggedByteArray(0x20, 0x10); - public byte[][][] KeyAreaKeys { get; } = Utilities.CreateJaggedByteArray(0x20, 3, 0x10); - public byte[] EticketRsaKek { get; } = new byte[0x10]; - public byte[] RetailSpecificAesKeySource { get; } = new byte[0x10]; - public byte[] PerConsoleKeySource { get; } = new byte[0x10]; - public byte[] BisKekSource { get; } = new byte[0x10]; - public byte[][] BisKeySource { get; } = Utilities.CreateJaggedByteArray(4, 0x20); - public byte[] SslRsaKek { get; } = new byte[0x10]; - public byte[] MarikoBek { get; } = new byte[0x10]; - public byte[] MarikoKek { get; } = new byte[0x10]; - - // Device-specific keys - public byte[] SecureBootKey { get; } = new byte[0x10]; - public byte[] TsecKey { get; } = new byte[0x10]; - public byte[] DeviceKey { get; } = new byte[0x10]; - public byte[][] BisKeys { get; } = Utilities.CreateJaggedByteArray(4, 0x20); - public byte[] SaveMacKey { get; } = new byte[0x10]; - public byte[] SaveMacSdCardKey { get; } = new byte[0x10]; - public byte[] SdSeed { get; } = new byte[0x10]; - public byte[][] SdCardKeySourcesSpecific { get; } = Utilities.CreateJaggedByteArray(SdCardKeyIdCount, 0x20); - public byte[][] SdCardKeys { get; } = Utilities.CreateJaggedByteArray(SdCardKeyIdCount, 0x20); - - public RSAParameters EticketExtKeyRsa { get; set; } - - private RSAParameters? _nca0RsaKeyAreaKey; - public RSAParameters Nca0RsaKeyAreaKey - { - // Lazily init this key - get - { - if (_nca0RsaKeyAreaKey.HasValue) - return _nca0RsaKeyAreaKey.Value; - - _nca0RsaKeyAreaKey = Rsa.RecoverParameters(BetaNca0Modulus, new byte[] { 1, 0, 1 }, BetaNca0Exponent); - return _nca0RsaKeyAreaKey.Value; - } - } - - public bool KeysetForDev; - public byte[] NcaHdrFixedKeyModulus - { - get - { - if (KeysetForDev) - { - return NcaHdrFixedKeyModulusDev; - } - else - { - return NcaHdrFixedKeyModulusProd; - } - } - } - - public byte[] AcidFixedKeyModulus - { - get - { - if (KeysetForDev) - { - return AcidFixedKeyModulusDev; - } - else - { - return AcidFixedKeyModulusProd; - } - } - } - - public byte[] Package2FixedKeyModulus - { - get - { - if (KeysetForDev) - { - return Package2FixedKeyModulusDev; - } - else - { - return Package2FixedKeyModulusProd; - } - } - } - - public readonly byte[] BetaNca0Modulus = - { - 0xAD, 0x58, 0xEE, 0x97, 0xF9, 0x47, 0x90, 0x7D, 0xF9, 0x29, 0x5F, 0x1F, 0x39, 0x68, 0xEE, 0x49, - 0x4C, 0x1E, 0x8D, 0x84, 0x91, 0x31, 0x5D, 0xE5, 0x96, 0x27, 0xB2, 0xB3, 0x59, 0x7B, 0xDE, 0xFD, - 0xB7, 0xEB, 0x40, 0xA1, 0xE7, 0xEB, 0xDC, 0x60, 0xD0, 0x3D, 0xC5, 0x50, 0x92, 0xAD, 0x3D, 0xC4, - 0x8C, 0x17, 0xD2, 0x37, 0x66, 0xE3, 0xF7, 0x14, 0x34, 0x38, 0x6B, 0xA7, 0x2B, 0x21, 0x10, 0x9B, - 0x73, 0x49, 0x15, 0xD9, 0x2A, 0x90, 0x86, 0x76, 0x81, 0x6A, 0x10, 0xBD, 0x74, 0xC4, 0x20, 0x55, - 0x25, 0xA8, 0x02, 0xC5, 0xA0, 0x34, 0x36, 0x7B, 0x66, 0x47, 0x2C, 0x7E, 0x47, 0x82, 0xA5, 0xD4, - 0xA3, 0x42, 0x45, 0xE8, 0xFD, 0x65, 0x72, 0x48, 0xA1, 0xB0, 0x44, 0x10, 0xEF, 0xAC, 0x1D, 0x0F, - 0xB5, 0x12, 0x19, 0xA8, 0x41, 0x0B, 0x76, 0x3B, 0xBC, 0xF1, 0x4A, 0x10, 0x46, 0x22, 0xB8, 0xF1, - 0xBC, 0x21, 0x81, 0x69, 0x9B, 0x63, 0x6F, 0xD7, 0xB9, 0x60, 0x2A, 0x9A, 0xE5, 0x2C, 0x47, 0x72, - 0x59, 0x65, 0xA2, 0x21, 0x60, 0xC4, 0xFC, 0xB0, 0xD7, 0x6F, 0x42, 0xC9, 0x0C, 0xF5, 0x76, 0x7D, - 0xF2, 0x5C, 0xE0, 0x80, 0x0F, 0xEE, 0x45, 0x7E, 0x4E, 0x3A, 0x8D, 0x9C, 0x5B, 0x5B, 0xD9, 0xD1, - 0x43, 0x94, 0x2C, 0xC7, 0x2E, 0xB9, 0x4A, 0xE5, 0x3E, 0x15, 0xDD, 0x43, 0x00, 0xF7, 0x78, 0xE7, - 0x7C, 0x39, 0xB0, 0x4D, 0xC5, 0xD1, 0x1C, 0xF2, 0xB4, 0x7A, 0x2A, 0xEA, 0x0A, 0x8E, 0xB9, 0x13, - 0xB4, 0x4F, 0xD7, 0x5B, 0x4D, 0x7B, 0x43, 0xB0, 0x3A, 0x9A, 0x60, 0x22, 0x47, 0x91, 0x78, 0xC7, - 0x10, 0x64, 0xE0, 0x2C, 0x69, 0xD1, 0x66, 0x3C, 0x42, 0x2E, 0xEF, 0x19, 0x21, 0x89, 0x8E, 0xE1, - 0xB0, 0xB4, 0xD0, 0x17, 0xA1, 0x0F, 0x73, 0x98, 0x5A, 0xF6, 0xEE, 0xC0, 0x2F, 0x9E, 0xCE, 0xC5 - }; - - public readonly byte[] BetaNca0Exponent = - { - 0x3C, 0x66, 0x37, 0x44, 0x26, 0xAC, 0x63, 0xD1, 0x30, 0xE6, 0xD4, 0x68, 0xF9, 0xC4, 0xF0, 0xFA, - 0x03, 0x16, 0xC6, 0x32, 0x81, 0xB0, 0x94, 0xC9, 0xF1, 0x26, 0xC5, 0xE2, 0x2D, 0xF4, 0xB6, 0x3E, - 0xEB, 0x3D, 0x82, 0x18, 0xA7, 0xC9, 0x8B, 0xD1, 0x03, 0xDD, 0xF2, 0x09, 0x60, 0x02, 0x12, 0xFA, - 0x8F, 0xE1, 0xA0, 0xF2, 0x82, 0xDC, 0x3D, 0x74, 0x01, 0xBA, 0x02, 0xF0, 0x8D, 0x5B, 0x89, 0x00, - 0xD1, 0x0B, 0x8F, 0x1C, 0x4A, 0xF3, 0x6E, 0x96, 0x8E, 0x03, 0x19, 0xF0, 0x19, 0x66, 0x58, 0xE9, - 0xB2, 0x24, 0x37, 0x4B, 0x0A, 0xC6, 0x06, 0x91, 0xBA, 0x92, 0x64, 0x13, 0x5F, 0xF1, 0x4A, 0xBC, - 0xAB, 0x61, 0xE5, 0x20, 0x08, 0x62, 0xB7, 0x8E, 0x4D, 0x20, 0x30, 0xA7, 0x42, 0x0B, 0x53, 0x58, - 0xEC, 0xBB, 0x70, 0xCB, 0x2A, 0x56, 0xC7, 0x0C, 0x8B, 0x89, 0xFB, 0x47, 0x6E, 0x58, 0x9C, 0xDD, - 0xB2, 0xE5, 0x4F, 0x49, 0x52, 0x0B, 0xD9, 0x96, 0x30, 0x8D, 0xDE, 0xC9, 0x0F, 0x6A, 0x82, 0xC7, - 0xE8, 0x20, 0xB6, 0xB3, 0x95, 0xDD, 0xEB, 0xDF, 0xF7, 0x25, 0x23, 0x6B, 0xF8, 0x5B, 0xD4, 0x81, - 0x7A, 0xBC, 0x94, 0x13, 0x30, 0x59, 0x28, 0xC8, 0xC9, 0x3A, 0x5D, 0xCC, 0x8D, 0xFD, 0x1A, 0xE1, - 0xCB, 0xA4, 0x1D, 0xD4, 0x45, 0xF1, 0xBF, 0x87, 0x6C, 0x0E, 0xB1, 0x44, 0xC7, 0x88, 0x62, 0x2B, - 0x43, 0xAD, 0x75, 0xE6, 0x69, 0xFF, 0xD3, 0x39, 0xF5, 0x7F, 0x2A, 0xA2, 0x5F, 0x7A, 0x5E, 0xE6, - 0xEF, 0xCB, 0x2F, 0x2C, 0x90, 0xE6, 0x4B, 0x2D, 0x94, 0x62, 0xE8, 0xEC, 0x54, 0x7B, 0x94, 0xB7, - 0xFB, 0x72, 0x05, 0xFB, 0xB3, 0x23, 0xCA, 0xF8, 0xD4, 0x5C, 0xF6, 0xAC, 0x7D, 0xEC, 0x47, 0xC6, - 0xD3, 0x22, 0x5D, 0x7C, 0x15, 0xDD, 0x48, 0xE9, 0xBF, 0xA8, 0x99, 0x33, 0x02, 0x79, 0xD3, 0x65 - }; - - private static readonly byte[] NcaHdrFixedKeyModulusProd = - { - 0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F, - 0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58, - 0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16, - 0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2, - 0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF, - 0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67, - 0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26, - 0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C, - 0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE, - 0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86, - 0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35, - 0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B, - 0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29, - 0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40, - 0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81, - 0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03 - }; - - private static readonly byte[] AcidFixedKeyModulusProd = - { - 0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, - 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, - 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, - 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, - 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, - 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, - 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, - 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, - 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, - 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, - 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, - 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, - 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, - 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, - 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, - 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD - }; - - private static readonly byte[] Package2FixedKeyModulusProd = - { - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - }; - - private static readonly byte[] NcaHdrFixedKeyModulusDev = - { - 0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4, - 0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49, - 0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C, - 0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B, - 0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4, - 0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47, - 0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE, - 0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D, - 0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E, - 0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0, - 0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D, - 0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E, - 0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B, - 0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E, - 0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62, - 0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9 - }; - - private static readonly byte[] AcidFixedKeyModulusDev = - { - 0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89, - 0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87, - 0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C, - 0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B, - 0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5, - 0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32, - 0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53, - 0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4, - 0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA, - 0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B, - 0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F, - 0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33, - 0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C, - 0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3, - 0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0, - 0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69 - }; - - private static readonly byte[] Package2FixedKeyModulusDev = - { - 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, - 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, - 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, - 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, - 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, - 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, - 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, - 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, - 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, - 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, - 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, - 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, - 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, - 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, - 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, - 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B - }; - - public ExternalKeySet ExternalKeySet { get; } = new ExternalKeySet(); - - public void SetSdSeed(byte[] sdseed) - { - Array.Copy(sdseed, SdSeed, SdSeed.Length); - DeriveSdCardKeys(); - } - - public void DeriveKeys(IProgressReport logger = null) - { - DeriveKeyblobKeys(); - DecryptKeyblobs(logger); - ReadKeyblobs(); - - Derive620MasterKeks(); - DeriveMasterKeys(); - - DerivePerConsoleKeys(); - DerivePerFirmwareKeys(); - DeriveNcaHeaderKey(); - DeriveSdCardKeys(); - } - - private void DeriveKeyblobKeys() - { - if (SecureBootKey.IsEmpty() || TsecKey.IsEmpty()) return; - - bool haveKeyblobMacKeySource = !MasterKeySource.IsEmpty(); - var temp = new byte[0x10]; - - for (int i = 0; i < UsedKeyblobCount; i++) - { - if (KeyblobKeySources[i].IsEmpty()) continue; - - Aes.DecryptEcb128(KeyblobKeySources[i], temp, TsecKey); - Aes.DecryptEcb128(temp, KeyblobKeys[i], SecureBootKey); - - if (!haveKeyblobMacKeySource) continue; - - Aes.DecryptEcb128(KeyblobMacKeySource, KeyblobMacKeys[i], KeyblobKeys[i]); - } - } - - private void DecryptKeyblobs(IProgressReport logger = null) - { - var cmac = new byte[0x10]; - var expectedCmac = new byte[0x10]; - var counter = new byte[0x10]; - - for (int i = 0; i < UsedKeyblobCount; i++) - { - if (KeyblobKeys[i].IsEmpty() || KeyblobMacKeys[i].IsEmpty() || EncryptedKeyblobs[i].IsEmpty()) - { - continue; - } - - Array.Copy(EncryptedKeyblobs[i], expectedCmac, 0x10); - Aes.CalculateCmac(cmac, EncryptedKeyblobs[i].AsSpan(0x10, 0xA0), KeyblobMacKeys[i]); - - if (!Utilities.ArraysEqual(cmac, expectedCmac)) - { - logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?"); - } - - Array.Copy(EncryptedKeyblobs[i], 0x10, counter, 0, 0x10); - - Aes.DecryptCtr128(EncryptedKeyblobs[i].AsSpan(0x20), Keyblobs[i], KeyblobKeys[i], counter); - } - } - - private void ReadKeyblobs() - { - for (int i = 0; i < UsedKeyblobCount; i++) - { - if (Keyblobs[i].IsEmpty()) continue; - - Array.Copy(Keyblobs[i], 0x80, Package1Keys[i], 0, 0x10); - Array.Copy(Keyblobs[i], MasterKeks[i], 0x10); - } - } - - private void Derive620MasterKeks() - { - for (int i = UsedKeyblobCount; i < 0x20; i++) - { - if (TsecRootKeys[i - UsedKeyblobCount].IsEmpty() || MasterKekSources[i].IsEmpty()) continue; - - Aes.DecryptEcb128(MasterKekSources[i], MasterKeks[i], TsecRootKeys[i - UsedKeyblobCount]); - } - } - - private void DeriveMasterKeys() - { - if (MasterKeySource.IsEmpty()) return; - - for (int i = 0; i < 0x20; i++) - { - if (MasterKeks[i].IsEmpty()) continue; - - Aes.DecryptEcb128(MasterKeySource, MasterKeys[i], MasterKeks[i]); - } - } - - private void DerivePerConsoleKeys() - { - var kek = new byte[0x10]; - - // Derive the device key - if (!PerConsoleKeySource.IsEmpty() && !KeyblobKeys[0].IsEmpty()) - { - Aes.DecryptEcb128(PerConsoleKeySource, DeviceKey, KeyblobKeys[0]); - } - - // Derive save key - if (!SaveMacKekSource.IsEmpty() && !SaveMacKeySource.IsEmpty() && !DeviceKey.IsEmpty()) - { - GenerateKek(DeviceKey, SaveMacKekSource, kek, AesKekGenerationSource, null); - Aes.DecryptEcb128(SaveMacKeySource, SaveMacKey, kek); - } - - // Derive BIS keys - if (DeviceKey.IsEmpty() - || BisKekSource.IsEmpty() - || AesKekGenerationSource.IsEmpty() - || AesKeyGenerationSource.IsEmpty() - || RetailSpecificAesKeySource.IsEmpty()) - { - return; - } - - // If the user doesn't provide bis_key_source_03 we can assume it's the same as bis_key_source_02 - if (BisKeySource[3].IsEmpty() && !BisKeySource[2].IsEmpty()) - { - Array.Copy(BisKeySource[2], BisKeySource[3], 0x20); - } - - Aes.DecryptEcb128(RetailSpecificAesKeySource, kek, DeviceKey); - if (!BisKeySource[0].IsEmpty()) Aes.DecryptEcb128(BisKeySource[0], BisKeys[0], kek); - - GenerateKek(DeviceKey, BisKekSource, kek, AesKekGenerationSource, AesKeyGenerationSource); - - for (int i = 1; i < 4; i++) - { - if (!BisKeySource[i].IsEmpty()) Aes.DecryptEcb128(BisKeySource[i], BisKeys[i], kek); - } - } - - private void DerivePerFirmwareKeys() - { - bool haveKakSource0 = !KeyAreaKeyApplicationSource.IsEmpty(); - bool haveKakSource1 = !KeyAreaKeyOceanSource.IsEmpty(); - bool haveKakSource2 = !KeyAreaKeySystemSource.IsEmpty(); - bool haveTitleKekSource = !TitleKekSource.IsEmpty(); - bool havePackage2KeySource = !Package2KeySource.IsEmpty(); - - for (int i = 0; i < 0x20; i++) - { - if (MasterKeys[i].IsEmpty()) - { - continue; - } - - if (haveKakSource0) - { - GenerateKek(MasterKeys[i], KeyAreaKeyApplicationSource, KeyAreaKeys[i][0], - AesKekGenerationSource, AesKeyGenerationSource); - } - - if (haveKakSource1) - { - GenerateKek(MasterKeys[i], KeyAreaKeyOceanSource, KeyAreaKeys[i][1], - AesKekGenerationSource, AesKeyGenerationSource); - } - - if (haveKakSource2) - { - GenerateKek(MasterKeys[i], KeyAreaKeySystemSource, KeyAreaKeys[i][2], - AesKekGenerationSource, AesKeyGenerationSource); - } - - if (haveTitleKekSource) - { - Aes.DecryptEcb128(TitleKekSource, TitleKeks[i], MasterKeys[i]); - } - - if (havePackage2KeySource) - { - Aes.DecryptEcb128(Package2KeySource, Package2Keys[i], MasterKeys[i]); - } - } - } - - private void DeriveNcaHeaderKey() - { - if (HeaderKekSource.IsEmpty() || HeaderKeySource.IsEmpty() || MasterKeys[0].IsEmpty()) return; - - var headerKek = new byte[0x10]; - - GenerateKek(MasterKeys[0], HeaderKekSource, headerKek, AesKekGenerationSource, - AesKeyGenerationSource); - Aes.DecryptEcb128(HeaderKeySource, HeaderKey, headerKek); - } - - public void DeriveSdCardKeys() - { - var sdKek = new byte[0x10]; - GenerateKek(MasterKeys[0], SdCardKekSource, sdKek, AesKekGenerationSource, AesKeyGenerationSource); - - for (int k = 0; k < SdCardKeyIdCount; k++) - { - for (int i = 0; i < 0x20; i++) - { - SdCardKeySourcesSpecific[k][i] = (byte)(SdCardKeySources[k][i] ^ SdSeed[i & 0xF]); - } - } - - for (int k = 0; k < SdCardKeyIdCount; k++) - { - Aes.DecryptEcb128(SdCardKeySourcesSpecific[k], SdCardKeys[k], sdKek); - } - - // Derive sd card save key - if (!SaveMacSdCardKekSource.IsEmpty() && !SaveMacSdCardKeySource.IsEmpty()) - { - var keySource = new byte[0x10]; - - for (int i = 0; i < 0x10; i++) - { - keySource[i] = (byte)(SaveMacSdCardKeySource[i] ^ SdSeed[i]); - } - - GenerateKek(MasterKeys[0], SaveMacSdCardKekSource, sdKek, AesKekGenerationSource, null); - Aes.DecryptEcb128(keySource, SaveMacSdCardKey, sdKek); - } - } - - internal static readonly string[] KakNames = { "application", "ocean", "system" }; - - public static int GetMasterKeyRevisionFromKeyGeneration(int keyGeneration) - { - if (keyGeneration == 0) return 0; - - return keyGeneration - 1; - } - - private static void GenerateKek(ReadOnlySpan key, ReadOnlySpan src, Span dest, ReadOnlySpan kekSeed, ReadOnlySpan keySeed) - { - Span kek = stackalloc byte[0x10]; - Span srcKek = stackalloc byte[0x10]; - - Aes.DecryptEcb128(kekSeed, kek, key); - Aes.DecryptEcb128(src, srcKek, kek); - - if (!keySeed.IsEmpty) - { - Aes.DecryptEcb128(keySeed, dest, srcKek); - } - else - { - srcKek.CopyTo(dest); - } - } - } - - public static class ExternalKeyReader - { - private const int TitleKeySize = 0x10; - - public static void ReadKeyFile(Keyset keyset, string filename, string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport logger = null) - { - Dictionary keyDictionary = CreateFullKeyDictionary(); - - if (filename != null) ReadMainKeys(keyset, filename, keyDictionary, logger); - if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, keyDictionary, logger); - if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, logger); - - keyset.ExternalKeySet.TrimExcess(); - keyset.DeriveKeys(logger); - } - - public static Keyset ReadKeyFile(string filename, string titleKeysFilename = null, string consoleKeysFilename = null, IProgressReport logger = null, bool dev = false) - { - var keyset = new Keyset(); - keyset.KeysetForDev = dev; - ReadKeyFile(keyset, filename, titleKeysFilename, consoleKeysFilename, logger); - - return keyset; - } - - public static void LoadConsoleKeys(this Keyset keyset, string filename, IProgressReport logger = null) - { - Dictionary uniqueKeyDictionary = CreateUniqueKeyDictionary(); - - foreach (KeyValue key in uniqueKeyDictionary.Values) - { - byte[] keyBytes = key.GetKey(keyset); - Array.Clear(keyBytes, 0, keyBytes.Length); - } - - ReadMainKeys(keyset, filename, uniqueKeyDictionary, logger); - keyset.DeriveKeys(); - } - - private static void ReadMainKeys(Keyset keyset, string filename, Dictionary keyDict, IProgressReport logger = null) - { - if (filename == null) return; - - using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) - { - string line; - while ((line = reader.ReadLine()) != null) - { - string[] a = line.Split(',', '='); - if (a.Length != 2) continue; - - string key = a[0].Trim(); - string valueStr = a[1].Trim(); - - if (!keyDict.TryGetValue(key, out KeyValue kv)) - { - logger?.LogMessage($"Failed to match key {key}"); - continue; - } - - byte[] value = valueStr.ToBytes(); - if (value.Length != kv.Size) - { - logger?.LogMessage($"Key {key} had incorrect size {value.Length}. (Expected {kv.Size})"); - continue; - } - - byte[] dest = kv.GetKey(keyset); - Array.Copy(value, dest, value.Length); - } - } - } - - private static void ReadTitleKeys(Keyset keyset, string filename, IProgressReport progress = null) - { - if (filename == null) return; - - using (var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) - { - string line; - while ((line = reader.ReadLine()) != null) - { - string[] splitLine; - - // Some people use pipes as delimiters - if (line.Contains('|')) - { - splitLine = line.Split('|'); - } - else - { - splitLine = line.Split(',', '='); - } - - if (splitLine.Length < 2) continue; - - if (!splitLine[0].Trim().TryToBytes(out byte[] rightsId)) - { - progress?.LogMessage($"Invalid rights ID \"{splitLine[0].Trim()}\" in title key file"); - continue; - } - - if (!splitLine[1].Trim().TryToBytes(out byte[] titleKey)) - { - progress?.LogMessage($"Invalid title key \"{splitLine[1].Trim()}\" in title key file"); - continue; - } - - if (rightsId.Length != TitleKeySize) - { - progress?.LogMessage($"Rights ID {rightsId.ToHexString()} had incorrect size {rightsId.Length}. (Expected {TitleKeySize})"); - continue; - } - - if (titleKey.Length != TitleKeySize) - { - progress?.LogMessage($"Title key {titleKey.ToHexString()} had incorrect size {titleKey.Length}. (Expected {TitleKeySize})"); - continue; - } - - keyset.ExternalKeySet.Add(new RightsId(rightsId), new AccessKey(titleKey)).ThrowIfFailure(); - } - } - } - - public static string PrintKeys(Keyset keyset, Dictionary dict) - { - if (dict.Count == 0) return string.Empty; - - var sb = new StringBuilder(); - int maxNameLength = dict.Values.Max(x => x.Name.Length); - int currentGroup = 0; - - foreach (KeyValue keySlot in dict.Values.Where(x => x.Group >= 0).OrderBy(x => x.Group).ThenBy(x => x.Name)) - { - byte[] key = keySlot.GetKey(keyset); - if (key.IsEmpty()) continue; - - if (keySlot.Group > currentGroup) - { - sb.AppendLine(); - currentGroup = keySlot.Group; - } - - string line = $"{keySlot.Name.PadRight(maxNameLength)} = {key.ToHexString()}"; - sb.AppendLine(line); - } - - return sb.ToString(); - } - - public static string PrintCommonKeys(Keyset keyset) - { - return PrintKeys(keyset, CreateCommonKeyDictionary()); - } - - public static string PrintUniqueKeys(Keyset keyset) - { - return PrintKeys(keyset, CreateUniqueKeyDictionary()); - } - - public static string PrintAllKeys(Keyset keyset) - { - return PrintKeys(keyset, CreateFullKeyDictionary()); - } - - public static string PrintTitleKeys(Keyset keyset) - { - var sb = new StringBuilder(); - - foreach ((RightsId rightsId, AccessKey key) kv in keyset.ExternalKeySet.ToList().OrderBy(x => x.rightsId.ToString())) - { - string line = $"{kv.rightsId} = {kv.key}"; - sb.AppendLine(line); - } - - return sb.ToString(); - } - - public static Dictionary CreateCommonKeyDictionary() - { - return CreateCommonKeyList().ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase); - } - - public static Dictionary CreateUniqueKeyDictionary() - { - return CreateUniqueKeyList().ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase); - } - - public static Dictionary CreateFullKeyDictionary() - { - List commonKeys = CreateCommonKeyList(); - List uniqueKeys = CreateUniqueKeyList(); - - return uniqueKeys.Concat(commonKeys).ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase); - } - - private static List CreateCommonKeyList() - { - var keys = new List - { - new KeyValue("keyblob_mac_key_source", 0x10, 0, set => set.KeyblobMacKeySource), - - new KeyValue("mariko_bek", 0x10, 32, set => set.MarikoBek), - new KeyValue("mariko_kek", 0x10, 32, set => set.MarikoKek), - - new KeyValue("master_key_source", 0x10, 60, set => set.MasterKeySource), - new KeyValue("package2_key_source", 0x10, 60, set => set.Package2KeySource), - - new KeyValue("aes_kek_generation_source", 0x10, 70, set => set.AesKekGenerationSource), - new KeyValue("aes_key_generation_source", 0x10, 70, set => set.AesKeyGenerationSource), - - new KeyValue("bis_kek_source", 0x10, 80, set => set.BisKekSource), - - new KeyValue("retail_specific_aes_key_source", 0x10, 90, set => set.RetailSpecificAesKeySource), - new KeyValue("per_console_key_source", 0x10, 90, set => set.PerConsoleKeySource), - - new KeyValue("header_kek_source", 0x10, 100, set => set.HeaderKekSource), - new KeyValue("header_key_source", 0x20, 100, set => set.HeaderKeySource), - new KeyValue("key_area_key_application_source", 0x10, 100, set => set.KeyAreaKeyApplicationSource), - new KeyValue("key_area_key_ocean_source", 0x10, 100, set => set.KeyAreaKeyOceanSource), - new KeyValue("key_area_key_system_source", 0x10, 100, set => set.KeyAreaKeySystemSource), - new KeyValue("titlekek_source", 0x10, 100, set => set.TitleKekSource), - - new KeyValue("save_mac_kek_source", 0x10, 110, set => set.SaveMacKekSource), - new KeyValue("save_mac_sd_card_kek_source", 0x10, 110, set => set.SaveMacSdCardKekSource), - new KeyValue("save_mac_key_source", 0x10, 110, set => set.SaveMacKeySource), - new KeyValue("save_mac_sd_card_key_source", 0x10, 110, set => set.SaveMacSdCardKeySource), - new KeyValue("sd_card_kek_source", 0x10, 110, set => set.SdCardKekSource), - new KeyValue("sd_card_save_key_source", 0x20, 110, set => set.SdCardKeySources[0]), - new KeyValue("sd_card_nca_key_source", 0x20, 110, set => set.SdCardKeySources[1]), - new KeyValue("sd_card_custom_storage_key_source", 0x20, 110, set => set.SdCardKeySources[2]), - - new KeyValue("eticket_rsa_kek", 0x10, 120, set => set.EticketRsaKek), - new KeyValue("ssl_rsa_kek", 0x10, 120, set => set.SslRsaKek), - new KeyValue("xci_header_key", 0x10, 130, set => set.XciHeaderKey), - - new KeyValue("header_key", 0x20, 220, set => set.HeaderKey) - }; - - for (int slot = 0; slot < 0x20; slot++) - { - int i = slot; - keys.Add(new KeyValue($"keyblob_key_source_{i:x2}", 0x10, 0, set => set.KeyblobKeySources[i])); - keys.Add(new KeyValue($"keyblob_{i:x2}", 0x90, 10, set => set.Keyblobs[i])); - keys.Add(new KeyValue($"tsec_root_key_{i:x2}", 0x10, 20, set => set.TsecRootKeys[i])); - keys.Add(new KeyValue($"master_kek_source_{i:x2}", 0x10, 30, set => set.MasterKekSources[i])); - keys.Add(new KeyValue($"mariko_master_kek_source_{i:x2}", 0x10, 35, set => set.MarikoMasterKekSources[i])); - keys.Add(new KeyValue($"master_kek_{i:x2}", 0x10, 40, set => set.MasterKeks[i])); - keys.Add(new KeyValue($"package1_key_{i:x2}", 0x10, 50, set => set.Package1Keys[i])); - - keys.Add(new KeyValue($"master_key_{i:x2}", 0x10, 200, set => set.MasterKeys[i])); - keys.Add(new KeyValue($"package2_key_{i:x2}", 0x10, 210, set => set.Package2Keys[i])); - keys.Add(new KeyValue($"titlekek_{i:x2}", 0x10, 230, set => set.TitleKeks[i])); - keys.Add(new KeyValue($"key_area_key_application_{i:x2}", 0x10, 240, set => set.KeyAreaKeys[i][0])); - keys.Add(new KeyValue($"key_area_key_ocean_{i:x2}", 0x10, 250, set => set.KeyAreaKeys[i][1])); - keys.Add(new KeyValue($"key_area_key_system_{i:x2}", 0x10, 260, set => set.KeyAreaKeys[i][2])); - } - - for (int slot = 0; slot < 4; slot++) - { - int i = slot; - keys.Add(new KeyValue($"bis_key_source_{i:x2}", 0x20, 80, set => set.BisKeySource[i])); - } - - return keys; - } - - private static List CreateUniqueKeyList() - { - var keys = new List - { - new KeyValue("secure_boot_key", 0x10, 0, set => set.SecureBootKey), - new KeyValue("tsec_key", 0x10, 0, set => set.TsecKey), - new KeyValue("sd_seed", 0x10, 10, set => set.SdSeed), - - new KeyValue("device_key", 0x10, 40, set => set.DeviceKey), - new KeyValue("save_mac_key", 0x10, 60, set => set.SaveMacKey), - new KeyValue("save_mac_sd_card_key", 0x10, 60, set => set.SaveMacSdCardKey) - }; - - for (int slot = 0; slot < 0x20; slot++) - { - int i = slot; - keys.Add(new KeyValue($"keyblob_mac_key_{i:x2}", 0x10, 20, set => set.KeyblobMacKeys[i])); - keys.Add(new KeyValue($"keyblob_key_{i:x2}", 0x10, 30, set => set.KeyblobKeys[i])); - keys.Add(new KeyValue($"encrypted_keyblob_{i:x2}", 0xB0, 100, set => set.EncryptedKeyblobs[i])); - } - - for (int slot = 0; slot < 4; slot++) - { - int i = slot; - keys.Add(new KeyValue($"bis_key_{i:x2}", 0x20, 50, set => set.BisKeys[i])); - } - - return keys; - } - - public class KeyValue - { - public readonly string Name; - public readonly int Size; - public readonly int Group; - public readonly Func GetKey; - - public KeyValue(string name, int size, int group, Func retrieveFunc) - { - Name = name; - Size = size; - Group = group; - GetKey = retrieveFunc; - } - } - } - - public enum KeyType - { - None, - Common, - Unique, - Title - } -} diff --git a/src/LibHac/Npdm/Acid.cs b/src/LibHac/Npdm/Acid.cs index f332781b..e62d31b3 100644 --- a/src/LibHac/Npdm/Acid.cs +++ b/src/LibHac/Npdm/Acid.cs @@ -1,6 +1,7 @@ // ReSharper disable UnusedVariable using System; using System.IO; +using LibHac.Common.Keys; namespace LibHac.Npdm { @@ -23,7 +24,7 @@ namespace LibHac.Npdm public Acid(Stream stream, int offset) : this(stream, offset, null) { } - public Acid(Stream stream, int offset, Keyset keyset) + public Acid(Stream stream, int offset, KeySet keySet) { stream.Seek(offset, SeekOrigin.Begin); @@ -40,11 +41,12 @@ namespace LibHac.Npdm Size = reader.ReadInt32(); - if (keyset != null) + if (keySet != null) { reader.BaseStream.Position = offset + 0x100; byte[] signatureData = reader.ReadBytes(Size); - SignatureValidity = CryptoOld.Rsa2048PssVerify(signatureData, Rsa2048Signature, keyset.AcidFixedKeyModulus); + SignatureValidity = + CryptoOld.Rsa2048PssVerify(signatureData, Rsa2048Signature, keySet.AcidSigningKeys[0].Modulus); } reader.BaseStream.Position = offset + 0x208; diff --git a/src/LibHac/Npdm/NpdmBinary.cs b/src/LibHac/Npdm/NpdmBinary.cs index 941ec422..ef298f53 100644 --- a/src/LibHac/Npdm/NpdmBinary.cs +++ b/src/LibHac/Npdm/NpdmBinary.cs @@ -2,6 +2,7 @@ using System; using System.Buffers.Binary; using System.IO; +using LibHac.Common.Keys; namespace LibHac.Npdm { @@ -27,7 +28,7 @@ namespace LibHac.Npdm public NpdmBinary(Stream stream) : this(stream, null) { } - public NpdmBinary(Stream stream, Keyset keyset) + public NpdmBinary(Stream stream, KeySet keySet) { var reader = new BinaryReader(stream); @@ -74,7 +75,7 @@ namespace LibHac.Npdm int acidSize = reader.ReadInt32(); Aci0 = new Aci0(stream, aci0Offset); - AciD = new Acid(stream, acidOffset, keyset); + AciD = new Acid(stream, acidOffset, keySet); } } } diff --git a/src/LibHac/Package1.cs b/src/LibHac/Package1.cs index 94c9a34c..6cc0c4df 100644 --- a/src/LibHac/Package1.cs +++ b/src/LibHac/Package1.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.FsSystem; @@ -20,7 +21,7 @@ namespace LibHac private IStorage Storage { get; } - public Package1(Keyset keyset, IStorage storage) + public Package1(KeySet keySet, IStorage storage) { Storage = storage; var reader = new BinaryReader(storage.AsStream()); @@ -41,7 +42,7 @@ namespace LibHac for (int i = 0; i < 0x20; i++) { - var dec = new Aes128CtrStorage(encStorage, keyset.Package1Keys[i], Counter, true); + var dec = new Aes128CtrStorage(encStorage, keySet.Package1Keys[i].DataRo.ToArray(), Counter, true); dec.Read(0, decBuffer).ThrowIfFailure(); if (BitConverter.ToUInt32(decBuffer, 0) == Pk11Magic) diff --git a/src/LibHac/Package2.cs b/src/LibHac/Package2.cs index e94149c6..6b2eda82 100644 --- a/src/LibHac/Package2.cs +++ b/src/LibHac/Package2.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.FsSystem; @@ -18,15 +19,15 @@ namespace LibHac private IStorage Storage { get; } - public Package2(Keyset keyset, IStorage storage) + public Package2(KeySet keySet, IStorage storage) { Storage = storage; IStorage headerStorage = Storage.Slice(0, 0x200); - KeyRevision = FindKeyGeneration(keyset, headerStorage); - Key = keyset.Package2Keys[KeyRevision]; + KeyRevision = FindKeyGeneration(keySet, headerStorage); + Key = keySet.Package2Keys[KeyRevision].DataRo.ToArray(); - Header = new Package2Header(headerStorage, keyset, KeyRevision); + Header = new Package2Header(headerStorage, keySet, KeyRevision); PackageSize = BitConverter.ToInt32(Header.Counter, 0) ^ BitConverter.ToInt32(Header.Counter, 8) ^ BitConverter.ToInt32(Header.Counter, 12); @@ -106,7 +107,7 @@ namespace LibHac return new CachedStorage(new Aes128CtrStorage(encStorage, Key, Header.SectionCounters[1], true), 0x4000, 4, true); } - private int FindKeyGeneration(Keyset keyset, IStorage storage) + private int FindKeyGeneration(KeySet keySet, IStorage storage) { var counter = new byte[0x10]; var decBuffer = new byte[0x10]; @@ -115,7 +116,8 @@ namespace LibHac for (int i = 0; i < 0x20; i++) { - var dec = new Aes128CtrStorage(storage.Slice(0x100), keyset.Package2Keys[i], counter, false); + var dec = new Aes128CtrStorage(storage.Slice(0x100), keySet.Package2Keys[i].DataRo.ToArray(), counter, + false); dec.Read(0x50, decBuffer).ThrowIfFailure(); if (BitConverter.ToUInt32(decBuffer, 0) == Pk21Magic) @@ -145,14 +147,14 @@ namespace LibHac public Validity SignatureValidity { get; } - public Package2Header(IStorage storage, Keyset keyset, int keyGeneration) + public Package2Header(IStorage storage, KeySet keySet, int keyGeneration) { var reader = new BinaryReader(storage.AsStream()); - byte[] key = keyset.Package2Keys[keyGeneration]; + byte[] key = keySet.Package2Keys[keyGeneration].DataRo.ToArray(); Signature = reader.ReadBytes(0x100); byte[] sigData = reader.ReadBytes(0x100); - SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keyset.Package2FixedKeyModulus); + SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keySet.Package2SigningKey.Modulus); reader.BaseStream.Position -= 0x100; Counter = reader.ReadBytes(0x10); diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index 4361002e..ed52dda7 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -16,7 +17,7 @@ namespace LibHac { public class SwitchFs : IDisposable { - public Keyset Keyset { get; } + public KeySet KeySet { get; } public IFileSystem ContentFs { get; } public IFileSystem SaveFs { get; } @@ -25,9 +26,9 @@ namespace LibHac public Dictionary Titles { get; } = new Dictionary(); public Dictionary Applications { get; } = new Dictionary(); - public SwitchFs(Keyset keyset, IFileSystem contentFileSystem, IFileSystem saveFileSystem) + public SwitchFs(KeySet keySet, IFileSystem contentFileSystem, IFileSystem saveFileSystem) { - Keyset = keyset; + KeySet = keySet; ContentFs = contentFileSystem; SaveFs = saveFileSystem; @@ -38,7 +39,7 @@ namespace LibHac CreateApplications(); } - public static SwitchFs OpenSdCard(Keyset keyset, IAttributeFileSystem fileSystem) + public static SwitchFs OpenSdCard(KeySet keySet, IAttributeFileSystem fileSystem) { var concatFs = new ConcatenationFileSystem(fileSystem); SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem contentDirFs, concatFs, "/Nintendo/Contents".ToU8String()).ThrowIfFailure(); @@ -47,15 +48,15 @@ namespace LibHac if (fileSystem.DirectoryExists("/Nintendo/save")) { SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem saveDirFs, concatFs, "/Nintendo/save".ToU8String()).ThrowIfFailure(); - encSaveFs = new AesXtsFileSystem(saveDirFs, keyset.SdCardKeys[0], 0x4000); + encSaveFs = new AesXtsFileSystem(saveDirFs, keySet.SdCardEncryptionKeys[0].DataRo.ToArray(), 0x4000); } - var encContentFs = new AesXtsFileSystem(contentDirFs, keyset.SdCardKeys[1], 0x4000); + var encContentFs = new AesXtsFileSystem(contentDirFs, keySet.SdCardEncryptionKeys[1].DataRo.ToArray(), 0x4000); - return new SwitchFs(keyset, encContentFs, encSaveFs); + return new SwitchFs(keySet, encContentFs, encSaveFs); } - public static SwitchFs OpenNandPartition(Keyset keyset, IAttributeFileSystem fileSystem) + public static SwitchFs OpenNandPartition(KeySet keySet, IAttributeFileSystem fileSystem) { var concatFs = new ConcatenationFileSystem(fileSystem); SubdirectoryFileSystem saveDirFs = null; @@ -67,12 +68,12 @@ namespace LibHac SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem contentDirFs, concatFs, "/Contents".ToU8String()).ThrowIfFailure(); - return new SwitchFs(keyset, contentDirFs, saveDirFs); + return new SwitchFs(keySet, contentDirFs, saveDirFs); } - public static SwitchFs OpenNcaDirectory(Keyset keyset, IFileSystem fileSystem) + public static SwitchFs OpenNcaDirectory(KeySet keySet, IFileSystem fileSystem) { - return new SwitchFs(keyset, fileSystem, null); + return new SwitchFs(keySet, fileSystem, null); } private void OpenAllNcas() @@ -88,7 +89,7 @@ namespace LibHac { ContentFs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nca = new SwitchFsNca(new Nca(Keyset, ncaFile.AsStorage())); + nca = new SwitchFsNca(new Nca(KeySet, ncaFile.AsStorage())); nca.NcaId = GetNcaFilename(fileEntry.Name, nca); string extension = nca.Nca.Header.ContentType == NcaContentType.Meta ? ".cnmt.nca" : ".nca"; @@ -126,7 +127,7 @@ namespace LibHac { SaveFs.OpenFile(out IFile file, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - save = new SaveDataFileSystem(Keyset, file.AsStorage(), IntegrityCheckLevel.None, true); + save = new SaveDataFileSystem(KeySet, file.AsStorage(), IntegrityCheckLevel.None, true); } catch (Exception ex) { diff --git a/src/LibHac/Ticket.cs b/src/LibHac/Ticket.cs index 9bf27664..6b328197 100644 --- a/src/LibHac/Ticket.cs +++ b/src/LibHac/Ticket.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Common.Keys; namespace LibHac { @@ -144,7 +145,7 @@ namespace LibHac return stream.ToArray(); } - public byte[] GetTitleKey(Keyset keyset) + public byte[] GetTitleKey(KeySet keySet) { if (TitleKeyType == TitleKeyType.Common) { @@ -153,7 +154,7 @@ namespace LibHac return commonKey; } - return CryptoOld.DecryptRsaOaep(TitleKeyBlock, keyset.EticketExtKeyRsa); + return CryptoOld.DecryptRsaOaep(TitleKeyBlock, keySet.ETicketExtKeyRsa); } } diff --git a/src/LibHac/Xci.cs b/src/LibHac/Xci.cs index 66eeab96..f0e0375e 100644 --- a/src/LibHac/Xci.cs +++ b/src/LibHac/Xci.cs @@ -1,4 +1,5 @@ using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -13,10 +14,10 @@ namespace LibHac private object InitLocker { get; } = new object(); private XciPartition RootPartition { get; set; } - public Xci(Keyset keyset, IStorage storage) + public Xci(KeySet keySet, IStorage storage) { BaseStorage = storage; - Header = new XciHeader(keyset, storage.AsStream()); + Header = new XciHeader(keySet, storage.AsStream()); } public bool HasPartition(XciPartitionType type) diff --git a/src/LibHac/XciHeader.cs b/src/LibHac/XciHeader.cs index 7fa5fb4c..b893c375 100644 --- a/src/LibHac/XciHeader.cs +++ b/src/LibHac/XciHeader.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text; +using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; @@ -69,7 +70,7 @@ namespace LibHac public Validity SignatureValidity { get; set; } public Validity PartitionFsHeaderValidity { get; set; } - public XciHeader(Keyset keyset, Stream stream) + public XciHeader(KeySet keySet, Stream stream) { using (var reader = new BinaryReader(stream, Encoding.Default, true)) { @@ -107,11 +108,11 @@ namespace LibHac SelKey = reader.ReadInt32(); LimAreaPage = reader.ReadInt32(); - if (keyset != null && !keyset.XciHeaderKey.IsEmpty()) + if (keySet != null && !keySet.XciHeaderKey.IsEmpty()) { byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize); var decHeader = new byte[EncryptedHeaderSize]; - Aes.DecryptCbc128(encHeader, decHeader, keyset.XciHeaderKey, AesCbcIv); + Aes.DecryptCbc128(encHeader, decHeader, keySet.XciHeaderKey, AesCbcIv); using (var decreader = new BinaryReader(new MemoryStream(decHeader))) { diff --git a/src/hactoolnet/Options.cs b/src/hactoolnet/Options.cs index 0791c14b..bd73f28a 100644 --- a/src/hactoolnet/Options.cs +++ b/src/hactoolnet/Options.cs @@ -1,4 +1,5 @@ using LibHac; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.FsSystem; @@ -69,6 +70,8 @@ namespace hactoolnet return IntegrityCheckLevel.None; } } + + public KeySet.Mode KeyMode => UseDevKeys ? KeySet.Mode.Dev : KeySet.Mode.Prod; } internal enum FileType @@ -95,7 +98,7 @@ namespace hactoolnet internal class Context { public Options Options; - public Keyset Keyset; + public KeySet KeySet; public ProgressBar Logger; public FileSystemClient FsClient; } diff --git a/src/hactoolnet/ProcessDelta.cs b/src/hactoolnet/ProcessDelta.cs index 00f6da1b..791e5427 100644 --- a/src/hactoolnet/ProcessDelta.cs +++ b/src/hactoolnet/ProcessDelta.cs @@ -28,7 +28,7 @@ namespace hactoolnet { try { - var nca = new Nca(ctx.Keyset, deltaStorage); + var nca = new Nca(ctx.KeySet, deltaStorage); IFileSystem fs = nca.OpenFileSystem(0, IntegrityCheckLevel.ErrorOnInvalid); if (!fs.FileExists(FragmentFileName)) diff --git a/src/hactoolnet/ProcessNax0.cs b/src/hactoolnet/ProcessNax0.cs index 4ddc7055..5507fe85 100644 --- a/src/hactoolnet/ProcessNax0.cs +++ b/src/hactoolnet/ProcessNax0.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using LibHac; using LibHac.Common; +using LibHac.Crypto; using LibHac.Fs; using LibHac.FsSystem; using static hactoolnet.Print; @@ -13,7 +14,13 @@ namespace hactoolnet { public static void Process(Context ctx) { - byte[][] keys = ctx.Keyset.SdCardKeys; + if (ctx.Options.SdPath == null) + { + ctx.Logger.LogMessage("SD path must be specified."); + return; + } + + Span keys = ctx.KeySet.SdCardEncryptionKeys; using var baseFile = new LocalFile(ctx.Options.InFile, OpenMode.Read); @@ -22,8 +29,8 @@ namespace hactoolnet for (int i = 0; i < keys.Length; i++) { - byte[] kekSource = keys[i].AsSpan(0, 0x10).ToArray(); - byte[] validationKey = keys[i].AsSpan(0x10, 0x10).ToArray(); + byte[] kekSource = keys[i].SubKeys[0].DataRo.ToArray(); + byte[] validationKey = keys[i].SubKeys[1].DataRo.ToArray(); try { @@ -37,7 +44,7 @@ namespace hactoolnet if (xtsFile == null) { - ctx.Logger.LogMessage($"Error: NAX0 key derivation failed. Check SD card seed and relative path."); + ctx.Logger.LogMessage("Error: NAX0 key derivation failed. Check SD card seed and relative path."); return; } diff --git a/src/hactoolnet/ProcessNca.cs b/src/hactoolnet/ProcessNca.cs index d5530a23..e3ba8ffb 100644 --- a/src/hactoolnet/ProcessNca.cs +++ b/src/hactoolnet/ProcessNca.cs @@ -17,7 +17,7 @@ namespace hactoolnet { using (IStorage file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { - var nca = new Nca(ctx.Keyset, file); + var nca = new Nca(ctx.KeySet, file); Nca baseNca = null; var ncaHolder = new NcaHolder { Nca = nca }; @@ -33,7 +33,7 @@ namespace hactoolnet if (ctx.Options.BaseNca != null) { IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read); - baseNca = new Nca(ctx.Keyset, baseFile); + baseNca = new Nca(ctx.KeySet, baseFile); } for (int i = 0; i < 3; i++) @@ -223,10 +223,17 @@ namespace hactoolnet return nca.Header.VerifySignature2(npdm.AciD.Rsa2048Modulus); } + public static int GetMasterKeyRevisionFromKeyGeneration(int keyGeneration) + { + if (keyGeneration == 0) return 0; + + return keyGeneration - 1; + } + private static string Print(this NcaHolder ncaHolder) { Nca nca = ncaHolder.Nca; - int masterKey = Keyset.GetMasterKeyRevisionFromKeyGeneration(nca.Header.KeyGeneration); + int masterKey = GetMasterKeyRevisionFromKeyGeneration(nca.Header.KeyGeneration); int colLen = 36; var sb = new StringBuilder(); diff --git a/src/hactoolnet/ProcessPackage.cs b/src/hactoolnet/ProcessPackage.cs index 40e08814..5e54b286 100644 --- a/src/hactoolnet/ProcessPackage.cs +++ b/src/hactoolnet/ProcessPackage.cs @@ -17,7 +17,7 @@ namespace hactoolnet using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { var package1 = new LibHac.Boot.Package1(); - package1.Initialize(ctx.Keyset, file).ThrowIfFailure(); + package1.Initialize(ctx.KeySet, file).ThrowIfFailure(); ctx.Logger.LogMessage(package1.Print()); @@ -107,7 +107,7 @@ namespace hactoolnet using (var file = new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)) { var package2 = new Package2StorageReader(); - package2.Initialize(ctx.Keyset, file).ThrowIfFailure(); + package2.Initialize(ctx.KeySet, file).ThrowIfFailure(); ctx.Logger.LogMessage(package2.Print()); diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs index 0ea5e29c..f717d56a 100644 --- a/src/hactoolnet/ProcessSave.cs +++ b/src/hactoolnet/ProcessSave.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using LibHac; using LibHac.Common; +using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -29,7 +30,7 @@ namespace hactoolnet { bool signNeeded = ctx.Options.SignSave; - var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true); + var save = new SaveDataFileSystem(ctx.KeySet, file, ctx.Options.IntegrityLevel, true); FileSystemClient fs = ctx.FsClient; fs.Register("save".ToU8Span(), save); @@ -104,9 +105,10 @@ namespace hactoolnet { if (signNeeded) { - if (save.Commit(ctx.Keyset).IsSuccess()) + if (save.Commit(ctx.KeySet).IsSuccess()) { - ctx.Logger.LogMessage($"Successfully signed save file with key {ctx.Keyset.SaveMacKey.ToHexString()}"); + ctx.Logger.LogMessage( + $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}"); } else { @@ -126,9 +128,10 @@ namespace hactoolnet if (signNeeded) { - if (save.Commit(ctx.Keyset).IsSuccess()) + if (save.Commit(ctx.KeySet).IsSuccess()) { - ctx.Logger.LogMessage($"Successfully signed save file with key {ctx.Keyset.SaveMacKey.ToHexString()}"); + ctx.Logger.LogMessage( + $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}"); } else { @@ -147,7 +150,7 @@ namespace hactoolnet } } - ctx.Logger.LogMessage(save.Print(ctx.Keyset)); + ctx.Logger.LogMessage(save.Print(ctx.KeySet)); //ctx.Logger.LogMessage(PrintFatLayout(save.SaveDataFileSystemCore)); fs.Unmount("save".ToU8Span()); @@ -316,7 +319,7 @@ namespace hactoolnet } } - private static string Print(this SaveDataFileSystem save, Keyset keyset) + private static string Print(this SaveDataFileSystem save, KeySet keySet) { int colLen = 25; var sb = new StringBuilder(); @@ -325,7 +328,7 @@ namespace hactoolnet save.GetFreeSpaceSize(out long freeSpace, "".ToU8String()).ThrowIfFailure(); sb.AppendLine("Savefile:"); - PrintItem(sb, colLen, "CMAC Key Used:", keyset.SaveMacKey); + PrintItem(sb, colLen, "CMAC Key Used:", keySet.DeviceUniqueSaveMacKeys[0].DataRo.ToArray()); PrintItem(sb, colLen, $"CMAC Signature{save.Header.SignatureValidity.GetValidityString()}:", save.Header.Cmac); PrintItem(sb, colLen, "Title ID:", $"{save.Header.ExtraData.TitleId:x16}"); PrintItem(sb, colLen, "User ID:", save.Header.ExtraData.UserId); diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs index b3926b05..fbe9c50b 100644 --- a/src/hactoolnet/ProcessSwitchFs.cs +++ b/src/hactoolnet/ProcessSwitchFs.cs @@ -24,21 +24,21 @@ namespace hactoolnet if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as SD card storage"); - switchFs = SwitchFs.OpenSdCard(ctx.Keyset, baseFs); + switchFs = SwitchFs.OpenSdCard(ctx.KeySet, baseFs); CheckForNcaFolders(ctx, switchFs); } else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as NAND storage"); - switchFs = SwitchFs.OpenNandPartition(ctx.Keyset, baseFs); + switchFs = SwitchFs.OpenNandPartition(ctx.KeySet, baseFs); CheckForNcaFolders(ctx, switchFs); } else { ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); - switchFs = SwitchFs.OpenNcaDirectory(ctx.Keyset, baseFs); + switchFs = SwitchFs.OpenNcaDirectory(ctx.KeySet, baseFs); } if (ctx.Options.ListNcas) diff --git a/src/hactoolnet/ProcessXci.cs b/src/hactoolnet/ProcessXci.cs index 10d61aa7..8b1c7807 100644 --- a/src/hactoolnet/ProcessXci.cs +++ b/src/hactoolnet/ProcessXci.cs @@ -15,7 +15,7 @@ namespace hactoolnet { using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { - var xci = new Xci(ctx.Keyset, file); + var xci = new Xci(ctx.KeySet, file); ctx.Logger.LogMessage(xci.Print()); @@ -125,7 +125,7 @@ namespace hactoolnet foreach (PartitionFileEntry fileEntry in partition.Files.Where(x => x.Name.EndsWith(".nca"))) { IStorage ncaStorage = partition.OpenFile(fileEntry, OpenMode.Read).AsStorage(); - var nca = new Nca(ctx.Keyset, ncaStorage); + var nca = new Nca(ctx.KeySet, ncaStorage); if (nca.Header.ContentType == NcaContentType.Program) { diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 0302c5b3..f33f67bb 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Text; using LibHac; +using LibHac.Common.Keys; using LibHac.Fs; namespace hactoolnet @@ -197,26 +198,34 @@ namespace hactoolnet consoleKeyFile = homeConsoleKeyFile; } - ctx.Keyset = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger, ctx.Options.UseDevKeys); + ctx.KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger, ctx.Options.KeyMode); + if (ctx.Options.SdSeed != null) { - ctx.Keyset.SetSdSeed(ctx.Options.SdSeed.ToBytes()); - } - - if (ctx.Options.InFileType == FileType.Keygen && ctx.Options.OutDir != null) - { - string dir = ctx.Options.OutDir; - Directory.CreateDirectory(dir); - - File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyReader.PrintCommonKeys(ctx.Keyset)); - File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyReader.PrintUniqueKeys(ctx.Keyset)); - File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyReader.PrintTitleKeys(ctx.Keyset)); + ctx.KeySet.SetSdSeed(ctx.Options.SdSeed.ToBytes()); } } private static void ProcessKeygen(Context ctx) { - Console.WriteLine(ExternalKeyReader.PrintCommonKeys(ctx.Keyset)); + Console.WriteLine(ExternalKeyReader.PrintAllKeys(ctx.KeySet)); + + if (ctx.Options.OutDir != null) + { + string keyFileName = ctx.Options.UseDevKeys ? "dev.keys" : "prod.keys"; + string dir = ctx.Options.OutDir; + Directory.CreateDirectory(dir); + + File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyReader.PrintCommonKeys(ctx.KeySet)); + File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyReader.PrintDeviceKeys(ctx.KeySet)); + File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyReader.PrintTitleKeys(ctx.KeySet)); + + if (!ctx.Options.UseDevKeys) + { + File.WriteAllText(Path.Combine(dir, "prod+dev.keys"), + ExternalKeyReader.PrintCommonKeysWithDev(ctx.KeySet)); + } + } } // For running random stuff diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs index c5f80d91..132e5ada 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs @@ -1,4 +1,5 @@ -using LibHac.Fs; +using LibHac.Common.Keys; +using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSrv; @@ -10,7 +11,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests { rootFs = new InMemoryFileSystem(); - var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset()); + var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new KeySet()); defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted); diff --git a/tests/LibHac.Tests/HorizonFactory.cs b/tests/LibHac.Tests/HorizonFactory.cs index a580ecb8..cdfbca8d 100644 --- a/tests/LibHac.Tests/HorizonFactory.cs +++ b/tests/LibHac.Tests/HorizonFactory.cs @@ -1,4 +1,5 @@ -using LibHac.Fs; +using LibHac.Common.Keys; +using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSrv; @@ -10,7 +11,7 @@ namespace LibHac.Tests { IFileSystem rootFs = new InMemoryFileSystem(); - var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset()); + var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new KeySet()); var config = new FileSystemServerConfig(); config.FsCreators = defaultObjects.FsCreators;