Implement function in NCA class to re-encrypt decrypted NCAs

This commit is contained in:
Xpl0itR 2020-07-16 19:31:57 +01:00 committed by Alex Barney
parent c56c8ba8ed
commit e0ddf17945
2 changed files with 31 additions and 18 deletions

View file

@ -1,4 +1,4 @@
using System; using System;
using LibHac.Fs; using LibHac.Fs;
namespace LibHac.FsSystem namespace LibHac.FsSystem
@ -9,24 +9,27 @@ namespace LibHac.FsSystem
private readonly byte[] _tempBuffer; private readonly byte[] _tempBuffer;
private Aes128XtsTransform _decryptor; private Aes128XtsTransform _readTransform;
private Aes128XtsTransform _encryptor; private Aes128XtsTransform _writeTransform;
private readonly byte[] _key1; private readonly byte[] _key1;
private readonly byte[] _key2; private readonly byte[] _key2;
public Aes128XtsStorage(IStorage baseStorage, Span<byte> key, int sectorSize, bool leaveOpen) private readonly bool _decryptRead;
public Aes128XtsStorage(IStorage baseStorage, Span<byte> key, int sectorSize, bool leaveOpen, bool decryptRead = true)
: base(baseStorage, sectorSize, leaveOpen) : base(baseStorage, sectorSize, leaveOpen)
{ {
if (key == null) throw new NullReferenceException(nameof(key)); 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"); if (key.Length != BlockSize * 2) throw new ArgumentException(nameof(key), $"Key must be {BlockSize * 2} bytes long");
_tempBuffer = new byte[sectorSize]; _tempBuffer = new byte[sectorSize];
_decryptRead = decryptRead;
_key1 = key.Slice(0, BlockSize).ToArray(); _key1 = key.Slice(0, BlockSize).ToArray();
_key2 = key.Slice(BlockSize, BlockSize).ToArray(); _key2 = key.Slice(BlockSize, BlockSize).ToArray();
} }
public Aes128XtsStorage(IStorage baseStorage, Span<byte> key1, Span<byte> key2, int sectorSize, bool leaveOpen) public Aes128XtsStorage(IStorage baseStorage, Span<byte> key1, Span<byte> key2, int sectorSize, bool leaveOpen, bool decryptRead = true)
: base(baseStorage, sectorSize, leaveOpen) : base(baseStorage, sectorSize, leaveOpen)
{ {
if (key1 == null) throw new NullReferenceException(nameof(key1)); 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"); if (key1.Length != BlockSize || key1.Length != BlockSize) throw new ArgumentException($"Keys must be {BlockSize} bytes long");
_tempBuffer = new byte[sectorSize]; _tempBuffer = new byte[sectorSize];
_decryptRead = decryptRead;
_key1 = key1.ToArray(); _key1 = key1.ToArray();
_key2 = key2.ToArray(); _key2 = key2.ToArray();
} }
@ -43,12 +47,12 @@ namespace LibHac.FsSystem
int size = destination.Length; int size = destination.Length;
long sectorIndex = offset / SectorSize; 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)); Result rc = base.DoRead(offset, _tempBuffer.AsSpan(0, size));
if (rc.IsFailure()) return rc; 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); _tempBuffer.AsSpan(0, size).CopyTo(destination);
return Result.Success; return Result.Success;
@ -59,10 +63,10 @@ namespace LibHac.FsSystem
int size = source.Length; int size = source.Length;
long sectorIndex = offset / SectorSize; 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); 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)); return base.DoWrite(offset, _tempBuffer.AsSpan(0, size));
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -19,9 +19,13 @@ namespace LibHac.FsSystem.NcaUtils
public Nca(Keyset keyset, IStorage storage) public Nca(Keyset keyset, IStorage storage)
{ {
Span<byte> magic = stackalloc byte[3];
storage.Read(0x200, magic);
bool encrypted = magic[0] != 'N' && magic[1] != 'C' && magic[2] != 'A';
Keyset = keyset; Keyset = keyset;
BaseStorage = storage; BaseStorage = storage;
Header = new NcaHeader(keyset, storage); Header = encrypted ? new NcaHeader(keyset, storage) : new NcaHeader(storage);
} }
public byte[] GetDecryptedKey(int index) public byte[] GetDecryptedKey(int index)
@ -117,7 +121,7 @@ namespace LibHac.FsSystem.NcaUtils
return Header.IsSectionEnabled(index); return Header.IsSectionEnabled(index);
} }
private IStorage OpenEncryptedStorage(int index) private IStorage OpenSectionStorage(int index)
{ {
if (!SectionExists(index)) throw new ArgumentException(nameof(index), Messages.NcaSectionMissing); if (!SectionExists(index)) throw new ArgumentException(nameof(index), Messages.NcaSectionMissing);
@ -215,7 +219,7 @@ namespace LibHac.FsSystem.NcaUtils
public IStorage OpenRawStorage(int index) public IStorage OpenRawStorage(int index)
{ {
IStorage encryptedStorage = OpenEncryptedStorage(index); IStorage encryptedStorage = OpenSectionStorage(index);
IStorage decryptedStorage = OpenDecryptedStorage(encryptedStorage, index); IStorage decryptedStorage = OpenDecryptedStorage(encryptedStorage, index);
return decryptedStorage; return decryptedStorage;
@ -353,10 +357,12 @@ namespace LibHac.FsSystem.NcaUtils
return OpenStorageWithPatch(patchNca, GetSectionIndexFromType(type), integrityCheckLevel); return OpenStorageWithPatch(patchNca, GetSectionIndexFromType(type), integrityCheckLevel);
} }
public IStorage OpenDecryptedNca() public IStorage OpenDecryptedNca() => TransformNca(true);
public IStorage TransformNca(bool decrypting)
{ {
var builder = new ConcatenationStorageBuilder(); var builder = new ConcatenationStorageBuilder();
builder.Add(OpenDecryptedHeaderStorage(), 0); builder.Add(TransformHeaderStorage(decrypting), 0);
for (int i = 0; i < NcaHeader.SectionCount; i++) for (int i = 0; i < NcaHeader.SectionCount; i++)
{ {
@ -499,7 +505,9 @@ namespace LibHac.FsSystem.NcaUtils
return new HierarchicalIntegrityVerificationStorage(initInfo, integrityCheckLevel, leaveOpen); return new HierarchicalIntegrityVerificationStorage(initInfo, integrityCheckLevel, leaveOpen);
} }
public IStorage OpenDecryptedHeaderStorage() public IStorage OpenDecryptedHeaderStorage() => TransformHeaderStorage(true);
public IStorage TransformHeaderStorage(bool decrypting)
{ {
long firstSectionOffset = long.MaxValue; long firstSectionOffset = long.MaxValue;
bool hasEnabledSection = false; 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, decrypting), 1, true);
IStorage header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, headerSize), Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true), 1, true);
int version = ReadHeaderVersion(header); int version = ReadHeaderVersion(header);
if (version == 2) if (version == 2)