diff --git a/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs b/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs index 578ddd35..3ec61e7c 100644 --- a/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs +++ b/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs @@ -17,11 +17,11 @@ namespace LibHac.FsSystem; /// The base data used for this storage comes with a table of ranges and counter values that are used /// to decrypt each range. This encryption scheme is used for encrypting content updates so that no counter values /// are ever reused. -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class AesCtrCounterExtendedStorage : IStorage { - public delegate Result DecryptFunction(Span destination, int index, ReadOnlySpan encryptedKey, - ReadOnlySpan iv, ReadOnlySpan source); + public delegate Result DecryptFunction(Span destination, int index, int generation, + ReadOnlySpan encryptedKey, ReadOnlySpan iv, ReadOnlySpan source); public interface IDecryptor : IDisposable { @@ -32,9 +32,16 @@ public class AesCtrCounterExtendedStorage : IStorage public struct Entry { public Array8 Offset; - public int Reserved; + public Encryption EncryptionValue; + public Array3 Reserved; public int Generation; + public enum Encryption : byte + { + Encrypted = 0, + NotEncrypted = 1 + } + public void SetOffset(long value) { BinaryPrimitives.WriteInt64LittleEndian(Offset.Items, value); @@ -74,9 +81,9 @@ public class AesCtrCounterExtendedStorage : IStorage } public static Result CreateExternalDecryptor(ref UniqueRef outDecryptor, - DecryptFunction decryptFunction, int keyIndex) + DecryptFunction decryptFunction, int keyIndex, int keyGeneration) { - using var decryptor = new UniqueRef(new ExternalDecryptor(decryptFunction, keyIndex)); + using var decryptor = new UniqueRef(new ExternalDecryptor(decryptFunction, keyIndex, keyGeneration)); if (!decryptor.HasValue) return ResultFs.AllocationMemoryFailedInAesCtrCounterExtendedStorageA.Log(); @@ -159,9 +166,18 @@ public class AesCtrCounterExtendedStorage : IStorage Assert.SdkRequiresGreaterEqual(counterOffset, 0); Assert.SdkRequiresNotNull(in decryptor); - Result rc = _table.Initialize(allocator, in nodeStorage, in entryStorage, NodeSize, Unsafe.SizeOf(), - entryCount); - if (rc.IsFailure()) return rc.Miss(); + Result rc; + + if (entryCount > 0) + { + rc = _table.Initialize(allocator, in nodeStorage, in entryStorage, NodeSize, Unsafe.SizeOf(), + entryCount); + if (rc.IsFailure()) return rc.Miss(); + } + else + { + _table.Initialize(NodeSize, 0); + } rc = dataStorage.GetSize(out long dataStorageSize); if (rc.IsFailure()) return rc.Miss(); @@ -279,20 +295,23 @@ public class AesCtrCounterExtendedStorage : IStorage long readSize = Math.Min(remainingSize, dataSize); Assert.SdkLessEqual(readSize, destination.Length); - // Create the counter for the first data block we're decrypting. - long counterOffset = _counterOffset + entryStartOffset + dataOffset; - var upperIv = new NcaAesCtrUpperIv + if (entry.EncryptionValue == Entry.Encryption.Encrypted) { - Generation = (uint)entry.Generation, - SecureValue = _secureValue - }; + // Create the counter for the first data block we're decrypting. + long counterOffset = _counterOffset + entryStartOffset + dataOffset; + var upperIv = new NcaAesCtrUpperIv + { + Generation = (uint)entry.Generation, + SecureValue = _secureValue + }; - Unsafe.SkipInit(out Array16 counter); - AesCtrStorage.MakeIv(counter.Items, upperIv.Value, counterOffset); + Unsafe.SkipInit(out Array16 counter); + AesCtrStorage.MakeIv(counter.Items, upperIv.Value, counterOffset); - // Decrypt the data from the current entry. - rc = _decryptor.Get.Decrypt(currentData.Slice(0, (int)dataSize), _key, counter); - if (rc.IsFailure()) return rc.Miss(); + // Decrypt the data from the current entry. + rc = _decryptor.Get.Decrypt(currentData.Slice(0, (int)dataSize), _key, counter); + if (rc.IsFailure()) return rc.Miss(); + } // Advance the current offsets. currentData = currentData.Slice((int)dataSize); @@ -402,13 +421,15 @@ public class AesCtrCounterExtendedStorage : IStorage { private DecryptFunction _decryptFunction; private int _keyIndex; + private int _keyGeneration; - public ExternalDecryptor(DecryptFunction decryptFunction, int keyIndex) + public ExternalDecryptor(DecryptFunction decryptFunction, int keyIndex, int keyGeneration) { Assert.SdkRequiresNotNull(decryptFunction); _decryptFunction = decryptFunction; _keyIndex = keyIndex; + _keyGeneration = keyGeneration; } public void Dispose() { } @@ -435,7 +456,7 @@ public class AesCtrCounterExtendedStorage : IStorage Span dstBuffer = destination.Slice(currentOffset, currentSize); Span workBuffer = pooledBuffer.GetBuffer().Slice(0, currentSize); - Result rc = _decryptFunction(workBuffer, _keyIndex, encryptedKey, counter, dstBuffer); + Result rc = _decryptFunction(workBuffer, _keyIndex, _keyGeneration, encryptedKey, counter, dstBuffer); if (rc.IsFailure()) return rc.Miss(); workBuffer.CopyTo(dstBuffer);