mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add XTS mode and remove duplicate code
This commit is contained in:
parent
df27d2a83b
commit
8b47be19c2
17 changed files with 8923 additions and 150 deletions
68
src/LibHac/Common/Buffer.cs
Normal file
68
src/LibHac/Common/Buffer.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace LibHac.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a buffer of 16 bytes.
|
||||||
|
/// Contains functions that assist with common operations on small buffers.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||||
|
public struct Buffer16
|
||||||
|
{
|
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||||
|
|
||||||
|
public byte this[int i]
|
||||||
|
{
|
||||||
|
get => Bytes[i];
|
||||||
|
set => Bytes[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator Span<byte>(in Buffer16 value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in Buffer16 value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(ref Unsafe.AsRef(in value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref T As<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>())
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Span<T> AsSpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsSpan<Buffer16, T>(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlySpan<Buffer16, T>(ref Unsafe.AsRef(in this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Bytes.ToHexString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,11 +25,51 @@ namespace LibHac.Common
|
||||||
return CreateSpan(ref reference, 1);
|
return CreateSpan(ref reference, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
|
||||||
|
where TStruct : unmanaged where TSpan : unmanaged
|
||||||
|
{
|
||||||
|
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||||
|
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
||||||
{
|
{
|
||||||
Span<T> span = AsSpan(ref reference);
|
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||||
return MemoryMarshal.Cast<T, byte>(span);
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#if NETCOREAPP
|
||||||
|
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
|
||||||
|
{
|
||||||
|
return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
|
||||||
|
{
|
||||||
|
return new ReadOnlySpan<T>(Unsafe.AsPointer(ref reference), length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged
|
||||||
|
{
|
||||||
|
return CreateReadOnlySpan(ref reference, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(ref TStruct reference)
|
||||||
|
where TStruct : unmanaged where TSpan : unmanaged
|
||||||
|
{
|
||||||
|
return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||||
|
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(ref T reference) where T : unmanaged
|
||||||
|
{
|
||||||
|
return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ namespace LibHac.Crypto2
|
||||||
{
|
{
|
||||||
public static class AesCrypto
|
public static class AesCrypto
|
||||||
{
|
{
|
||||||
|
public const int KeySize128 = 0x10;
|
||||||
|
public const int BlockSize = 0x10;
|
||||||
|
|
||||||
public static bool IsAesNiSupported()
|
public static bool IsAesNiSupported()
|
||||||
{
|
{
|
||||||
#if HAS_INTRINSICS
|
#if HAS_INTRINSICS
|
||||||
|
@ -77,5 +80,29 @@ namespace LibHac.Crypto2
|
||||||
#endif
|
#endif
|
||||||
return new AesCtrEncryptor(key, iv);
|
return new AesCtrEncryptor(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ICipher CreateXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
#if HAS_INTRINSICS
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesXtsCipherHw(key1, key2, iv, true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return new AesXtsCipher(key1, key2, iv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ICipher CreateXtsEncryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
#if HAS_INTRINSICS
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesXtsCipherHw(key1, key2, iv, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return new AesXtsCipher(key1, key2, iv, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,32 +24,24 @@ namespace LibHac.Crypto2
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = _aesCore.RoundKeys;
|
int blockCount = Math.Min(input.Length, output.Length) >> 4;
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
|
||||||
|
|
||||||
Vector128<byte> b = _iv;
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
|
||||||
|
Vector128<byte> iv = _iv;
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; i++)
|
||||||
{
|
{
|
||||||
b = Sse2.Xor(b, inBlocks[i]);
|
iv = _aesCore.EncryptBlock(Sse2.Xor(iv, inBlock));
|
||||||
|
|
||||||
b = Sse2.Xor(b, keys[0]);
|
outBlock = iv;
|
||||||
b = Aes.Encrypt(b, keys[1]);
|
|
||||||
b = Aes.Encrypt(b, keys[2]);
|
|
||||||
b = Aes.Encrypt(b, keys[3]);
|
|
||||||
b = Aes.Encrypt(b, keys[4]);
|
|
||||||
b = Aes.Encrypt(b, keys[5]);
|
|
||||||
b = Aes.Encrypt(b, keys[6]);
|
|
||||||
b = Aes.Encrypt(b, keys[7]);
|
|
||||||
b = Aes.Encrypt(b, keys[8]);
|
|
||||||
b = Aes.Encrypt(b, keys[9]);
|
|
||||||
b = Aes.EncryptLast(b, keys[10]);
|
|
||||||
|
|
||||||
outBlocks[i] = b;
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_iv = b;
|
_iv = iv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,32 +62,22 @@ namespace LibHac.Crypto2
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = _aesCore.RoundKeys;
|
int blockCount = Math.Min(input.Length, output.Length) >> 4;
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
Vector128<byte> iv = _iv;
|
Vector128<byte> iv = _iv;
|
||||||
|
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
for (int i = 0; i < blockCount; i++)
|
||||||
{
|
{
|
||||||
Vector128<byte> b = inBlocks[i];
|
Vector128<byte> decBeforeIv = _aesCore.DecryptBlock(inBlock);
|
||||||
Vector128<byte> nextIv = b;
|
outBlock = Sse2.Xor(decBeforeIv, iv);
|
||||||
|
|
||||||
b = Sse2.Xor(b, keys[10]);
|
iv = inBlock;
|
||||||
b = Aes.Decrypt(b, keys[9]);
|
|
||||||
b = Aes.Decrypt(b, keys[8]);
|
|
||||||
b = Aes.Decrypt(b, keys[7]);
|
|
||||||
b = Aes.Decrypt(b, keys[6]);
|
|
||||||
b = Aes.Decrypt(b, keys[5]);
|
|
||||||
b = Aes.Decrypt(b, keys[4]);
|
|
||||||
b = Aes.Decrypt(b, keys[3]);
|
|
||||||
b = Aes.Decrypt(b, keys[2]);
|
|
||||||
b = Aes.Decrypt(b, keys[1]);
|
|
||||||
b = Aes.DecryptLast(b, keys[0]);
|
|
||||||
|
|
||||||
b = Sse2.Xor(b, iv);
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
iv = nextIv;
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
outBlocks[i] = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_iv = iv;
|
_iv = iv;
|
||||||
|
|
|
@ -15,67 +15,90 @@ namespace LibHac.Crypto2
|
||||||
|
|
||||||
private Vector128<byte> _roundKeys;
|
private Vector128<byte> _roundKeys;
|
||||||
|
|
||||||
|
public AesCoreNi(ReadOnlySpan<byte> key, bool isDecrypting)
|
||||||
|
{
|
||||||
|
_roundKeys = default;
|
||||||
|
|
||||||
|
KeyExpansion(key, MemoryMarshal.CreateSpan(ref _roundKeys, RoundKeyCount), isDecrypting);
|
||||||
|
}
|
||||||
|
|
||||||
public void Initialize(ReadOnlySpan<byte> key, bool isDecrypting)
|
public void Initialize(ReadOnlySpan<byte> key, bool isDecrypting)
|
||||||
{
|
{
|
||||||
KeyExpansion(key, RoundKeys, isDecrypting);
|
KeyExpansion(key, MemoryMarshal.CreateSpan(ref _roundKeys, RoundKeyCount), isDecrypting);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span<Vector128<byte>> RoundKeys =>
|
public readonly ReadOnlySpan<Vector128<byte>> RoundKeys =>
|
||||||
MemoryMarshal.CreateSpan(ref _roundKeys, RoundKeyCount);
|
MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in _roundKeys), RoundKeyCount);
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Encrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
public readonly void Encrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = RoundKeys;
|
int blockCount = Math.Min(input.Length, output.Length) >> 4;
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
|
||||||
|
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; i++)
|
||||||
{
|
{
|
||||||
Vector128<byte> b = inBlocks[i];
|
outBlock = EncryptBlock(inBlock);
|
||||||
|
|
||||||
b = Sse2.Xor(b, keys[0]);
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
b = Aes.Encrypt(b, keys[1]);
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
b = Aes.Encrypt(b, keys[2]);
|
|
||||||
b = Aes.Encrypt(b, keys[3]);
|
|
||||||
b = Aes.Encrypt(b, keys[4]);
|
|
||||||
b = Aes.Encrypt(b, keys[5]);
|
|
||||||
b = Aes.Encrypt(b, keys[6]);
|
|
||||||
b = Aes.Encrypt(b, keys[7]);
|
|
||||||
b = Aes.Encrypt(b, keys[8]);
|
|
||||||
b = Aes.Encrypt(b, keys[9]);
|
|
||||||
b = Aes.EncryptLast(b, keys[10]);
|
|
||||||
|
|
||||||
outBlocks[i] = b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Decrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
public readonly void Decrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
int blockCount = Math.Min(input.Length, output.Length) >> 4;
|
||||||
|
|
||||||
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; i++)
|
||||||
|
{
|
||||||
|
outBlock = DecryptBlock(inBlock);
|
||||||
|
|
||||||
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly Vector128<byte> EncryptBlock(Vector128<byte> input)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = RoundKeys;
|
ReadOnlySpan<Vector128<byte>> keys = RoundKeys;
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
|
||||||
|
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
Vector128<byte> b = Sse2.Xor(input, keys[0]);
|
||||||
{
|
b = Aes.Encrypt(b, keys[1]);
|
||||||
Vector128<byte> b = inBlocks[i];
|
b = Aes.Encrypt(b, keys[2]);
|
||||||
|
b = Aes.Encrypt(b, keys[3]);
|
||||||
|
b = Aes.Encrypt(b, keys[4]);
|
||||||
|
b = Aes.Encrypt(b, keys[5]);
|
||||||
|
b = Aes.Encrypt(b, keys[6]);
|
||||||
|
b = Aes.Encrypt(b, keys[7]);
|
||||||
|
b = Aes.Encrypt(b, keys[8]);
|
||||||
|
b = Aes.Encrypt(b, keys[9]);
|
||||||
|
return Aes.EncryptLast(b, keys[10]);
|
||||||
|
}
|
||||||
|
|
||||||
b = Sse2.Xor(b, keys[10]);
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
b = Aes.Decrypt(b, keys[9]);
|
public readonly Vector128<byte> DecryptBlock(Vector128<byte> input)
|
||||||
b = Aes.Decrypt(b, keys[8]);
|
{
|
||||||
b = Aes.Decrypt(b, keys[7]);
|
ReadOnlySpan<Vector128<byte>> keys = RoundKeys;
|
||||||
b = Aes.Decrypt(b, keys[6]);
|
|
||||||
b = Aes.Decrypt(b, keys[5]);
|
|
||||||
b = Aes.Decrypt(b, keys[4]);
|
|
||||||
b = Aes.Decrypt(b, keys[3]);
|
|
||||||
b = Aes.Decrypt(b, keys[2]);
|
|
||||||
b = Aes.Decrypt(b, keys[1]);
|
|
||||||
b = Aes.DecryptLast(b, keys[0]);
|
|
||||||
|
|
||||||
outBlocks[i] = b;
|
Vector128<byte> b = Sse2.Xor(input, keys[10]);
|
||||||
}
|
b = Aes.Decrypt(b, keys[9]);
|
||||||
|
b = Aes.Decrypt(b, keys[8]);
|
||||||
|
b = Aes.Decrypt(b, keys[7]);
|
||||||
|
b = Aes.Decrypt(b, keys[6]);
|
||||||
|
b = Aes.Decrypt(b, keys[5]);
|
||||||
|
b = Aes.Decrypt(b, keys[4]);
|
||||||
|
b = Aes.Decrypt(b, keys[3]);
|
||||||
|
b = Aes.Decrypt(b, keys[2]);
|
||||||
|
b = Aes.Decrypt(b, keys[1]);
|
||||||
|
return Aes.DecryptLast(b, keys[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
|
|
|
@ -23,42 +23,35 @@ namespace LibHac.Crypto2
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = _aesCore.RoundKeys;
|
int blockCount = Math.Min(input.Length, output.Length) >> 4;
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
Vector128<byte> byteSwapMask = Vector128.Create((ulong)0x706050403020100, 0x8090A0B0C0D0E0F).AsByte();
|
Vector128<byte> byteSwapMask = Vector128.Create((ulong)0x706050403020100, 0x8090A0B0C0D0E0F).AsByte();
|
||||||
Vector128<ulong> inc = Vector128.Create((ulong)0, 1);
|
Vector128<ulong> inc = Vector128.Create((ulong)0, 1);
|
||||||
|
|
||||||
var iv = _iv;
|
Vector128<byte> iv = _iv;
|
||||||
Vector128<ulong> bSwappedIv = Ssse3.Shuffle(iv, byteSwapMask).AsUInt64();
|
Vector128<ulong> bSwappedIv = Ssse3.Shuffle(iv, byteSwapMask).AsUInt64();
|
||||||
|
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
for (int i = 0; i < blockCount; i++)
|
||||||
{
|
{
|
||||||
Vector128<byte> b = Sse2.Xor(iv, keys[0]);
|
Vector128<byte> encIv = _aesCore.EncryptBlock(iv);
|
||||||
b = Aes.Encrypt(b, keys[1]);
|
outBlock = Sse2.Xor(inBlock, encIv);
|
||||||
b = Aes.Encrypt(b, keys[2]);
|
|
||||||
b = Aes.Encrypt(b, keys[3]);
|
|
||||||
b = Aes.Encrypt(b, keys[4]);
|
|
||||||
b = Aes.Encrypt(b, keys[5]);
|
|
||||||
b = Aes.Encrypt(b, keys[6]);
|
|
||||||
b = Aes.Encrypt(b, keys[7]);
|
|
||||||
b = Aes.Encrypt(b, keys[8]);
|
|
||||||
b = Aes.Encrypt(b, keys[9]);
|
|
||||||
b = Aes.EncryptLast(b, keys[10]);
|
|
||||||
|
|
||||||
outBlocks[i] = Sse2.Xor(inBlocks[i], b);
|
|
||||||
|
|
||||||
// Increase the counter
|
// Increase the counter
|
||||||
bSwappedIv = Sse2.Add(bSwappedIv, inc);
|
bSwappedIv = Sse2.Add(bSwappedIv, inc);
|
||||||
iv = Ssse3.Shuffle(bSwappedIv.AsByte(), byteSwapMask);
|
iv = Ssse3.Shuffle(bSwappedIv.AsByte(), byteSwapMask);
|
||||||
|
|
||||||
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_iv = iv;
|
_iv = iv;
|
||||||
|
|
||||||
if ((input.Length & 0xF) != 0)
|
if ((input.Length & 0xF) != 0)
|
||||||
{
|
{
|
||||||
EncryptCtrPartialBlock(input.Slice(inBlocks.Length * 0x10), output.Slice(outBlocks.Length * 0x10));
|
EncryptCtrPartialBlock(input.Slice(blockCount * 0x10), output.Slice(blockCount * 0x10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace LibHac.Crypto2
|
namespace LibHac.Crypto2
|
||||||
{
|
{
|
||||||
public class AesEcbEncryptor : ICipher
|
public class AesEcbEncryptor : ICipher
|
||||||
{
|
{
|
||||||
|
private const int BufferRentThreshold = 1024;
|
||||||
private ICryptoTransform _encryptor;
|
private ICryptoTransform _encryptor;
|
||||||
|
|
||||||
public AesEcbEncryptor(ReadOnlySpan<byte> key)
|
public AesEcbEncryptor(ReadOnlySpan<byte> key)
|
||||||
|
@ -21,16 +23,34 @@ namespace LibHac.Crypto2
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
var outputBuffer = new byte[input.Length];
|
if (input.Length < BufferRentThreshold)
|
||||||
|
{
|
||||||
|
var outputBuffer = new byte[input.Length];
|
||||||
|
input.CopyTo(outputBuffer);
|
||||||
|
|
||||||
_encryptor.TransformBlock(input.ToArray(), 0, input.Length, outputBuffer, 0);
|
_encryptor.TransformBlock(outputBuffer, 0, input.Length, outputBuffer, 0);
|
||||||
|
|
||||||
outputBuffer.CopyTo(output);
|
outputBuffer.CopyTo(output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byte[] outputBuffer = ArrayPool<byte>.Shared.Rent(input.Length);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
input.CopyTo(outputBuffer);
|
||||||
|
|
||||||
|
_encryptor.TransformBlock(outputBuffer, 0, input.Length, outputBuffer, 0);
|
||||||
|
|
||||||
|
outputBuffer.CopyTo(output);
|
||||||
|
}
|
||||||
|
finally { ArrayPool<byte>.Shared.Return(outputBuffer); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesEcbDecryptor : ICipher
|
public class AesEcbDecryptor : ICipher
|
||||||
{
|
{
|
||||||
|
private const int BufferRentThreshold = 1024;
|
||||||
private ICryptoTransform _decryptor;
|
private ICryptoTransform _decryptor;
|
||||||
|
|
||||||
public AesEcbDecryptor(ReadOnlySpan<byte> key)
|
public AesEcbDecryptor(ReadOnlySpan<byte> key)
|
||||||
|
@ -47,11 +67,28 @@ namespace LibHac.Crypto2
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
var outputBuffer = new byte[input.Length];
|
if (input.Length < BufferRentThreshold)
|
||||||
|
{
|
||||||
|
var outputBuffer = new byte[input.Length];
|
||||||
|
input.CopyTo(outputBuffer);
|
||||||
|
|
||||||
_decryptor.TransformBlock(input.ToArray(), 0, input.Length, outputBuffer, 0);
|
_decryptor.TransformBlock(outputBuffer, 0, input.Length, outputBuffer, 0);
|
||||||
|
|
||||||
outputBuffer.CopyTo(output);
|
outputBuffer.CopyTo(output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byte[] outputBuffer = ArrayPool<byte>.Shared.Rent(input.Length);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
input.CopyTo(outputBuffer);
|
||||||
|
|
||||||
|
_decryptor.TransformBlock(outputBuffer, 0, input.Length, outputBuffer, 0);
|
||||||
|
|
||||||
|
outputBuffer.CopyTo(output);
|
||||||
|
}
|
||||||
|
finally { ArrayPool<byte>.Shared.Return(outputBuffer); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
|
#if HAS_INTRINSICS
|
||||||
|
|
||||||
#if HAS_INTRINSICS
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Intrinsics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
|
|
||||||
namespace LibHac.Crypto2
|
namespace LibHac.Crypto2
|
||||||
{
|
{
|
||||||
|
@ -20,28 +15,7 @@ namespace LibHac.Crypto2
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<Vector128<byte>> keys = _aesCore.RoundKeys;
|
_aesCore.Encrypt(input, output);
|
||||||
ReadOnlySpan<Vector128<byte>> inBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(input);
|
|
||||||
Span<Vector128<byte>> outBlocks = MemoryMarshal.Cast<byte, Vector128<byte>>(output);
|
|
||||||
|
|
||||||
for (int i = 0; i < inBlocks.Length; i++)
|
|
||||||
{
|
|
||||||
Vector128<byte> b = inBlocks[i];
|
|
||||||
|
|
||||||
b = Sse2.Xor(b, keys[0]);
|
|
||||||
b = Aes.Encrypt(b, keys[1]);
|
|
||||||
b = Aes.Encrypt(b, keys[2]);
|
|
||||||
b = Aes.Encrypt(b, keys[3]);
|
|
||||||
b = Aes.Encrypt(b, keys[4]);
|
|
||||||
b = Aes.Encrypt(b, keys[5]);
|
|
||||||
b = Aes.Encrypt(b, keys[6]);
|
|
||||||
b = Aes.Encrypt(b, keys[7]);
|
|
||||||
b = Aes.Encrypt(b, keys[8]);
|
|
||||||
b = Aes.Encrypt(b, keys[9]);
|
|
||||||
b = Aes.EncryptLast(b, keys[10]);
|
|
||||||
|
|
||||||
outBlocks[i] = b;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
208
src/LibHac/Crypto2/AesXtsMode.cs
Normal file
208
src/LibHac/Crypto2/AesXtsMode.cs
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
|
||||||
|
namespace LibHac.Crypto2
|
||||||
|
{
|
||||||
|
public class AesXtsCipher : ICipher
|
||||||
|
{
|
||||||
|
private ICipher _dataCipher;
|
||||||
|
private ICipher _tweakCipher;
|
||||||
|
private Buffer16 _iv;
|
||||||
|
private bool _decrypting;
|
||||||
|
|
||||||
|
public AesXtsCipher(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool decrypting)
|
||||||
|
{
|
||||||
|
Debug.Assert(key1.Length == AesCrypto.KeySize128);
|
||||||
|
Debug.Assert(key2.Length == AesCrypto.KeySize128);
|
||||||
|
Debug.Assert(iv.Length == AesCrypto.KeySize128);
|
||||||
|
|
||||||
|
if (decrypting)
|
||||||
|
{
|
||||||
|
_dataCipher = new AesEcbDecryptor(key1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dataCipher = new AesEcbEncryptor(key1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_tweakCipher = new AesEcbEncryptor(key2);
|
||||||
|
|
||||||
|
_iv = new Buffer16();
|
||||||
|
iv.CopyTo(_iv);
|
||||||
|
|
||||||
|
_decrypting = decrypting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Encrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
int length = Math.Min(input.Length, output.Length);
|
||||||
|
int blockCount = length >> 4;
|
||||||
|
int leftover = length & 0xF;
|
||||||
|
|
||||||
|
// Data units must be at least 1 block long.
|
||||||
|
if (length < AesCrypto.BlockSize)
|
||||||
|
throw new ArgumentException();
|
||||||
|
|
||||||
|
var tweak = new Buffer16();
|
||||||
|
|
||||||
|
_tweakCipher.Transform(_iv, tweak);
|
||||||
|
|
||||||
|
byte[] tweakBufferRented = ArrayPool<byte>.Shared.Rent(blockCount * AesCrypto.BlockSize);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Span<byte> tweakBuffer = tweakBufferRented.AsSpan(0, blockCount * AesCrypto.BlockSize);
|
||||||
|
tweak = FillTweakBuffer(tweak, MemoryMarshal.Cast<byte, Buffer16>(tweakBuffer));
|
||||||
|
|
||||||
|
Util.XorArrays(output, input, tweakBuffer);
|
||||||
|
_dataCipher.Transform(output.Slice(0, blockCount * AesCrypto.BlockSize), output);
|
||||||
|
Util.XorArrays(output, output, tweakBuffer);
|
||||||
|
}
|
||||||
|
finally { ArrayPool<byte>.Shared.Return(tweakBufferRented); }
|
||||||
|
|
||||||
|
if (leftover != 0)
|
||||||
|
{
|
||||||
|
ref Buffer16 inBlock =
|
||||||
|
ref Unsafe.Add(ref Unsafe.As<byte, Buffer16>(ref MemoryMarshal.GetReference(input)), blockCount);
|
||||||
|
|
||||||
|
ref Buffer16 outBlock =
|
||||||
|
ref Unsafe.Add(ref Unsafe.As<byte, Buffer16>(ref MemoryMarshal.GetReference(output)), blockCount);
|
||||||
|
|
||||||
|
ref Buffer16 prevOutBlock = ref Unsafe.Subtract(ref outBlock, 1);
|
||||||
|
|
||||||
|
var tmp = new Buffer16();
|
||||||
|
|
||||||
|
for (int i = 0; i < leftover; i++)
|
||||||
|
{
|
||||||
|
outBlock[i] = prevOutBlock[i];
|
||||||
|
tmp[i] = inBlock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = leftover; i < AesCrypto.BlockSize; i++)
|
||||||
|
{
|
||||||
|
tmp[i] = prevOutBlock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
XorBuffer(ref tmp, ref tmp, ref tweak);
|
||||||
|
_dataCipher.Transform(tmp, tmp);
|
||||||
|
XorBuffer(ref prevOutBlock, ref tmp, ref tweak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Decrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
int length = Math.Min(input.Length, output.Length);
|
||||||
|
int blockCount = length >> 4;
|
||||||
|
int leftover = length & 0xF;
|
||||||
|
|
||||||
|
// Data units must be at least 1 block long.
|
||||||
|
if (length < AesCrypto.BlockSize)
|
||||||
|
throw new ArgumentException();
|
||||||
|
|
||||||
|
if (leftover != 0) blockCount--;
|
||||||
|
|
||||||
|
var tweak = new Buffer16();
|
||||||
|
|
||||||
|
_tweakCipher.Transform(_iv, tweak);
|
||||||
|
|
||||||
|
if (blockCount > 0)
|
||||||
|
{
|
||||||
|
byte[] tweakBufferRented = ArrayPool<byte>.Shared.Rent(blockCount * AesCrypto.BlockSize);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Span<byte> tweakBuffer = tweakBufferRented.AsSpan(0, blockCount * AesCrypto.BlockSize);
|
||||||
|
tweak = FillTweakBuffer(tweak, MemoryMarshal.Cast<byte, Buffer16>(tweakBuffer));
|
||||||
|
|
||||||
|
Util.XorArrays(output, input, tweakBuffer);
|
||||||
|
_dataCipher.Transform(output.Slice(0, blockCount * AesCrypto.BlockSize), output);
|
||||||
|
Util.XorArrays(output, output, tweakBuffer);
|
||||||
|
}
|
||||||
|
finally { ArrayPool<byte>.Shared.Return(tweakBufferRented); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftover != 0)
|
||||||
|
{
|
||||||
|
Buffer16 finalTweak = tweak;
|
||||||
|
Gf128Mul(ref finalTweak);
|
||||||
|
|
||||||
|
ref Buffer16 inBlock =
|
||||||
|
ref Unsafe.Add(ref Unsafe.As<byte, Buffer16>(ref MemoryMarshal.GetReference(input)), blockCount);
|
||||||
|
|
||||||
|
ref Buffer16 outBlock =
|
||||||
|
ref Unsafe.Add(ref Unsafe.As<byte, Buffer16>(ref MemoryMarshal.GetReference(output)), blockCount);
|
||||||
|
|
||||||
|
var tmp = new Buffer16();
|
||||||
|
|
||||||
|
XorBuffer(ref tmp, ref inBlock, ref finalTweak);
|
||||||
|
_dataCipher.Transform(tmp, tmp);
|
||||||
|
XorBuffer(ref outBlock, ref tmp, ref finalTweak);
|
||||||
|
|
||||||
|
ref Buffer16 finalOutBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
|
ref Buffer16 finalInBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < leftover; i++)
|
||||||
|
{
|
||||||
|
finalOutBlock[i] = outBlock[i];
|
||||||
|
tmp[i] = finalInBlock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = leftover; i < AesCrypto.BlockSize; i++)
|
||||||
|
{
|
||||||
|
tmp[i] = outBlock[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
XorBuffer(ref tmp, ref tmp, ref tweak);
|
||||||
|
_dataCipher.Transform(tmp, tmp);
|
||||||
|
XorBuffer(ref outBlock, ref tmp, ref tweak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
if (_decrypting)
|
||||||
|
{
|
||||||
|
Decrypt(input, output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Encrypt(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Buffer16 FillTweakBuffer(Buffer16 initialTweak, Span<Buffer16> tweakBuffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tweakBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
tweakBuffer[i] = initialTweak;
|
||||||
|
Gf128Mul(ref initialTweak);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialTweak;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void Gf128Mul(ref Buffer16 buffer)
|
||||||
|
{
|
||||||
|
Span<ulong> b = buffer.AsSpan<ulong>();
|
||||||
|
|
||||||
|
ulong tt = (ulong)((long)b[1] >> 63) & 0x87;
|
||||||
|
|
||||||
|
b[1] = (b[1] << 1) | (b[0] >> 63);
|
||||||
|
b[0] = (b[0] << 1) ^ tt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void XorBuffer(ref Buffer16 output, ref Buffer16 input1, ref Buffer16 input2)
|
||||||
|
{
|
||||||
|
Span<ulong> outputS = output.AsSpan<ulong>();
|
||||||
|
Span<ulong> input1S = input1.AsSpan<ulong>();
|
||||||
|
Span<ulong> input2S = input2.AsSpan<ulong>();
|
||||||
|
|
||||||
|
outputS[0] = input1S[0] ^ input2S[0];
|
||||||
|
outputS[1] = input1S[1] ^ input2S[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
186
src/LibHac/Crypto2/AesXtsModeHw.cs
Normal file
186
src/LibHac/Crypto2/AesXtsModeHw.cs
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
#if HAS_INTRINSICS
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
using LibHac.Common;
|
||||||
|
|
||||||
|
namespace LibHac.Crypto2
|
||||||
|
{
|
||||||
|
public class AesXtsCipherHw : ICipher
|
||||||
|
{
|
||||||
|
private AesCoreNi _dataAesCore;
|
||||||
|
private AesCoreNi _tweakAesCore;
|
||||||
|
private Vector128<byte> _iv;
|
||||||
|
private bool _decrypting;
|
||||||
|
|
||||||
|
public AesXtsCipherHw(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool decrypting)
|
||||||
|
{
|
||||||
|
Debug.Assert(key1.Length == AesCrypto.KeySize128);
|
||||||
|
Debug.Assert(key2.Length == AesCrypto.KeySize128);
|
||||||
|
Debug.Assert(iv.Length == AesCrypto.KeySize128);
|
||||||
|
|
||||||
|
_dataAesCore = new AesCoreNi();
|
||||||
|
_dataAesCore.Initialize(key1, decrypting);
|
||||||
|
|
||||||
|
_tweakAesCore = new AesCoreNi();
|
||||||
|
_tweakAesCore.Initialize(key2, false);
|
||||||
|
|
||||||
|
_iv = Unsafe.ReadUnaligned<Vector128<byte>>(ref MemoryMarshal.GetReference(iv));
|
||||||
|
|
||||||
|
_decrypting = decrypting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Encrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
int length = Math.Min(input.Length, output.Length);
|
||||||
|
int blockCount = length >> 4;
|
||||||
|
int leftover = length & 0xF;
|
||||||
|
|
||||||
|
Debug.Assert(blockCount > 0);
|
||||||
|
|
||||||
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
|
Vector128<byte> mask = Vector128.Create(0x87, 1).AsByte();
|
||||||
|
|
||||||
|
Vector128<byte> tweak = _tweakAesCore.EncryptBlock(_iv);
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; i++)
|
||||||
|
{
|
||||||
|
Vector128<byte> tmp = Sse2.Xor(inBlock, tweak);
|
||||||
|
tmp = _dataAesCore.EncryptBlock(tmp);
|
||||||
|
outBlock = Sse2.Xor(tmp, tweak);
|
||||||
|
|
||||||
|
tweak = Gf128Mul(tweak, mask);
|
||||||
|
|
||||||
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftover != 0)
|
||||||
|
{
|
||||||
|
EncryptPartialFinalBlock(ref inBlock, ref outBlock, tweak, leftover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Decrypt(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
int length = Math.Min(input.Length, output.Length);
|
||||||
|
int blockCount = length >> 4;
|
||||||
|
int leftover = length & 0xF;
|
||||||
|
|
||||||
|
Debug.Assert(blockCount > 0);
|
||||||
|
|
||||||
|
if (leftover != 0) blockCount--;
|
||||||
|
|
||||||
|
ref Vector128<byte> inBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input));
|
||||||
|
ref Vector128<byte> outBlock = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(output));
|
||||||
|
|
||||||
|
Vector128<byte> mask = Vector128.Create(0x87, 1).AsByte();
|
||||||
|
|
||||||
|
Vector128<byte> tweak = _tweakAesCore.EncryptBlock(_iv);
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; i++)
|
||||||
|
{
|
||||||
|
Vector128<byte> tmp = Sse2.Xor(inBlock, tweak);
|
||||||
|
tmp = _dataAesCore.DecryptBlock(tmp);
|
||||||
|
outBlock = Sse2.Xor(tmp, tweak);
|
||||||
|
|
||||||
|
tweak = Gf128Mul(tweak, mask);
|
||||||
|
|
||||||
|
inBlock = ref Unsafe.Add(ref inBlock, 1);
|
||||||
|
outBlock = ref Unsafe.Add(ref outBlock, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftover != 0)
|
||||||
|
{
|
||||||
|
DecryptPartialFinalBlock(ref inBlock, ref outBlock, tweak, mask, leftover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once RedundantAssignment
|
||||||
|
private void DecryptPartialFinalBlock(ref Vector128<byte> input, ref Vector128<byte> output,
|
||||||
|
Vector128<byte> tweak, Vector128<byte> mask, int finalBlockLength)
|
||||||
|
{
|
||||||
|
Vector128<byte> finalTweak = Gf128Mul(tweak, mask);
|
||||||
|
|
||||||
|
Vector128<byte> tmp = Sse2.Xor(input, finalTweak);
|
||||||
|
tmp = _dataAesCore.DecryptBlock(tmp);
|
||||||
|
output = Sse2.Xor(tmp, finalTweak);
|
||||||
|
|
||||||
|
var x = new Buffer16();
|
||||||
|
ref Buffer16 outBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref output);
|
||||||
|
ref Buffer16 nextInBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref Unsafe.Add(ref input, 1));
|
||||||
|
ref Buffer16 nextOutBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref Unsafe.Add(ref output, 1));
|
||||||
|
|
||||||
|
for (int i = 0; i < finalBlockLength; i++)
|
||||||
|
{
|
||||||
|
nextOutBuf[i] = outBuf[i];
|
||||||
|
x[i] = nextInBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = finalBlockLength; i < 16; i++)
|
||||||
|
{
|
||||||
|
x[i] = outBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = Sse2.Xor(x.As<Vector128<byte>>(), tweak);
|
||||||
|
tmp = _dataAesCore.DecryptBlock(tmp);
|
||||||
|
output = Sse2.Xor(tmp, tweak);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EncryptPartialFinalBlock(ref Vector128<byte> input, ref Vector128<byte> output,
|
||||||
|
Vector128<byte> tweak, int finalBlockLength)
|
||||||
|
{
|
||||||
|
ref Vector128<byte> prevOutBlock = ref Unsafe.Subtract(ref output, 1);
|
||||||
|
|
||||||
|
var x = new Buffer16();
|
||||||
|
ref Buffer16 outBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref output);
|
||||||
|
ref Buffer16 inBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref input);
|
||||||
|
ref Buffer16 prevOutBuf = ref Unsafe.As<Vector128<byte>, Buffer16>(ref prevOutBlock);
|
||||||
|
|
||||||
|
for (int i = 0; i < finalBlockLength; i++)
|
||||||
|
{
|
||||||
|
outBuf[i] = prevOutBuf[i];
|
||||||
|
x[i] = inBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = finalBlockLength; i < 16; i++)
|
||||||
|
{
|
||||||
|
x[i] = prevOutBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector128<byte> tmp = Sse2.Xor(x.As<Vector128<byte>>(), tweak);
|
||||||
|
tmp = _dataAesCore.EncryptBlock(tmp);
|
||||||
|
prevOutBlock = Sse2.Xor(tmp, tweak);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
if (_decrypting)
|
||||||
|
{
|
||||||
|
Decrypt(input, output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Encrypt(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static Vector128<byte> Gf128Mul(Vector128<byte> iv, Vector128<byte> mask)
|
||||||
|
{
|
||||||
|
Vector128<byte> tmp1 = Sse2.Add(iv.AsUInt64(), iv.AsUInt64()).AsByte();
|
||||||
|
|
||||||
|
Vector128<byte> tmp2 = Sse2.Shuffle(iv.AsInt32(), 0x13).AsByte();
|
||||||
|
tmp2 = Sse2.ShiftRightArithmetic(tmp2.AsInt32(), 31).AsByte();
|
||||||
|
tmp2 = Sse2.And(mask, tmp2);
|
||||||
|
|
||||||
|
return Sse2.Xor(tmp1, tmp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -546,6 +546,7 @@ namespace LibHac.FsService
|
||||||
|
|
||||||
private void CloseReader(SaveDataInfoReader reader)
|
private void CloseReader(SaveDataInfoReader reader)
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once RedundantAssignment
|
||||||
bool wasRemoved = OpenedReaders.Remove(reader);
|
bool wasRemoved = OpenedReaders.Remove(reader);
|
||||||
|
|
||||||
Debug.Assert(wasRemoved);
|
Debug.Assert(wasRemoved);
|
||||||
|
|
|
@ -145,13 +145,13 @@ namespace LibHac
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void XorArrays(Span<byte> transformData, Span<byte> xorData)
|
public static void XorArrays(Span<byte> transformData, ReadOnlySpan<byte> xorData)
|
||||||
{
|
{
|
||||||
int sisdStart = 0;
|
int sisdStart = 0;
|
||||||
if (Vector.IsHardwareAccelerated)
|
if (Vector.IsHardwareAccelerated)
|
||||||
{
|
{
|
||||||
Span<Vector<byte>> dataVec = MemoryMarshal.Cast<byte, Vector<byte>>(transformData);
|
Span<Vector<byte>> dataVec = MemoryMarshal.Cast<byte, Vector<byte>>(transformData);
|
||||||
Span<Vector<byte>> xorVec = MemoryMarshal.Cast<byte, Vector<byte>>(xorData);
|
ReadOnlySpan<Vector<byte>> xorVec = MemoryMarshal.Cast<byte, Vector<byte>>(xorData);
|
||||||
sisdStart = dataVec.Length * Vector<byte>.Count;
|
sisdStart = dataVec.Length * Vector<byte>.Count;
|
||||||
|
|
||||||
for (int i = 0; i < dataVec.Length; i++)
|
for (int i = 0; i < dataVec.Length; i++)
|
||||||
|
@ -166,6 +166,33 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void XorArrays(Span<byte> output, ReadOnlySpan<byte> input1, ReadOnlySpan<byte> input2)
|
||||||
|
{
|
||||||
|
int length = Math.Min(input1.Length, input2.Length);
|
||||||
|
|
||||||
|
int sisdStart = 0;
|
||||||
|
if (Vector.IsHardwareAccelerated)
|
||||||
|
{
|
||||||
|
int lengthVec = length / Vector<byte>.Count;
|
||||||
|
|
||||||
|
Span<Vector<byte>> outputVec = MemoryMarshal.Cast<byte, Vector<byte>>(output);
|
||||||
|
ReadOnlySpan<Vector<byte>> input1Vec = MemoryMarshal.Cast<byte, Vector<byte>>(input1);
|
||||||
|
ReadOnlySpan<Vector<byte>> input2Vec = MemoryMarshal.Cast<byte, Vector<byte>>(input2);
|
||||||
|
|
||||||
|
sisdStart = lengthVec * Vector<byte>.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < lengthVec; i++)
|
||||||
|
{
|
||||||
|
outputVec[i] = input1Vec[i] ^ input2Vec[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = sisdStart; i < length; i++)
|
||||||
|
{
|
||||||
|
output[i] = (byte)(input1[i] ^ input2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void CopyStream(this Stream input, Stream output, long length, IProgressReport progress = null)
|
public static void CopyStream(this Stream input, Stream output, long length, IProgressReport progress = null)
|
||||||
{
|
{
|
||||||
const int bufferSize = 0x8000;
|
const int bufferSize = 0x8000;
|
||||||
|
|
|
@ -116,7 +116,8 @@ namespace hactoolnet
|
||||||
logger.LogMessage($"{label}{averageRate}/s, fastest run: {fastestRate}/s, slowest run: {slowestRate}/s");
|
logger.LogMessage($"{label}{averageRate}/s, fastest run: {fastestRate}/s, slowest run: {slowestRate}/s");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunCipherBenchmark(Func<ICipher> cipherNet, Func<ICipher> cipherLibHac, string label, IProgressReport logger)
|
private static void RunCipherBenchmark(Func<ICipher> cipherNet, Func<ICipher> cipherLibHac, bool benchBlocked,
|
||||||
|
string label, IProgressReport logger)
|
||||||
{
|
{
|
||||||
var srcData = new byte[Size];
|
var srcData = new byte[Size];
|
||||||
|
|
||||||
|
@ -131,14 +132,23 @@ namespace hactoolnet
|
||||||
if (AesCrypto.IsAesNiSupported()) CipherBenchmark(srcData, dstDataLh, cipherLibHac, Iterations, "LibHac impl: ", logger);
|
if (AesCrypto.IsAesNiSupported()) CipherBenchmark(srcData, dstDataLh, cipherLibHac, Iterations, "LibHac impl: ", logger);
|
||||||
CipherBenchmark(srcData, dstDataNet, cipherNet, Iterations, ".NET impl: ", logger);
|
CipherBenchmark(srcData, dstDataNet, cipherNet, Iterations, ".NET impl: ", logger);
|
||||||
|
|
||||||
if (AesCrypto.IsAesNiSupported()) CipherBenchmarkBlocked(srcData, dstDataBlockedLh, cipherLibHac, Iterations / 5, "LibHac impl (blocked): ", logger);
|
if (benchBlocked)
|
||||||
CipherBenchmarkBlocked(srcData, dstDataBlockedNet, cipherNet, Iterations / 5, ".NET impl (blocked): ", logger);
|
{
|
||||||
|
if (AesCrypto.IsAesNiSupported())
|
||||||
|
CipherBenchmarkBlocked(srcData, dstDataBlockedLh, cipherLibHac, Iterations / 5, "LibHac impl (blocked): ", logger);
|
||||||
|
|
||||||
|
CipherBenchmarkBlocked(srcData, dstDataBlockedNet, cipherNet, Iterations / 5, ".NET impl (blocked): ", logger);
|
||||||
|
}
|
||||||
|
|
||||||
if (AesCrypto.IsAesNiSupported())
|
if (AesCrypto.IsAesNiSupported())
|
||||||
{
|
{
|
||||||
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataNet)}");
|
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataNet)}");
|
||||||
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataBlockedLh)}");
|
|
||||||
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataBlockedNet)}");
|
if (benchBlocked)
|
||||||
|
{
|
||||||
|
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataBlockedLh)}");
|
||||||
|
logger.LogMessage($"{dstDataLh.SequenceEqual(dstDataBlockedNet)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,12 +207,12 @@ namespace hactoolnet
|
||||||
Func<ICipher> encryptorNet = () => AesCrypto.CreateEcbEncryptor(new byte[0x10], true);
|
Func<ICipher> encryptorNet = () => AesCrypto.CreateEcbEncryptor(new byte[0x10], true);
|
||||||
Func<ICipher> encryptorLh = () => AesCrypto.CreateEcbEncryptor(new byte[0x10]);
|
Func<ICipher> encryptorLh = () => AesCrypto.CreateEcbEncryptor(new byte[0x10]);
|
||||||
|
|
||||||
RunCipherBenchmark(encryptorNet, encryptorLh, "AES-ECB encrypt", ctx.Logger);
|
RunCipherBenchmark(encryptorNet, encryptorLh, true, "AES-ECB encrypt", ctx.Logger);
|
||||||
|
|
||||||
Func<ICipher> decryptorNet = () => AesCrypto.CreateEcbDecryptor(new byte[0x10], true);
|
Func<ICipher> decryptorNet = () => AesCrypto.CreateEcbDecryptor(new byte[0x10], true);
|
||||||
Func<ICipher> decryptorLh = () => AesCrypto.CreateEcbDecryptor(new byte[0x10]);
|
Func<ICipher> decryptorLh = () => AesCrypto.CreateEcbDecryptor(new byte[0x10]);
|
||||||
|
|
||||||
RunCipherBenchmark(decryptorNet, decryptorLh, "AES-ECB decrypt", ctx.Logger);
|
RunCipherBenchmark(decryptorNet, decryptorLh, true, "AES-ECB decrypt", ctx.Logger);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -211,12 +221,12 @@ namespace hactoolnet
|
||||||
Func<ICipher> encryptorNet = () => AesCrypto.CreateCbcEncryptor(new byte[0x10], new byte[0x10], true);
|
Func<ICipher> encryptorNet = () => AesCrypto.CreateCbcEncryptor(new byte[0x10], new byte[0x10], true);
|
||||||
Func<ICipher> encryptorLh = () => AesCrypto.CreateCbcEncryptor(new byte[0x10], new byte[0x10]);
|
Func<ICipher> encryptorLh = () => AesCrypto.CreateCbcEncryptor(new byte[0x10], new byte[0x10]);
|
||||||
|
|
||||||
RunCipherBenchmark(encryptorNet, encryptorLh, "AES-CBC encrypt", ctx.Logger);
|
RunCipherBenchmark(encryptorNet, encryptorLh, true, "AES-CBC encrypt", ctx.Logger);
|
||||||
|
|
||||||
Func<ICipher> decryptorNet = () => AesCrypto.CreateCbcDecryptor(new byte[0x10], new byte[0x10], true);
|
Func<ICipher> decryptorNet = () => AesCrypto.CreateCbcDecryptor(new byte[0x10], new byte[0x10], true);
|
||||||
Func<ICipher> decryptorLh = () => AesCrypto.CreateCbcDecryptor(new byte[0x10], new byte[0x10]);
|
Func<ICipher> decryptorLh = () => AesCrypto.CreateCbcDecryptor(new byte[0x10], new byte[0x10]);
|
||||||
|
|
||||||
RunCipherBenchmark(decryptorNet, decryptorLh, "AES-CBC decrypt", ctx.Logger);
|
RunCipherBenchmark(decryptorNet, decryptorLh, true, "AES-CBC decrypt", ctx.Logger);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -226,7 +236,21 @@ namespace hactoolnet
|
||||||
Func<ICipher> encryptorNet = () => AesCrypto.CreateCtrEncryptor(new byte[0x10], new byte[0x10], true);
|
Func<ICipher> encryptorNet = () => AesCrypto.CreateCtrEncryptor(new byte[0x10], new byte[0x10], true);
|
||||||
Func<ICipher> encryptorLh = () => AesCrypto.CreateCtrEncryptor(new byte[0x10], new byte[0x10]);
|
Func<ICipher> encryptorLh = () => AesCrypto.CreateCtrEncryptor(new byte[0x10], new byte[0x10]);
|
||||||
|
|
||||||
RunCipherBenchmark(encryptorNet, encryptorLh, "AES-CTR", ctx.Logger);
|
RunCipherBenchmark(encryptorNet, encryptorLh, true, "AES-CTR", ctx.Logger);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "aesxtsnew":
|
||||||
|
{
|
||||||
|
Func<ICipher> encryptorNet = () => AesCrypto.CreateXtsEncryptor(new byte[0x10], new byte[0x10], new byte[0x10], true);
|
||||||
|
Func<ICipher> encryptorLh = () => AesCrypto.CreateXtsEncryptor(new byte[0x10], new byte[0x10], new byte[0x10]);
|
||||||
|
|
||||||
|
RunCipherBenchmark(encryptorNet, encryptorLh, false, "AES-XTS encrypt", ctx.Logger);
|
||||||
|
|
||||||
|
Func<ICipher> decryptorNet = () => AesCrypto.CreateXtsDecryptor(new byte[0x10], new byte[0x10], new byte[0x10], true);
|
||||||
|
Func<ICipher> decryptorLh = () => AesCrypto.CreateXtsDecryptor(new byte[0x10], new byte[0x10], new byte[0x10]);
|
||||||
|
|
||||||
|
RunCipherBenchmark(decryptorNet, decryptorLh, false, "AES-XTS decrypt", ctx.Logger);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
88
tests/LibHac.Tests/BufferStructTests.cs
Normal file
88
tests/LibHac.Tests/BufferStructTests.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests
|
||||||
|
{
|
||||||
|
public class BufferStructTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public static void BufferIndexer()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
buffer[0] = 5;
|
||||||
|
buffer[1] = 6;
|
||||||
|
|
||||||
|
Assert.Equal(5, buffer[0]);
|
||||||
|
Assert.Equal(6, buffer[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToByteSpan()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
Span<byte> byteSpan = buffer.Bytes;
|
||||||
|
|
||||||
|
Assert.Equal(16, byteSpan.Length);
|
||||||
|
Assert.True(Unsafe.AreSame(ref Unsafe.As<Buffer16, byte>(ref buffer), ref byteSpan[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToByteSpanImplicit()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
Span<byte> byteSpan = buffer;
|
||||||
|
|
||||||
|
Assert.Equal(16, byteSpan.Length);
|
||||||
|
Assert.True(Unsafe.AreSame(ref Unsafe.As<Buffer16, byte>(ref buffer), ref byteSpan[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToReadOnlyByteSpanImplicit()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> byteSpan = buffer;
|
||||||
|
|
||||||
|
Assert.Equal(16, byteSpan.Length);
|
||||||
|
Assert.True(Unsafe.AreSame(ref Unsafe.As<Buffer16, byte>(ref buffer), ref Unsafe.AsRef(byteSpan[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToSpan()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
Span<ulong> ulongSpan = buffer.AsSpan<ulong>();
|
||||||
|
|
||||||
|
Assert.Equal(2, ulongSpan.Length);
|
||||||
|
Assert.True(Unsafe.AreSame(ref Unsafe.As<Buffer16, ulong>(ref buffer), ref ulongSpan[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToStruct()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
ref ulong ulongSpan = ref buffer.As<ulong>();
|
||||||
|
|
||||||
|
Assert.True(Unsafe.AreSame(ref Unsafe.As<Buffer16, ulong>(ref buffer), ref ulongSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CastBufferToLargerStruct()
|
||||||
|
{
|
||||||
|
var buffer = new Buffer16();
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => buffer.As<Struct32Bytes>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||||
|
private struct Struct32Bytes { }
|
||||||
|
}
|
||||||
|
}
|
75
tests/LibHac.Tests/CryptoTests/AesXtsTests.cs
Normal file
75
tests/LibHac.Tests/CryptoTests/AesXtsTests.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LibHac.Crypto2;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.CryptoTests
|
||||||
|
{
|
||||||
|
public class AesXtsTests
|
||||||
|
{
|
||||||
|
public static TheoryData<EncryptionTestVector> EncryptTestVectors =
|
||||||
|
RemovePartialByteTests(RspReader.ReadTestVectors(true, "XTSGenAES128.rsp"));
|
||||||
|
|
||||||
|
public static TheoryData<EncryptionTestVector> DecryptTestVectors =
|
||||||
|
RemovePartialByteTests(RspReader.ReadTestVectors(false, "XTSGenAES128.rsp"));
|
||||||
|
|
||||||
|
// The XTS implementation only supports multiples of whole bytes
|
||||||
|
private static TheoryData<EncryptionTestVector> RemovePartialByteTests(TheoryData<EncryptionTestVector> input)
|
||||||
|
{
|
||||||
|
IEnumerable<EncryptionTestVector> filteredTestVectors = input
|
||||||
|
.Select(x => x[0])
|
||||||
|
.Cast<EncryptionTestVector>()
|
||||||
|
.Where(x => x.DataUnitLength % 8 == 0);
|
||||||
|
|
||||||
|
var output = new TheoryData<EncryptionTestVector>();
|
||||||
|
|
||||||
|
foreach (EncryptionTestVector item in filteredTestVectors)
|
||||||
|
{
|
||||||
|
output.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(EncryptTestVectors))]
|
||||||
|
public static void Encrypt(EncryptionTestVector tv)
|
||||||
|
{
|
||||||
|
Span<byte> key1 = tv.Key.AsSpan(0, 0x10);
|
||||||
|
Span<byte> key2 = tv.Key.AsSpan(0x10, 0x10);
|
||||||
|
|
||||||
|
Common.CipherTestCore(tv.PlainText, tv.CipherText, AesCrypto.CreateXtsEncryptor(key1, key2, tv.Iv, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(DecryptTestVectors))]
|
||||||
|
public static void Decrypt(EncryptionTestVector tv)
|
||||||
|
{
|
||||||
|
Span<byte> key1 = tv.Key.AsSpan(0, 0x10);
|
||||||
|
Span<byte> key2 = tv.Key.AsSpan(0x10, 0x10);
|
||||||
|
|
||||||
|
Common.CipherTestCore(tv.CipherText, tv.PlainText, AesCrypto.CreateXtsDecryptor(key1, key2, tv.Iv, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AesIntrinsicsRequiredTheory]
|
||||||
|
[MemberData(nameof(EncryptTestVectors))]
|
||||||
|
public static void EncryptIntrinsics(EncryptionTestVector tv)
|
||||||
|
{
|
||||||
|
Span<byte> key1 = tv.Key.AsSpan(0, 0x10);
|
||||||
|
Span<byte> key2 = tv.Key.AsSpan(0x10, 0x10);
|
||||||
|
|
||||||
|
Common.CipherTestCore(tv.PlainText, tv.CipherText, AesCrypto.CreateXtsEncryptor(key1, key2, tv.Iv));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AesIntrinsicsRequiredTheory]
|
||||||
|
[MemberData(nameof(DecryptTestVectors))]
|
||||||
|
public static void DecryptIntrinsics(EncryptionTestVector tv)
|
||||||
|
{
|
||||||
|
Span<byte> key1 = tv.Key.AsSpan(0, 0x10);
|
||||||
|
Span<byte> key2 = tv.Key.AsSpan(0x10, 0x10);
|
||||||
|
|
||||||
|
Common.CipherTestCore(tv.CipherText, tv.PlainText, AesCrypto.CreateXtsDecryptor(key1, key2, tv.Iv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,21 +56,27 @@ namespace LibHac.Tests.CryptoTests
|
||||||
|
|
||||||
canOutputVector = true;
|
canOutputVector = true;
|
||||||
|
|
||||||
switch (kvp[0])
|
switch (kvp[0].ToUpperInvariant())
|
||||||
{
|
{
|
||||||
case "COUNT":
|
case "COUNT":
|
||||||
testVector.Count = int.Parse(kvp[1]);
|
testVector.Count = int.Parse(kvp[1]);
|
||||||
break;
|
break;
|
||||||
|
case "DATAUNITLEN":
|
||||||
|
testVector.DataUnitLength = int.Parse(kvp[1]);
|
||||||
|
break;
|
||||||
case "KEY":
|
case "KEY":
|
||||||
testVector.Key = kvp[1].ToBytes();
|
testVector.Key = kvp[1].ToBytes();
|
||||||
break;
|
break;
|
||||||
case "IV":
|
case "IV":
|
||||||
|
case "I":
|
||||||
testVector.Iv = kvp[1].ToBytes();
|
testVector.Iv = kvp[1].ToBytes();
|
||||||
break;
|
break;
|
||||||
case "PLAINTEXT":
|
case "PLAINTEXT":
|
||||||
|
case "PT":
|
||||||
testVector.PlainText = kvp[1].ToBytes();
|
testVector.PlainText = kvp[1].ToBytes();
|
||||||
break;
|
break;
|
||||||
case "CIPHERTEXT":
|
case "CIPHERTEXT":
|
||||||
|
case "CT":
|
||||||
testVector.CipherText = kvp[1].ToBytes();
|
testVector.CipherText = kvp[1].ToBytes();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +100,7 @@ namespace LibHac.Tests.CryptoTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return testVectors;
|
return testVectors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +109,7 @@ namespace LibHac.Tests.CryptoTests
|
||||||
{
|
{
|
||||||
public bool Encrypt { get; set; }
|
public bool Encrypt { get; set; }
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
|
public int DataUnitLength { get; set; }
|
||||||
public byte[] Key { get; set; }
|
public byte[] Key { get; set; }
|
||||||
public byte[] Iv { get; set; }
|
public byte[] Iv { get; set; }
|
||||||
public byte[] PlainText { get; set; }
|
public byte[] PlainText { get; set; }
|
||||||
|
|
8013
tests/LibHac.Tests/CryptoTests/TestVectors/XTSGenAES128.rsp
Normal file
8013
tests/LibHac.Tests/CryptoTests/TestVectors/XTSGenAES128.rsp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue