diff --git a/src/LibHac/FsSystem/AesXtsStorage.cs b/src/LibHac/FsSystem/AesXtsStorage.cs new file mode 100644 index 00000000..229f2161 --- /dev/null +++ b/src/LibHac/FsSystem/AesXtsStorage.cs @@ -0,0 +1,115 @@ +// ReSharper disable NotAccessedField.Local +using System; +using System.Buffers.Binary; +using LibHac.Common; +using LibHac.Common.FixedArrays; +using LibHac.Crypto; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.Os; + +namespace LibHac.FsSystem; + +/// +/// Reads and writes to an that's encrypted with AES-XTS-128. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) +public class AesXtsStorage : IStorage +{ + public static readonly int AesBlockSize = Aes.BlockSize; + public static readonly int KeySize = Aes.KeySize128; + public static readonly int IvSize = Aes.KeySize128; + + private IStorage _baseStorage; + private Array16 _key1; + private Array16 _key2; + private Array16 _iv; + private int _blockSize; + private SdkMutexType _mutex; + + // LibHac addition: This field goes unused if initialized with a plain IStorage. + // The original class uses a template for both the shared and non-shared IStorage which avoids needing this field. + private SharedRef _baseStorageShared; + + public static void MakeAesXtsIv(Span outIv, long offset, int blockSize) + { + Assert.Equal(outIv.Length, IvSize); + Assert.SdkRequiresGreaterEqual(offset, 0); + Assert.SdkRequiresAligned((ulong)blockSize, AesBlockSize); + + BinaryPrimitives.WriteInt64BigEndian(outIv.Slice(sizeof(long)), offset / blockSize); + } + + public AesXtsStorage(IStorage baseStorage, ReadOnlySpan key1, ReadOnlySpan key2, ReadOnlySpan iv, + int blockSize) + { + _baseStorage = baseStorage; + _blockSize = blockSize; + _mutex = new SdkMutexType(); + + Assert.SdkRequiresEqual(KeySize, key1.Length); + Assert.SdkRequiresEqual(KeySize, key2.Length); + Assert.SdkRequiresEqual(IvSize, iv.Length); + Assert.SdkRequiresAligned((ulong)blockSize, AesBlockSize); + + key1.CopyTo(_key1.Items); + key2.CopyTo(_key2.Items); + iv.CopyTo(_iv.Items); + } + + public AesXtsStorage(ref SharedRef baseStorage, ReadOnlySpan key1, ReadOnlySpan key2, + ReadOnlySpan iv, int blockSize) + { + _baseStorageShared = SharedRef.CreateMove(ref baseStorage); + _baseStorage = _baseStorageShared.Get; + _blockSize = blockSize; + _mutex = new SdkMutexType(); + + Assert.SdkRequiresEqual(KeySize, key1.Length); + Assert.SdkRequiresEqual(KeySize, key2.Length); + Assert.SdkRequiresEqual(IvSize, iv.Length); + Assert.SdkRequiresAligned((ulong)blockSize, AesBlockSize); + + key1.CopyTo(_key1.Items); + key2.CopyTo(_key2.Items); + iv.CopyTo(_iv.Items); + } + + public override void Dispose() + { + _baseStorageShared.Destroy(); + + base.Dispose(); + } + + public override Result Read(long offset, Span destination) + { + throw new NotImplementedException(); + } + + public override Result Write(long offset, ReadOnlySpan source) + { + throw new NotImplementedException(); + } + + public override Result Flush() + { + throw new NotImplementedException(); + } + + public override Result GetSize(out long size) + { + throw new NotImplementedException(); + } + + public override Result SetSize(long size) + { + throw new NotImplementedException(); + } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file