Update AesCtrCounterExtendedStorage for 14.0.0

This commit is contained in:
Alex Barney 2022-04-16 23:37:10 -07:00
parent 398a142b27
commit 2241a7ced3

View file

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