Remove the old Keyset class

This commit is contained in:
Alex Barney 2020-10-05 12:25:39 -07:00
parent e3bb1f1fac
commit 770406e9c2
33 changed files with 224 additions and 1040 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -0,0 +1,10 @@
namespace LibHac
{
public enum KeyType
{
None,
Common,
Unique,
Title
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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