mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Remove the old Keyset class
This commit is contained in:
parent
e3bb1f1fac
commit
770406e9c2
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Package2StorageReader"/>.
|
||||
/// </summary>
|
||||
/// <param name="keyset">The keyset to use for decrypting the package.</param>
|
||||
/// <param name="keySet">The keyset to use for decrypting the package.</param>
|
||||
/// <param name="storage">An <see cref="IStorage"/> of the encrypted package2.</param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
||||
{
|
||||
Buffer16 iv = source.HeaderIv;
|
||||
|
||||
|
@ -101,12 +101,16 @@ namespace LibHac.Common.Keys
|
||||
public Span<AesKey> 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<AesXtsKey> SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items;
|
||||
|
||||
private RsaSigningKeyParameters _rsaSigningKeyParamsDev;
|
||||
private RsaSigningKeyParameters _rsaSigningKeyParamsProd;
|
||||
private RsaKeyParameters _rsaKeyParams;
|
||||
|
||||
public RSAParameters ETicketExtKeyRsa { get; set; }
|
||||
|
||||
public Span<RSAParameters> NcaHeaderSigningKeys
|
||||
{
|
||||
get
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<IStorage>();
|
||||
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<byte> 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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<byte> 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
10
src/LibHac/KeyType.cs
Normal file
10
src/LibHac/KeyType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace LibHac
|
||||
{
|
||||
public enum KeyType
|
||||
{
|
||||
None,
|
||||
Common,
|
||||
Unique,
|
||||
Title
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of keyblobs that were used for < 6.2.0 crypto
|
||||
/// </summary>
|
||||
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<byte> key, ReadOnlySpan<byte> src, Span<byte> dest, ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> keySeed)
|
||||
{
|
||||
Span<byte> kek = stackalloc byte[0x10];
|
||||
Span<byte> 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<string, KeyValue> 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<string, KeyValue> 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<string, KeyValue> 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<string, KeyValue> 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<string, KeyValue> CreateCommonKeyDictionary()
|
||||
{
|
||||
return CreateCommonKeyList().ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static Dictionary<string, KeyValue> CreateUniqueKeyDictionary()
|
||||
{
|
||||
return CreateUniqueKeyList().ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static Dictionary<string, KeyValue> CreateFullKeyDictionary()
|
||||
{
|
||||
List<KeyValue> commonKeys = CreateCommonKeyList();
|
||||
List<KeyValue> uniqueKeys = CreateUniqueKeyList();
|
||||
|
||||
return uniqueKeys.Concat(commonKeys).ToDictionary(k => k.Name, k => k, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static List<KeyValue> CreateCommonKeyList()
|
||||
{
|
||||
var keys = new List<KeyValue>
|
||||
{
|
||||
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<KeyValue> CreateUniqueKeyList()
|
||||
{
|
||||
var keys = new List<KeyValue>
|
||||
{
|
||||
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<Keyset, byte[]> GetKey;
|
||||
|
||||
public KeyValue(string name, int size, int group, Func<Keyset, byte[]> retrieveFunc)
|
||||
{
|
||||
Name = name;
|
||||
Size = size;
|
||||
Group = group;
|
||||
GetKey = retrieveFunc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum KeyType
|
||||
{
|
||||
None,
|
||||
Common,
|
||||
Unique,
|
||||
Title
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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<ulong, Title> Titles { get; } = new Dictionary<ulong, Title>();
|
||||
public Dictionary<ulong, Application> Applications { get; } = new Dictionary<ulong, Application>();
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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<AesXtsKey> 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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user