From 893f6a5a38b5391216b1b8aaa8d1c5d795c29649 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 19 Jul 2022 15:00:46 +0700 Subject: AesX86Engine improvements --- crypto/src/crypto/Check.cs | 14 ++++ crypto/src/crypto/engines/AesX86Engine.cs | 135 +++++++++++++++++------------- crypto/src/crypto/util/Pack.cs | 31 +++++++ 3 files changed, 124 insertions(+), 56 deletions(-) diff --git a/crypto/src/crypto/Check.cs b/crypto/src/crypto/Check.cs index aacda144f..81d07e23c 100644 --- a/crypto/src/crypto/Check.cs +++ b/crypto/src/crypto/Check.cs @@ -21,5 +21,19 @@ namespace Org.BouncyCastle.Crypto if (off > (buf.Length - len)) throw new OutputLengthException(msg); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void DataLength(Span input, int len, string msg) + { + if (input.Length < len) + throw new DataLengthException(msg); + } + + internal static void OutputLength(Span output, int len, string msg) + { + if (output.Length < len) + throw new OutputLengthException(msg); + } +#endif } } diff --git a/crypto/src/crypto/engines/AesX86Engine.cs b/crypto/src/crypto/engines/AesX86Engine.cs index b07e27bcf..d16019992 100644 --- a/crypto/src/crypto/engines/AesX86Engine.cs +++ b/crypto/src/crypto/engines/AesX86Engine.cs @@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Engines using Aes = System.Runtime.Intrinsics.X86.Aes; using Sse2 = System.Runtime.Intrinsics.X86.Sse2; - public class AesX86Engine + public struct AesX86Engine : IBlockCipher { public static bool IsSupported => Aes.IsSupported; @@ -28,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Engines K = new Vector128[11]; - var s = Load128(key, 0); + var s = Load128(key.AsSpan(0, 16)); K[0] = s; for (int round = 0; round < 10;) @@ -47,8 +47,8 @@ namespace Org.BouncyCastle.Crypto.Engines { K = new Vector128[13]; - var s1 = Load128(key, 0); - var s2 = Load64(key, 16).ToVector128(); + var s1 = Load128(key.AsSpan(0, 16)); + var s2 = Load64(key.AsSpan(16, 8)).ToVector128(); K[0] = s1; byte rcon = 0x01; @@ -93,8 +93,8 @@ namespace Org.BouncyCastle.Crypto.Engines { K = new Vector128[15]; - var s1 = Load128(key, 0); - var s2 = Load128(key, 16); + var s1 = Load128(key.AsSpan(0, 16)); + var s2 = Load128(key.AsSpan(16, 16)); K[0] = s1; K[1] = s2; @@ -140,7 +140,7 @@ namespace Org.BouncyCastle.Crypto.Engines private enum Mode { DEC_128, DEC_192, DEC_256, ENC_128, ENC_192, ENC_256, UNINITIALIZED }; - private Vector128[] m_roundKeys; + private Vector128[] m_roundKeys = null; private Mode m_mode = Mode.UNINITIALIZED; public AesX86Engine() @@ -149,11 +149,19 @@ namespace Org.BouncyCastle.Crypto.Engines throw new PlatformNotSupportedException(nameof(AesX86Engine)); } - public virtual void Init(bool forEncryption, ICipherParameters parameters) + public string AlgorithmName => "AES"; + + public bool IsPartialBlockOkay => false; + + public int GetBlockSize() => 16; + + public void Init(bool forEncryption, ICipherParameters parameters) { if (!(parameters is KeyParameter keyParameter)) - throw new ArgumentException( - "invalid parameter passed to AES Init - " + Platform.GetTypeName(parameters)); + { + ArgumentNullException.ThrowIfNull(parameters, nameof(parameters)); + throw new ArgumentException("invalid type: " + Platform.GetTypeName(parameters), nameof(parameters)); + } m_roundKeys = CreateRoundKeys(keyParameter.GetKey(), forEncryption); @@ -171,39 +179,50 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public virtual string AlgorithmName => "AES"; - - public virtual bool IsPartialBlockOkay => false; + public int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + { + Check.DataLength(inBuf, inOff, 16, "input buffer too short"); + Check.OutputLength(outBuf, outOff, 16, "output buffer too short"); - public virtual int GetBlockSize() => 16; + var state = Load128(inBuf.AsSpan(inOff, 16)); + ImplRounds(ref state); + Store128(ref state, outBuf.AsSpan(outOff, 16)); + return 16; + } - public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + public int ProcessBlock(Span input, Span output) { - Check.DataLength(input, inOff, 16, "input buffer too short"); - Check.OutputLength(output, outOff, 16, "output buffer too short"); - - switch (m_mode) - { - case Mode.DEC_128: Decrypt128(input, inOff, output, outOff, m_roundKeys); break; - case Mode.DEC_192: Decrypt192(input, inOff, output, outOff, m_roundKeys); break; - case Mode.DEC_256: Decrypt256(input, inOff, output, outOff, m_roundKeys); break; - case Mode.ENC_128: Encrypt128(input, inOff, output, outOff, m_roundKeys); break; - case Mode.ENC_192: Encrypt192(input, inOff, output, outOff, m_roundKeys); break; - case Mode.ENC_256: Encrypt256(input, inOff, output, outOff, m_roundKeys); break; - default: throw new InvalidOperationException("AES engine not initialised"); - } + Check.DataLength(input, 16, "input buffer too short"); + Check.OutputLength(output, 16, "output buffer too short"); + var state = Load128(input); + ImplRounds(ref state); + Store128(ref state, output); return 16; } - public virtual void Reset() + public void Reset() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Decrypt128(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private void ImplRounds(ref Vector128 state) + { + switch (m_mode) + { + case Mode.DEC_128: Decrypt128(ref state, m_roundKeys); break; + case Mode.DEC_192: Decrypt192(ref state, m_roundKeys); break; + case Mode.DEC_256: Decrypt256(ref state, m_roundKeys); break; + case Mode.ENC_128: Encrypt128(ref state, m_roundKeys); break; + case Mode.ENC_192: Encrypt192(ref state, m_roundKeys); break; + case Mode.ENC_256: Encrypt256(ref state, m_roundKeys); break; + default: throw new InvalidOperationException(nameof(AesX86Engine) + " not initialised"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Decrypt128(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Decrypt(state, roundKeys[1]); state = Aes.Decrypt(state, roundKeys[2]); @@ -215,13 +234,11 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Decrypt(state, roundKeys[8]); state = Aes.Decrypt(state, roundKeys[9]); state = Aes.DecryptLast(state, roundKeys[10]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Decrypt192(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private static void Decrypt192(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Decrypt(state, roundKeys[1]); state = Aes.Decrypt(state, roundKeys[2]); @@ -235,13 +252,11 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Decrypt(state, roundKeys[10]); state = Aes.Decrypt(state, roundKeys[11]); state = Aes.DecryptLast(state, roundKeys[12]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Decrypt256(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private static void Decrypt256(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Decrypt(state, roundKeys[1]); state = Aes.Decrypt(state, roundKeys[2]); @@ -257,13 +272,11 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Decrypt(state, roundKeys[12]); state = Aes.Decrypt(state, roundKeys[13]); state = Aes.DecryptLast(state, roundKeys[14]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encrypt128(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private static void Encrypt128(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Encrypt(state, roundKeys[1]); state = Aes.Encrypt(state, roundKeys[2]); @@ -275,13 +288,11 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Encrypt(state, roundKeys[8]); state = Aes.Encrypt(state, roundKeys[9]); state = Aes.EncryptLast(state, roundKeys[10]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encrypt192(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private static void Encrypt192(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Encrypt(state, roundKeys[1]); state = Aes.Encrypt(state, roundKeys[2]); @@ -295,13 +306,11 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Encrypt(state, roundKeys[10]); state = Aes.Encrypt(state, roundKeys[11]); state = Aes.EncryptLast(state, roundKeys[12]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encrypt256(byte[] input, int inOff, byte[] output, int outOff, Vector128[] roundKeys) + private static void Encrypt256(ref Vector128 state, Vector128[] roundKeys) { - var state = Load128(input, inOff); state = Sse2.Xor(state, roundKeys[0]); state = Aes.Encrypt(state, roundKeys[1]); state = Aes.Encrypt(state, roundKeys[2]); @@ -317,36 +326,50 @@ namespace Org.BouncyCastle.Crypto.Engines state = Aes.Encrypt(state, roundKeys[12]); state = Aes.Encrypt(state, roundKeys[13]); state = Aes.EncryptLast(state, roundKeys[14]); - Store128(ref state, output, outOff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 Load128(byte[] b, int n) + private static Vector128 Load128(Span t) { #if NET7_0_OR_GREATER - return Vector128.Create(b, n); + return Vector128.Create(t); #else - return Unsafe.ReadUnaligned>(ref b[n]); + if (BitConverter.IsLittleEndian && Unsafe.SizeOf>() == 16) + return Unsafe.ReadUnaligned>(ref t[0]); + + return Vector128.Create(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11], t[12], + t[13], t[14], t[15]); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector64 Load64(byte[] b, int n) + private static Vector64 Load64(Span t) { #if NET7_0_OR_GREATER - return Vector64.Create(b, n); + return Vector64.Create(t); #else - return Unsafe.ReadUnaligned>(ref b[n]); + if (BitConverter.IsLittleEndian && Unsafe.SizeOf>() == 8) + return Unsafe.ReadUnaligned>(ref t[0]); + + return Vector64.Create(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7]); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Store128(ref Vector128 s, byte[] b, int n) + private static void Store128(ref Vector128 s, Span t) { #if NET7_0_OR_GREATER - Vector128.CopyTo(s, b, n); + Vector128.CopyTo(s, t); #else - Unsafe.WriteUnaligned(ref b[n], s); + if (BitConverter.IsLittleEndian && Unsafe.SizeOf>() == 16) + { + Unsafe.WriteUnaligned(ref t[0], s); + return; + } + + var u = s.AsUInt64(); + Utilities.Pack.UInt64_To_LE(u.GetElement(0), t); + Utilities.Pack.UInt64_To_LE(u.GetElement(1), t.Slice(8)); #endif } } diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs index b7cfde905..9fd533cfc 100644 --- a/crypto/src/crypto/util/Pack.cs +++ b/crypto/src/crypto/util/Pack.cs @@ -439,5 +439,36 @@ namespace Org.BouncyCastle.Crypto.Utilities bsOff += 8; } } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static uint LE_To_UInt32(Span s) + { + return s[0] + | (uint)s[1] << 8 + | (uint)s[2] << 16 + | (uint)s[3] << 24; + } + + internal static ulong LE_To_UInt64(Span s) + { + uint lo = LE_To_UInt32(s); + uint hi = LE_To_UInt32(s.Slice(4)); + return (ulong)hi << 32 | lo; + } + + internal static void UInt32_To_LE(uint n, Span s) + { + s[0] = (byte)n; + s[1] = (byte)(n >> 8); + s[2] = (byte)(n >> 16); + s[3] = (byte)(n >> 24); + } + + internal static void UInt64_To_LE(ulong n, Span s) + { + UInt32_To_LE((uint)n, s); + UInt32_To_LE((uint)(n >> 32), s.Slice(4)); + } +#endif } } -- cgit 1.4.1