diff --git a/src/IKVM.Java/map.xml b/src/IKVM.Java/map.xml index f4ea2a3c4..5a55e04a2 100644 --- a/src/IKVM.Java/map.xml +++ b/src/IKVM.Java/map.xml @@ -1638,6 +1638,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/IKVM.Runtime/Accessors/Com/Sun/Crypto/Provider/AESCryptAccessor.cs b/src/IKVM.Runtime/Accessors/Com/Sun/Crypto/Provider/AESCryptAccessor.cs new file mode 100644 index 000000000..a5c6ff726 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Com/Sun/Crypto/Provider/AESCryptAccessor.cs @@ -0,0 +1,13 @@ +namespace IKVM.Runtime.Accessors.Com.Sun.Crypto.Provider; + +#if !(FIRST_PASS || EXPORTER || IMPORTER) + +internal sealed class AESCryptAccessor(AccessorTypeResolver resolver) + : Accessor(resolver, "com.sun.crypto.provider.AESCrypt") +{ + private FieldAccessor k; + + public int[] K(com.sun.crypto.provider.AESCrypt self) => GetField(ref k, "K").GetValue(self); +} + +#endif \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.X86.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.X86.cs new file mode 100644 index 000000000..205d070d1 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.X86.cs @@ -0,0 +1,163 @@ +#if NETCOREAPP3_0_OR_GREATER +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace IKVM.Java.Externs.com.sun.crypto.provider.X86; + +internal abstract partial class AESCrypt +{ + public static bool IsSupported => Aes.IsSupported && Ssse3.IsSupported; + + protected static ReadOnlySpan KeyShuffleMask => [0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DecryptBlock(ReadOnlySpan from, Span to, ReadOnlySpan key) + { + // ext\openjdk\hotspot\src\cpu\x86\vm\stubGenerator_x86_32.cpp:3125-3210 + Vector128 xmm_temp1, xmm_temp2, xmm_temp3, xmm_temp4; +#if NET8_0_OR_GREATER + var xmm_key_shuf_mask = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask), 0).AsByte(); + var xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from)); +#else + var xmm_key_shuf_mask = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask), 0).AsByte(); + var xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from)); +#endif + + xmm_temp1 = LoadKey(key, 0x10, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0x20, xmm_key_shuf_mask); + xmm_temp3 = LoadKey(key, 0x30, xmm_key_shuf_mask); + xmm_temp4 = LoadKey(key, 0x40, xmm_key_shuf_mask); + + xmm_result = Sse2.Xor(xmm_result, xmm_temp1); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp2); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp3); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp4); + + xmm_temp1 = LoadKey(key, 0x50, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0x60, xmm_key_shuf_mask); + xmm_temp3 = LoadKey(key, 0x70, xmm_key_shuf_mask); + xmm_temp4 = LoadKey(key, 0x80, xmm_key_shuf_mask); + + xmm_result = Aes.Decrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp2); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp3); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp4); + + xmm_temp1 = LoadKey(key, 0x90, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xa0, xmm_key_shuf_mask); + xmm_temp3 = LoadKey(key, 0x00, xmm_key_shuf_mask); + + if (key.Length == 44) + { + goto doLast; + } + + xmm_result = Aes.Decrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp2); + + xmm_temp1 = LoadKey(key, 0xb0, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xc0, xmm_key_shuf_mask); + + if (key.Length == 52) + { + goto doLast; + } + + xmm_result = Aes.Decrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp2); + + xmm_temp1 = LoadKey(key, 0xd0, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xe0, xmm_key_shuf_mask); + + doLast:; + xmm_result = Aes.Decrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Decrypt(xmm_result, xmm_temp2); + xmm_result = Aes.DecryptLast(xmm_result, xmm_temp3); + xmm_result.CopyTo(to); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EncryptBlock(ReadOnlySpan from, Span to, ReadOnlySpan key) + { + // ext\openjdk\hotspot\src\cpu\x86\vm\stubGenerator_x86_32.cpp:2189-2277 + Vector128 xmm_temp1, xmm_temp2, xmm_temp3, xmm_temp4; +#if NET8_0_OR_GREATER + var xmm_key_shuf_mask = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); + var xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from)); +#else + var xmm_key_shuf_mask = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); + var xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from)); +#endif + + xmm_temp1 = LoadKey(key, 0x00, xmm_key_shuf_mask); + xmm_result = Sse2.Xor(xmm_result, xmm_temp1); + + xmm_temp1 = LoadKey(key, 0x10, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0x20, xmm_key_shuf_mask); + xmm_temp3 = LoadKey(key, 0x30, xmm_key_shuf_mask); + xmm_temp4 = LoadKey(key, 0x40, xmm_key_shuf_mask); + + xmm_result = Aes.Encrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp2); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp3); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp4); + + xmm_temp1 = LoadKey(key, 0x50, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0x60, xmm_key_shuf_mask); + xmm_temp3 = LoadKey(key, 0x70, xmm_key_shuf_mask); + xmm_temp4 = LoadKey(key, 0x80, xmm_key_shuf_mask); + + xmm_result = Aes.Encrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp2); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp3); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp4); + + xmm_temp1 = LoadKey(key, 0x90, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xa0, xmm_key_shuf_mask); + + if (key.Length == 44) + { + goto doLast; + } + + xmm_result = Aes.Encrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp2); + + xmm_temp1 = LoadKey(key, 0xb0, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xc0, xmm_key_shuf_mask); + + if (key.Length == 52) + { + goto doLast; + } + + xmm_result = Aes.Encrypt(xmm_result, xmm_temp1); + xmm_result = Aes.Encrypt(xmm_result, xmm_temp2); + + xmm_temp1 = LoadKey(key, 0xd0, xmm_key_shuf_mask); + xmm_temp2 = LoadKey(key, 0xe0, xmm_key_shuf_mask); + + doLast:; + xmm_result = Aes.Encrypt(xmm_result, xmm_temp1); + xmm_result = Aes.EncryptLast(xmm_result, xmm_temp2); + xmm_result.CopyTo(to); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected static Vector128 LoadKey(ReadOnlySpan key, nuint offset, Vector128? shuffle_mask = null) + { +#if NET8_0_OR_GREATER + Vector128 xmm_temp = Vector128.LoadUnsafe(in Unsafe.As(ref MemoryMarshal.GetReference(key)), offset); + shuffle_mask ??= Vector128.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#else + Vector128 xmm_temp = Vector128Polyfill.LoadUnsafe(in Unsafe.As(ref MemoryMarshal.GetReference(key)), offset); + shuffle_mask ??= Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#endif + + return Ssse3.Shuffle(xmm_temp, shuffle_mask.Value); + } +} +#endif diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.cs new file mode 100644 index 000000000..d657047bc --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/AESCrypt.cs @@ -0,0 +1,37 @@ +using IKVM.Attributes; +using System; +using System.Runtime.CompilerServices; + +namespace IKVM.Java.Externs.com.sun.crypto.provider; + +[HideFromJava] +internal static class AESCrypt +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool DecryptBlock(byte[] @in, int inOffset, byte[] @out, int outOffset, int[] K) + { +#if NETCOREAPP3_0_OR_GREATER + if (X86.AESCrypt.IsSupported) + { + X86.AESCrypt.DecryptBlock(@in.AsSpan(inOffset), @out.AsSpan(outOffset), K); + return true; + } +#endif + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool EncryptBlock(byte[] @in, int inOffset, byte[] @out, int outOffset, int[] K) + { +#if NETCOREAPP3_0_OR_GREATER + if (X86.AESCrypt.IsSupported) + { + X86.AESCrypt.EncryptBlock(@in.AsSpan(inOffset), @out.AsSpan(outOffset), K); + return true; + } +#endif + + return false; + } +} diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.X86.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.X86.cs new file mode 100644 index 000000000..f8d0a781d --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.X86.cs @@ -0,0 +1,312 @@ +#if NETCOREAPP3_0_OR_GREATER +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace IKVM.Java.Externs.com.sun.crypto.provider.X86; + +internal abstract partial class CipherBlockChaining : AESCrypt +{ + const int AESBlockSize = 16; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DecryptAESCrypt(ReadOnlySpan from, Span to, ReadOnlySpan key, Span rvec, int length) + { + // ext\openjdk\hotspot\src\cpu\x86\vm\stubGenerator_x86_32.cpp:2568-2720 +#if NET8_0_OR_GREATER + Vector128 xmm_key_shuf_mask = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#else + Vector128 xmm_key_shuf_mask = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#endif + + const int XMM_REG_NUM_KEY_FIRST = 2; + const int XMM_REG_NUM_KEY_LAST = 7; + const nuint FIRST_NON_REG_KEY_offset = 0x70; + + Span> xmm_key = stackalloc Vector128[6]; + ref Vector128 xmm_key_first = ref MemoryMarshal.GetReference(xmm_key); + + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x10; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_key[as_XMMRegister(rnum)] = LoadKey(key, (nuint)offset, xmm_key_shuf_mask); + offset += 0x10; + } + + ref byte prev_block_cipher_ptr = ref MemoryMarshal.GetReference(rvec); + + if (key.Length != 44) + { + goto key_192_256; + } + + nuint pos = 0; + singleBlock_loopTop_128: + if (length == 0) + { + goto exit; + } + +#if NET8_0_OR_GREATER + Vector128 xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + Vector128 xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_key_first); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Decrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (var key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xa0; key_offset += 0x10) + { + xmm_result = AesDecKey(xmm_result, key, key_offset); + } + + Vector128 xmm_temp = LoadKey(key, 0x00); + xmm_result = Aes.DecryptLast(xmm_result, xmm_temp); +#if NET8_0_OR_GREATER + xmm_temp = Vector128.LoadUnsafe(in prev_block_cipher_ptr); +#else + xmm_temp = Vector128Polyfill.LoadUnsafe(in prev_block_cipher_ptr); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + prev_block_cipher_ptr = ref Unsafe.Add(ref MemoryMarshal.GetReference(from), pos); + pos += AESBlockSize; + length -= AESBlockSize; + goto singleBlock_loopTop_128; + + exit:; + Unsafe.CopyBlockUnaligned(ref MemoryMarshal.GetReference(rvec), ref prev_block_cipher_ptr, 16); + return; + + key_192_256:; + if (key.Length != 52) + { + goto key_256; + } + + pos = 0; + singleBlock_loopTop_192:; +#if NET8_0_OR_GREATER + xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_key_first); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Decrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (var key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xc0; key_offset += 0x10) + { + xmm_result = AesDecKey(xmm_result, key, key_offset); + } + + xmm_temp = LoadKey(key, 0x00); + xmm_result = Aes.DecryptLast(xmm_result, xmm_temp); +#if NET8_0_OR_GREATER + xmm_temp = Vector128.LoadUnsafe(in prev_block_cipher_ptr); +#else + xmm_temp = Vector128Polyfill.LoadUnsafe(in prev_block_cipher_ptr); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + prev_block_cipher_ptr = ref Unsafe.Add(ref MemoryMarshal.GetReference(from), pos); pos += AESBlockSize; + length -= AESBlockSize; + if (length != 0) + { + goto singleBlock_loopTop_192; + } + + goto exit; + + key_256:; + pos = 0; + singleBlock_loopTop_256:; +#if NET8_0_OR_GREATER + xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_key_first); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Decrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (var key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xe0; key_offset += 0x10) + { + xmm_result = AesDecKey(xmm_result, key, key_offset); + } + + xmm_temp = LoadKey(key, 0x00); + xmm_result = Aes.DecryptLast(xmm_result, xmm_temp); +#if NET8_0_OR_GREATER + xmm_temp = Vector128.LoadUnsafe(in prev_block_cipher_ptr); +#else + xmm_temp = Vector128Polyfill.LoadUnsafe(in prev_block_cipher_ptr); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + prev_block_cipher_ptr = ref Unsafe.Add(ref MemoryMarshal.GetReference(from), pos); pos += AESBlockSize; + length -= AESBlockSize; + if (length != 0) + { + goto singleBlock_loopTop_256; + } + + goto exit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EncryptAESCrypt(ReadOnlySpan from, Span to, ReadOnlySpan key, Span rvec, int length) + { + // ext\openjdk\hotspot\src\cpu\x86\vm\stubGenerator_x86_32.cpp:2410-2549 +#if NET8_0_OR_GREATER + Vector128 xmm_key_shuf_mask = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#else + Vector128 xmm_key_shuf_mask = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(KeyShuffleMask)).AsByte(); +#endif + + const int XMM_REG_NUM_KEY_FIRST = 2; + const int XMM_REG_NUM_KEY_LAST = 7; + + Span> xmm_key = stackalloc Vector128[6]; + ref Vector128 xmm_key0 = ref MemoryMarshal.GetReference(xmm_key); + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x00; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_key[as_XMMRegister(rnum)] = LoadKey(key, (nuint)offset, xmm_key_shuf_mask); + offset += 0x10; + } + +#if NET8_0_OR_GREATER + Vector128 xmm_result = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(rvec)); +#else + Vector128 xmm_result = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(rvec)); +#endif + + if (key.Length != 44) + { + goto key_192_256; + } + + nuint pos = 0; + loopTop_128:; +#if NET8_0_OR_GREATER + Vector128 xmm_temp = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + Vector128 xmm_temp = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result = Sse2.Xor(xmm_result, xmm_key0); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Encrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (int key_offset = 0x60; key_offset <= 0x90; key_offset += 0x10) + { + xmm_result = AesEncKey(xmm_result, key, (nuint)key_offset); + } + + xmm_temp = LoadKey(key, 0xa0); + xmm_result = Aes.EncryptLast(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + pos += AESBlockSize; + length -= AESBlockSize; + if (length != 0) + { + goto loopTop_128; + } + + exit: + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(rvec)); + return; + + key_192_256:; + if (key.Length != 52) + { + goto key_256; + } + + pos = 0; + loopTop_192:; +#if NET8_0_OR_GREATER + xmm_temp = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + xmm_temp = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result = Sse2.Xor(xmm_result, xmm_key0); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Encrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (nuint key_offset = 0x60; key_offset <= 0xb0; key_offset += 0x10) + { + xmm_result = AesEncKey(xmm_result, key, key_offset); + } + + xmm_temp = LoadKey(key, 0xc0); + xmm_result = Aes.EncryptLast(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + pos += AESBlockSize; + length -= AESBlockSize; + if (length != 0) + { + goto loopTop_192; + } + + goto exit; + + key_256:; + pos = 0; + loopTop_256: +#if NET8_0_OR_GREATER + xmm_temp = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#else + xmm_temp = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(from), pos); +#endif + xmm_result = Sse2.Xor(xmm_result, xmm_temp); + xmm_result = Sse2.Xor(xmm_result, xmm_key0); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) + { + xmm_result = Aes.Encrypt(xmm_result, xmm_key[as_XMMRegister(rnum)]); + } + + for (nuint key_offset = 0x60; key_offset <= 0xd0; key_offset += 0x10) + { + xmm_result = AesEncKey(xmm_result, key, key_offset); + } + + xmm_temp = LoadKey(key, 0xe0); + xmm_result = Aes.EncryptLast(xmm_result, xmm_temp); + xmm_result.StoreUnsafe(ref MemoryMarshal.GetReference(to), pos); + pos += AESBlockSize; + length -= AESBlockSize; + if (length != 0) + { + goto loopTop_256; + } + + goto exit; + } + + private static Vector128 AesDecKey(Vector128 xmmdst, ReadOnlySpan key, nuint offset, Vector128? xmm_shuf_mask = null) + { + return Aes.Decrypt(xmmdst, LoadKey(key, offset, xmm_shuf_mask)); + } + + private static Vector128 AesEncKey(Vector128 xmmdst, ReadOnlySpan key, nuint offset, Vector128? xmm_shuf_mask = null) + { + return Aes.Encrypt(xmmdst, LoadKey(key, offset, xmm_shuf_mask)); + } + + private static int as_XMMRegister(int @in) => @in - 2; +} +#endif \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.cs new file mode 100644 index 000000000..f9350406b --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/CipherBlockChaining.cs @@ -0,0 +1,63 @@ +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Com.Sun.Crypto.Provider; +using System; +using System.Runtime.CompilerServices; + +namespace IKVM.Java.Externs.com.sun.crypto.provider; + +internal static class CipherBlockChaining +{ +#if !FIRST_PASS + private static AESCryptAccessor aesCryptAccessor; + + private static AESCryptAccessor AESCryptAccessor => JVM.Internal.BaseAccessors.Get(ref aesCryptAccessor); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool DecryptAESCrypt(object self, byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset) + { +#if !FIRST_PASS && NETCOREAPP3_0_OR_GREATER + if (plain == cipher || self is not global::com.sun.crypto.provider.CipherBlockChaining + { + r: { } r, + embeddedCipher: global::com.sun.crypto.provider.AESCrypt aes + }) + { + return false; + } + + var k = AESCryptAccessor.K(aes); + + if (X86.AESCrypt.IsSupported) + { + X86.CipherBlockChaining.DecryptAESCrypt(cipher.AsSpan(cipherOffset), plain.AsSpan(plainOffset), k, r, cipherLen); + return true; + } +#endif + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool EncryptAESCrypt(object self, byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset) + { +#if !FIRST_PASS && NETCOREAPP3_0_OR_GREATER + if (self is not global::com.sun.crypto.provider.CipherBlockChaining + { + r: { } r, + embeddedCipher: global::com.sun.crypto.provider.AESCrypt aes + }) + { + return false; + } + + var k = AESCryptAccessor.K(aes); + + if (X86.AESCrypt.IsSupported) + { + X86.CipherBlockChaining.EncryptAESCrypt(plain.AsSpan(plainOffset), cipher.AsSpan(cipherOffset), k, r, plainLen); + return true; + } +#endif + return false; + } +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.X86.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.X86.cs new file mode 100644 index 000000000..b1c6125e2 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.X86.cs @@ -0,0 +1,129 @@ +#if NETCOREAPP3_0_OR_GREATER +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace IKVM.Java.Externs.com.sun.crypto.provider.X86; + +internal static class GHASH +{ + public static bool IsSupported => Pclmulqdq.IsSupported && Ssse3.IsSupported; + + private static ReadOnlySpan ByteSwapMask => [0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203]; + + private static ReadOnlySpan LongSwapMask => [0x0b0a0908, 0x0f0e0d0c, 0x03020100, 0x07060504]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ProcessBlocks(ReadOnlySpan data, int blocks, Span state, ReadOnlySpan subH) + { + // ext\openjdk\hotspot\src\cpu\x86\vm\stubGenerator_x86_32.cpp:2748-2883 + Vector128 + xmm_temp0, + xmm_temp1, + xmm_temp2, + xmm_temp3, + xmm_temp4, + xmm_temp5, + xmm_temp6, + xmm_temp7; + +#if NET8_0_OR_GREATER + xmm_temp0 = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(state)).AsInt32(); + xmm_temp0 = Ssse3.Shuffle(xmm_temp0.AsByte(), Vector128.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()).AsInt32(); + xmm_temp1 = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(subH)).AsInt32(); + xmm_temp1 = Ssse3.Shuffle(xmm_temp1.AsByte(), Vector128.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()).AsInt32(); +#else + xmm_temp0 = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(state)).AsInt32(); + xmm_temp0 = Ssse3.Shuffle(xmm_temp0.AsByte(), Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()).AsInt32(); + xmm_temp1 = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(subH)).AsInt32(); + xmm_temp1 = Ssse3.Shuffle(xmm_temp1.AsByte(), Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()).AsInt32(); +#endif + + ghash_loop: +#if NET8_0_OR_GREATER + xmm_temp2 = Vector128.LoadUnsafe(in MemoryMarshal.GetReference(data)).AsInt32(); + xmm_temp2 = Ssse3.Shuffle(xmm_temp2.AsByte(), Vector128.LoadUnsafe(in MemoryMarshal.GetReference(ByteSwapMask)).AsByte()).AsInt32(); +#else + xmm_temp2 = Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(data)).AsInt32(); + xmm_temp2 = Ssse3.Shuffle(xmm_temp2.AsByte(), Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(ByteSwapMask)).AsByte()).AsInt32(); +#endif + xmm_temp0 = Sse2.Xor(xmm_temp0, xmm_temp2); + + xmm_temp3 = xmm_temp0; + xmm_temp3 = Pclmulqdq.CarrylessMultiply(xmm_temp3.AsInt64(), xmm_temp1.AsInt64(), 0).AsInt32(); + xmm_temp4 = xmm_temp0; + xmm_temp4 = Pclmulqdq.CarrylessMultiply(xmm_temp4.AsInt64(), xmm_temp1.AsInt64(), 16).AsInt32(); + + xmm_temp5 = xmm_temp0; + xmm_temp5 = Pclmulqdq.CarrylessMultiply(xmm_temp5.AsInt64(), xmm_temp1.AsInt64(), 1).AsInt32(); + xmm_temp6 = xmm_temp0; + xmm_temp6 = Pclmulqdq.CarrylessMultiply(xmm_temp6.AsInt64(), xmm_temp1.AsInt64(), 17).AsInt32(); + + xmm_temp4 = Sse2.Xor(xmm_temp4, xmm_temp5); + + xmm_temp5 = xmm_temp4; + xmm_temp4 = Sse2.ShiftRightLogical128BitLane(xmm_temp4, 8); + xmm_temp5 = Sse2.ShiftLeftLogical128BitLane(xmm_temp5, 8); + xmm_temp3 = Sse2.Xor(xmm_temp3, xmm_temp5); + xmm_temp6 = Sse2.Xor(xmm_temp6, xmm_temp4); + + xmm_temp7 = xmm_temp3; + xmm_temp4 = xmm_temp6; + xmm_temp3 = Sse2.ShiftLeftLogical(xmm_temp3, 1); + xmm_temp6 = Sse2.ShiftLeftLogical(xmm_temp6, 1); + xmm_temp7 = Sse2.ShiftRightLogical(xmm_temp7, 31); + xmm_temp4 = Sse2.ShiftRightLogical(xmm_temp4, 31); + xmm_temp5 = xmm_temp7; + xmm_temp4 = Sse2.ShiftLeftLogical128BitLane(xmm_temp4, 4); + xmm_temp7 = Sse2.ShiftLeftLogical128BitLane(xmm_temp7, 4); + xmm_temp5 = Sse2.ShiftRightLogical128BitLane(xmm_temp5, 12); + xmm_temp3 = Sse2.Or(xmm_temp3, xmm_temp7); + xmm_temp6 = Sse2.Or(xmm_temp6, xmm_temp4); + xmm_temp6 = Sse2.Or(xmm_temp6, xmm_temp5); + + xmm_temp7 = xmm_temp3; + xmm_temp4 = xmm_temp3; + xmm_temp5 = xmm_temp3; + xmm_temp7 = Sse2.ShiftLeftLogical(xmm_temp7, 31); + xmm_temp4 = Sse2.ShiftLeftLogical(xmm_temp4, 30); + xmm_temp5 = Sse2.ShiftLeftLogical(xmm_temp5, 25); + xmm_temp7 = Sse2.Xor(xmm_temp7, xmm_temp4); + xmm_temp7 = Sse2.Xor(xmm_temp7, xmm_temp5); + xmm_temp4 = xmm_temp7; + xmm_temp7 = Sse2.ShiftLeftLogical128BitLane(xmm_temp7, 12); + xmm_temp4 = Sse2.ShiftRightLogical128BitLane(xmm_temp4, 4); + xmm_temp3 = Sse2.Xor(xmm_temp3, xmm_temp7); + + xmm_temp2 = xmm_temp3; + xmm_temp7 = xmm_temp3; + xmm_temp5 = xmm_temp3; + xmm_temp2 = Sse2.ShiftRightLogical(xmm_temp2, 1); + xmm_temp7 = Sse2.ShiftRightLogical(xmm_temp7, 2); + xmm_temp5 = Sse2.ShiftRightLogical(xmm_temp5, 7); + xmm_temp2 = Sse2.Xor(xmm_temp2, xmm_temp7); + xmm_temp2 = Sse2.Xor(xmm_temp2, xmm_temp5); + xmm_temp2 = Sse2.Xor(xmm_temp2, xmm_temp4); + xmm_temp3 = Sse2.Xor(xmm_temp3, xmm_temp2); + xmm_temp6 = Sse2.Xor(xmm_temp6, xmm_temp3); + if ((--blocks) == 0) + { + goto exit; + } + + xmm_temp0 = xmm_temp6; + data = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.AsRef(in MemoryMarshal.GetReference(data)), 16), data.Length - 16); + goto ghash_loop; + + exit:; +#if NET8_0_OR_GREATER + Ssse3.Shuffle(xmm_temp6.AsByte(), Vector128.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()) + .AsInt64().CopyTo(state); +#else + Ssse3.Shuffle(xmm_temp6.AsByte(), Vector128Polyfill.LoadUnsafe(in MemoryMarshal.GetReference(LongSwapMask)).AsByte()) + .AsInt64().CopyTo(state); +#endif + } +} +#endif diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.cs new file mode 100644 index 000000000..90035b476 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/GHASH.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.CompilerServices; + +namespace IKVM.Java.Externs.com.sun.crypto.provider; + +internal static class GHASH +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ProcessBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) + { +#if NETCOREAPP3_0_OR_GREATER + if (X86.GHASH.IsSupported) + { + X86.GHASH.ProcessBlocks(data.AsSpan(inOfs), blocks, st, subH); + return true; + } +#endif + + return false; + } +} diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/Vector128Polyfill.cs b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/Vector128Polyfill.cs new file mode 100644 index 000000000..5a3647532 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/com/sun/crypto/provider/Vector128Polyfill.cs @@ -0,0 +1,69 @@ +#if NETCOREAPP3_0_OR_GREATER && !NET8_0_OR_GREATER +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace IKVM.Java.Externs.com.sun.crypto.provider; + +internal static class Vector128Polyfill +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this Vector128 vector, Span destination) where T : struct + { + if (destination.Length < Vector128.Count) + { + throw new ArgumentException(null, nameof(destination)); + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), vector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Create(ReadOnlySpan values) where T : struct + { + if (values.Length < Vector128.Count) + { + throw new ArgumentOutOfRangeException(nameof(values)); + } + + return Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 LoadUnsafe(ref readonly T source) where T : struct + { + // Use with caution. + // Supports blittable primitives only. + ref byte address = ref Unsafe.As(ref Unsafe.AsRef(in source)); + return Unsafe.ReadUnaligned>(ref address); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 LoadUnsafe(ref readonly T source, nuint elementOffset) where T : struct + { + // Use with caution. + // Supports blittable primitives only. + ref byte address = ref Unsafe.As(ref Unsafe.Add(ref Unsafe.AsRef(in source), (nint)elementOffset)); + return Unsafe.ReadUnaligned>(ref address); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreUnsafe(this Vector128 source, ref T destination) where T : struct + { + // Use with caution. + // Supports blittable primitives only. + ref byte address = ref Unsafe.As(ref destination); + Unsafe.WriteUnaligned(ref address, source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreUnsafe(this Vector128 source, ref T destination, nuint elementOffset) where T : struct + { + // Use with caution. + // Supports blittable primitives only. + destination = ref Unsafe.Add(ref destination, (nint)elementOffset); + Unsafe.WriteUnaligned(ref Unsafe.As(ref destination), source); + } +} +#endif