From e0ddf17945e42582b916e2b4cca6436ff9c4f220 Mon Sep 17 00:00:00 2001 From: Xpl0itR Date: Thu, 16 Jul 2020 19:31:57 +0100 Subject: [PATCH] Implement function in NCA class to re-encrypt decrypted NCAs --- src/LibHac/FsSystem/Aes128XtsStorage.cs | 22 +++++++++++--------- src/LibHac/FsSystem/NcaUtils/Nca.cs | 27 ++++++++++++++++--------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/LibHac/FsSystem/Aes128XtsStorage.cs b/src/LibHac/FsSystem/Aes128XtsStorage.cs index 858a19f2..ec2621d3 100644 --- a/src/LibHac/FsSystem/Aes128XtsStorage.cs +++ b/src/LibHac/FsSystem/Aes128XtsStorage.cs @@ -1,4 +1,4 @@ -using System; +using System; using LibHac.Fs; namespace LibHac.FsSystem @@ -9,24 +9,27 @@ namespace LibHac.FsSystem private readonly byte[] _tempBuffer; - private Aes128XtsTransform _decryptor; - private Aes128XtsTransform _encryptor; + private Aes128XtsTransform _readTransform; + private Aes128XtsTransform _writeTransform; private readonly byte[] _key1; private readonly byte[] _key2; - public Aes128XtsStorage(IStorage baseStorage, Span key, int sectorSize, bool leaveOpen) + private readonly bool _decryptRead; + + public Aes128XtsStorage(IStorage baseStorage, Span key, int sectorSize, bool leaveOpen, bool decryptRead = true) : base(baseStorage, sectorSize, leaveOpen) { if (key == null) throw new NullReferenceException(nameof(key)); if (key.Length != BlockSize * 2) throw new ArgumentException(nameof(key), $"Key must be {BlockSize * 2} bytes long"); _tempBuffer = new byte[sectorSize]; + _decryptRead = decryptRead; _key1 = key.Slice(0, BlockSize).ToArray(); _key2 = key.Slice(BlockSize, BlockSize).ToArray(); } - public Aes128XtsStorage(IStorage baseStorage, Span key1, Span key2, int sectorSize, bool leaveOpen) + public Aes128XtsStorage(IStorage baseStorage, Span key1, Span key2, int sectorSize, bool leaveOpen, bool decryptRead = true) : base(baseStorage, sectorSize, leaveOpen) { if (key1 == null) throw new NullReferenceException(nameof(key1)); @@ -34,6 +37,7 @@ namespace LibHac.FsSystem if (key1.Length != BlockSize || key1.Length != BlockSize) throw new ArgumentException($"Keys must be {BlockSize} bytes long"); _tempBuffer = new byte[sectorSize]; + _decryptRead = decryptRead; _key1 = key1.ToArray(); _key2 = key2.ToArray(); } @@ -43,12 +47,12 @@ namespace LibHac.FsSystem int size = destination.Length; long sectorIndex = offset / SectorSize; - if (_decryptor == null) _decryptor = new Aes128XtsTransform(_key1, _key2, true); + if (_readTransform == null) _readTransform = new Aes128XtsTransform(_key1, _key2, _decryptRead); Result rc = base.DoRead(offset, _tempBuffer.AsSpan(0, size)); if (rc.IsFailure()) return rc; - _decryptor.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); + _readTransform.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); _tempBuffer.AsSpan(0, size).CopyTo(destination); return Result.Success; @@ -59,10 +63,10 @@ namespace LibHac.FsSystem int size = source.Length; long sectorIndex = offset / SectorSize; - if (_encryptor == null) _encryptor = new Aes128XtsTransform(_key1, _key2, false); + if (_writeTransform == null) _writeTransform = new Aes128XtsTransform(_key1, _key2, !_decryptRead); source.CopyTo(_tempBuffer); - _encryptor.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); + _writeTransform.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); return base.DoWrite(offset, _tempBuffer.AsSpan(0, size)); } diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs index 618cd4dd..be57e516 100644 --- a/src/LibHac/FsSystem/NcaUtils/Nca.cs +++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -19,9 +19,13 @@ namespace LibHac.FsSystem.NcaUtils public Nca(Keyset keyset, IStorage storage) { + Span magic = stackalloc byte[3]; + storage.Read(0x200, magic); + bool encrypted = magic[0] != 'N' && magic[1] != 'C' && magic[2] != 'A'; + Keyset = keyset; BaseStorage = storage; - Header = new NcaHeader(keyset, storage); + Header = encrypted ? new NcaHeader(keyset, storage) : new NcaHeader(storage); } public byte[] GetDecryptedKey(int index) @@ -117,7 +121,7 @@ namespace LibHac.FsSystem.NcaUtils return Header.IsSectionEnabled(index); } - private IStorage OpenEncryptedStorage(int index) + private IStorage OpenSectionStorage(int index) { if (!SectionExists(index)) throw new ArgumentException(nameof(index), Messages.NcaSectionMissing); @@ -215,7 +219,7 @@ namespace LibHac.FsSystem.NcaUtils public IStorage OpenRawStorage(int index) { - IStorage encryptedStorage = OpenEncryptedStorage(index); + IStorage encryptedStorage = OpenSectionStorage(index); IStorage decryptedStorage = OpenDecryptedStorage(encryptedStorage, index); return decryptedStorage; @@ -353,10 +357,12 @@ namespace LibHac.FsSystem.NcaUtils return OpenStorageWithPatch(patchNca, GetSectionIndexFromType(type), integrityCheckLevel); } - public IStorage OpenDecryptedNca() + public IStorage OpenDecryptedNca() => TransformNca(true); + + public IStorage TransformNca(bool decrypting) { var builder = new ConcatenationStorageBuilder(); - builder.Add(OpenDecryptedHeaderStorage(), 0); + builder.Add(TransformHeaderStorage(decrypting), 0); for (int i = 0; i < NcaHeader.SectionCount; i++) { @@ -499,7 +505,9 @@ namespace LibHac.FsSystem.NcaUtils return new HierarchicalIntegrityVerificationStorage(initInfo, integrityCheckLevel, leaveOpen); } - public IStorage OpenDecryptedHeaderStorage() + public IStorage OpenDecryptedHeaderStorage() => TransformHeaderStorage(true); + + public IStorage TransformHeaderStorage(bool decrypting) { long firstSectionOffset = long.MaxValue; bool hasEnabledSection = false; @@ -514,9 +522,10 @@ namespace LibHac.FsSystem.NcaUtils } } - long headerSize = hasEnabledSection ? NcaHeader.HeaderSize : firstSectionOffset; + long headerSize = hasEnabledSection ? firstSectionOffset : NcaHeader.HeaderSize; - IStorage header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, headerSize), Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true), 1, true); + IStorage header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, headerSize), Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true, decrypting), 1, true); + int version = ReadHeaderVersion(header); if (version == 2)