diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index ca7530d8f66..cb88a1ae0d9 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -8,6 +8,7 @@ + @@ -58,7 +59,6 @@ - @@ -76,4 +76,4 @@ - \ No newline at end of file + diff --git a/src/Nethermind/Nethermind.Crypto/AesEngineX86Intrinsic.cs b/src/Nethermind/Nethermind.Crypto/AesEngineX86Intrinsic.cs deleted file mode 100644 index 1f5751eaf3e..00000000000 --- a/src/Nethermind/Nethermind.Crypto/AesEngineX86Intrinsic.cs +++ /dev/null @@ -1,425 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only -// Modified from BouncyCastle MIT - -#pragma warning disable CA1857 // A constant is expected for the parameter - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; - -using Aes = System.Runtime.Intrinsics.X86.Aes; -using Sse2 = System.Runtime.Intrinsics.X86.Sse2; - -namespace Nethermind.Crypto; - -public sealed class AesEngineX86Intrinsic : IBlockCipher -{ - public static bool IsSupported => Aes.IsSupported; - public bool IsPartialBlockOkay => false; - public void Reset() { } - - public AesEngineX86Intrinsic() - { - if (!IsSupported) - throw new PlatformNotSupportedException(nameof(AesEngineX86Intrinsic)); - } - - public string AlgorithmName => "AES"; - - public int GetBlockSize() => 16; - - private AesEncoderDecoder _implementation; - - public void Init(bool forEncryption, ICipherParameters parameters) - { - if (parameters is not KeyParameter keyParameter) - { - ArgumentNullException.ThrowIfNull(parameters, nameof(parameters)); - throw new ArgumentException("invalid type: " + parameters.GetType(), nameof(parameters)); - } - - Vector128[] roundKeys = CreateRoundKeys(keyParameter.GetKey(), forEncryption); - _implementation = AesEncoderDecoder.Init(forEncryption, roundKeys); - } - - public int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) - { - Check.DataLength(inBuf, inOff, 16); - Check.OutputLength(outBuf, outOff, 16); - - Vector128 state = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(inBuf), inOff)); - - _implementation.ProcessRounds(ref state); - - Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(outBuf), outOff)) = state; - - return 16; - } - - private static Vector128[] CreateRoundKeys(byte[] key, bool forEncryption) - { - Vector128[] K = key.Length switch - { - 16 => KeyLength16(key), - 24 => KeyLength24(key), - 32 => KeyLength32(key), - _ => throw new ArgumentException("Key length not 128/192/256 bits.") - }; - - if (!forEncryption) - { - for (int i = 1, last = K.Length - 1; i < last; ++i) - { - K[i] = Aes.InverseMixColumns(K[i]); - } - - Array.Reverse(K); - } - - return K; - - [SkipLocalsInit] - static Vector128[] KeyLength16(byte[] key) - { - ReadOnlySpan rcon = stackalloc byte[] { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; - - Vector128 s = MemoryMarshal.Read>(key.AsSpan(0, 16)); - Vector128[] K = new Vector128[11]; - K[0] = s; - - for (int round = 0; round < 10;) - { - Vector128 t = Aes.KeygenAssist(s, rcon[round++]); - t = Sse2.Shuffle(t.AsInt32(), 0xFF).AsByte(); - s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 8)); - s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 4)); - s = Sse2.Xor(s, t); - K[round] = s; - } - - return K; - } - - static Vector128[] KeyLength24(byte[] key) - { - Vector128 s1 = MemoryMarshal.Read>(key.AsSpan(0, 16)); - Vector128 s2 = MemoryMarshal.Read>(key.AsSpan(16, 8)).ToVector128(); - Vector128[] K = new Vector128[13]; - K[0] = s1; - - byte rcon = 0x01; - for (int round = 0; ;) - { - Vector128 t1 = Aes.KeygenAssist(s2, rcon); rcon <<= 1; - t1 = Sse2.Shuffle(t1.AsInt32(), 0x55).AsByte(); - - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 8)); - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 4)); - s1 = Sse2.Xor(s1, t1); - - K[++round] = Sse2.Xor(s2, Sse2.ShiftLeftLogical128BitLane(s1, 8)); - - Vector128 s3 = Sse2.Xor(s2, Sse2.ShiftRightLogical128BitLane(s1, 12)); - s3 = Sse2.Xor(s3, Sse2.ShiftLeftLogical128BitLane(s3, 4)); - - K[++round] = Sse2.Xor( - Sse2.ShiftRightLogical128BitLane(s1, 8), - Sse2.ShiftLeftLogical128BitLane(s3, 8)); - - Vector128 t2 = Aes.KeygenAssist(s3, rcon); rcon <<= 1; - t2 = Sse2.Shuffle(t2.AsInt32(), 0x55).AsByte(); - - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 8)); - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 4)); - s1 = Sse2.Xor(s1, t2); - - K[++round] = s1; - - if (round == 12) - break; - - s2 = Sse2.Xor(s3, Sse2.ShiftRightLogical128BitLane(s1, 12)); - s2 = Sse2.Xor(s2, Sse2.ShiftLeftLogical128BitLane(s2, 4)); - s2 = s2.WithUpper(Vector64.Zero); - } - - return K; - } - - static Vector128[] KeyLength32(byte[] key) - { - Vector128 s1 = MemoryMarshal.Read>(key.AsSpan(0, 16)); - Vector128 s2 = MemoryMarshal.Read>(key.AsSpan(16, 16)); - Vector128[] K = new Vector128[15]; - K[0] = s1; - K[1] = s2; - - byte rcon = 0x01; - for (int round = 1; ;) - { - Vector128 t1 = Aes.KeygenAssist(s2, rcon); rcon <<= 1; - t1 = Sse2.Shuffle(t1.AsInt32(), 0xFF).AsByte(); - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 8)); - s1 = Sse2.Xor(s1, Sse2.ShiftLeftLogical128BitLane(s1, 4)); - s1 = Sse2.Xor(s1, t1); - K[++round] = s1; - - if (round == 14) - break; - - Vector128 t2 = Aes.KeygenAssist(s1, 0x00); - t2 = Sse2.Shuffle(t2.AsInt32(), 0xAA).AsByte(); - s2 = Sse2.Xor(s2, Sse2.ShiftLeftLogical128BitLane(s2, 8)); - s2 = Sse2.Xor(s2, Sse2.ShiftLeftLogical128BitLane(s2, 4)); - s2 = Sse2.Xor(s2, t2); - K[++round] = s2; - } - - return K; - } - } - - private abstract class AesEncoderDecoder - { - protected readonly Vector128[] _roundKeys; - - public AesEncoderDecoder(Vector128[] roundKeys) - { - _roundKeys = roundKeys; - } - - public static AesEncoderDecoder Init(bool forEncryption, Vector128[] roundKeys) - { - if (roundKeys.Length == 11) - { - return forEncryption ? new Encode128(roundKeys) : new Decode128(roundKeys); - } - else if (roundKeys.Length == 13) - { - return forEncryption ? new Encode192(roundKeys) : new Decode192(roundKeys); - } - else - { - return forEncryption ? new Encode256(roundKeys) : new Decode256(roundKeys); - } - } - - public abstract void ProcessRounds(ref Vector128 state); - - private sealed class Encode128 : AesEncoderDecoder - { - public Encode128(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[10]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Encrypt(state2, roundKeys[1]); - state2 = Aes.Encrypt(state2, roundKeys[2]); - state2 = Aes.Encrypt(state2, roundKeys[3]); - state2 = Aes.Encrypt(state2, roundKeys[4]); - state2 = Aes.Encrypt(state2, roundKeys[5]); - state2 = Aes.Encrypt(state2, roundKeys[6]); - state2 = Aes.Encrypt(state2, roundKeys[7]); - state2 = Aes.Encrypt(state2, roundKeys[8]); - state2 = Aes.Encrypt(state2, roundKeys[9]); - state2 = Aes.EncryptLast(state2, roundKeys[10]); - // Copy back to ref - state = state2; - } - } - - private sealed class Decode128 : AesEncoderDecoder - { - public Decode128(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[10]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Decrypt(state2, roundKeys[1]); - state2 = Aes.Decrypt(state2, roundKeys[2]); - state2 = Aes.Decrypt(state2, roundKeys[3]); - state2 = Aes.Decrypt(state2, roundKeys[4]); - state2 = Aes.Decrypt(state2, roundKeys[5]); - state2 = Aes.Decrypt(state2, roundKeys[6]); - state2 = Aes.Decrypt(state2, roundKeys[7]); - state2 = Aes.Decrypt(state2, roundKeys[8]); - state2 = Aes.Decrypt(state2, roundKeys[9]); - state2 = Aes.DecryptLast(state2, roundKeys[10]); - // Copy back to ref - state = state2; - } - } - - private sealed class Encode192 : AesEncoderDecoder - { - public Encode192(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[12]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Encrypt(state2, roundKeys[1]); - state2 = Aes.Encrypt(state2, roundKeys[2]); - state2 = Aes.Encrypt(state2, roundKeys[3]); - state2 = Aes.Encrypt(state2, roundKeys[4]); - state2 = Aes.Encrypt(state2, roundKeys[5]); - state2 = Aes.Encrypt(state2, roundKeys[6]); - state2 = Aes.Encrypt(state2, roundKeys[7]); - state2 = Aes.Encrypt(state2, roundKeys[8]); - state2 = Aes.Encrypt(state2, roundKeys[9]); - state2 = Aes.Encrypt(state2, roundKeys[10]); - state2 = Aes.Encrypt(state2, roundKeys[11]); - state2 = Aes.EncryptLast(state2, roundKeys[12]); - // Copy back to ref - state = state2; - } - } - - private sealed class Decode192 : AesEncoderDecoder - { - public Decode192(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[12]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Decrypt(state2, roundKeys[1]); - state2 = Aes.Decrypt(state2, roundKeys[2]); - state2 = Aes.Decrypt(state2, roundKeys[3]); - state2 = Aes.Decrypt(state2, roundKeys[4]); - state2 = Aes.Decrypt(state2, roundKeys[5]); - state2 = Aes.Decrypt(state2, roundKeys[6]); - state2 = Aes.Decrypt(state2, roundKeys[7]); - state2 = Aes.Decrypt(state2, roundKeys[8]); - state2 = Aes.Decrypt(state2, roundKeys[9]); - state2 = Aes.Decrypt(state2, roundKeys[10]); - state2 = Aes.Decrypt(state2, roundKeys[11]); - state2 = Aes.DecryptLast(state2, roundKeys[12]); - // Copy back to ref - state = state2; - } - } - - private sealed class Encode256 : AesEncoderDecoder - { - public Encode256(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[14]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Encrypt(state2, roundKeys[1]); - state2 = Aes.Encrypt(state2, roundKeys[2]); - state2 = Aes.Encrypt(state2, roundKeys[3]); - state2 = Aes.Encrypt(state2, roundKeys[4]); - state2 = Aes.Encrypt(state2, roundKeys[5]); - state2 = Aes.Encrypt(state2, roundKeys[6]); - state2 = Aes.Encrypt(state2, roundKeys[7]); - state2 = Aes.Encrypt(state2, roundKeys[8]); - state2 = Aes.Encrypt(state2, roundKeys[9]); - state2 = Aes.Encrypt(state2, roundKeys[10]); - state2 = Aes.Encrypt(state2, roundKeys[11]); - state2 = Aes.Encrypt(state2, roundKeys[12]); - state2 = Aes.Encrypt(state2, roundKeys[13]); - state2 = Aes.EncryptLast(state2, roundKeys[14]); - // Copy back to ref - state = state2; - } - } - - private sealed class Decode256 : AesEncoderDecoder - { - public Decode256(Vector128[] roundKeys) : base(roundKeys) { } - - public override void ProcessRounds(ref Vector128 state) - { - // Take local reference to array so Jit can reason length doesn't change in method - Vector128[] roundKeys = _roundKeys; - { - // Get the Jit to bounds check once rather than each increasing array access - Vector128 temp = roundKeys[14]; - } - - // Operate on non-ref local so it remains in register rather than operating on memory - Vector128 state2 = Sse2.Xor(state, roundKeys[0]); - state2 = Aes.Decrypt(state2, roundKeys[1]); - state2 = Aes.Decrypt(state2, roundKeys[2]); - state2 = Aes.Decrypt(state2, roundKeys[3]); - state2 = Aes.Decrypt(state2, roundKeys[4]); - state2 = Aes.Decrypt(state2, roundKeys[5]); - state2 = Aes.Decrypt(state2, roundKeys[6]); - state2 = Aes.Decrypt(state2, roundKeys[7]); - state2 = Aes.Decrypt(state2, roundKeys[8]); - state2 = Aes.Decrypt(state2, roundKeys[9]); - state2 = Aes.Decrypt(state2, roundKeys[10]); - state2 = Aes.Decrypt(state2, roundKeys[11]); - state2 = Aes.Decrypt(state2, roundKeys[12]); - state2 = Aes.Decrypt(state2, roundKeys[13]); - state2 = Aes.DecryptLast(state2, roundKeys[14]); - // Copy back to ref - state = state2; - } - } - } - - private static class Check - { - public static void DataLength(byte[] buf, int off, int len) - { - if (off > (buf.Length - len)) ThrowDataLengthException(); - - static void ThrowDataLengthException() => throw new DataLengthException("input buffer too short"); - } - - public static void OutputLength(byte[] buf, int off, int len) - { - if (off > (buf.Length - len)) ThrowOutputLengthException(); - - static void ThrowOutputLengthException() => throw new OutputLengthException("output buffer too short"); - } - } -} - -#pragma warning restore CA1857 // A constant is expected for the parameter diff --git a/src/Nethermind/Nethermind.Crypto/EciesCipher.cs b/src/Nethermind/Nethermind.Crypto/EciesCipher.cs index 078ccd9666b..7e9f1d89409 100644 --- a/src/Nethermind/Nethermind.Crypto/EciesCipher.cs +++ b/src/Nethermind/Nethermind.Crypto/EciesCipher.cs @@ -6,91 +6,89 @@ using Nethermind.Core.Extensions; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; -namespace Nethermind.Crypto +namespace Nethermind.Crypto; + +/// +/// Code adapted from ethereumJ (https://github.com/ethereum/ethereumj) +/// +public class EciesCipher : IEciesCipher { - /// - /// Code adapted from ethereumJ (https://github.com/ethereum/ethereumj) - /// - public class EciesCipher : IEciesCipher - { - private const int KeySize = 128; - private readonly ICryptoRandom _cryptoRandom; - private readonly PrivateKeyGenerator _keyGenerator; + private const int KeySize = 128; + private readonly ICryptoRandom _cryptoRandom; + private readonly PrivateKeyGenerator _keyGenerator; - public EciesCipher(ICryptoRandom cryptoRandom) - { - _cryptoRandom = cryptoRandom; - _keyGenerator = new PrivateKeyGenerator(cryptoRandom); - } + public EciesCipher(ICryptoRandom cryptoRandom) + { + _cryptoRandom = cryptoRandom; + _keyGenerator = new PrivateKeyGenerator(cryptoRandom); + } - private static readonly int ephemBytesLength = 2 * ((BouncyCrypto.DomainParameters.Curve.FieldSize + 7) / 8) + 1; + private static readonly int ephemBytesLength = 2 * ((BouncyCrypto.DomainParameters.Curve.FieldSize + 7) / 8) + 1; - public (bool, byte[]) Decrypt(PrivateKey privateKey, byte[] cipherText, byte[]? macData = null) + public (bool, byte[]) Decrypt(PrivateKey privateKey, byte[] cipherText, byte[]? macData = null) + { + if (cipherText[0] != 4) // if not a compressed public key then probably we need to use EIP8 { - if (cipherText[0] != 4) // if not a compressed public key then probably we need to use EIP8 - { - return (false, null); - } + return (false, null); + } - Span ephemBytes = cipherText.AsSpan(0, ephemBytesLength); - byte[] iv = cipherText.Slice(ephemBytesLength, KeySize / 8); - byte[] cipherBody = cipherText.Slice(ephemBytesLength + KeySize / 8); + Span ephemBytes = cipherText.AsSpan(0, ephemBytesLength); + byte[] iv = cipherText.Slice(ephemBytesLength, KeySize / 8); + byte[] cipherBody = cipherText.Slice(ephemBytesLength + KeySize / 8); - byte[] plaintext = Decrypt(new PublicKey(ephemBytes), privateKey, iv, cipherBody, macData); - return (true, plaintext); - } + byte[] plaintext = Decrypt(new PublicKey(ephemBytes), privateKey, iv, cipherBody, macData); + return (true, plaintext); + } - public byte[] Encrypt(PublicKey recipientPublicKey, byte[] plainText, byte[] macData) - { - byte[] iv = _cryptoRandom.GenerateRandomBytes(KeySize / 8); - PrivateKey ephemeralPrivateKey = _keyGenerator.Generate(); - IIesEngine iesEngine = MakeIesEngine(true, recipientPublicKey, ephemeralPrivateKey, iv); - byte[] cipher = iesEngine.ProcessBlock(plainText, 0, plainText.Length, macData); + public byte[] Encrypt(PublicKey recipientPublicKey, byte[] plainText, byte[] macData) + { + byte[] iv = _cryptoRandom.GenerateRandomBytes(KeySize / 8); + PrivateKey ephemeralPrivateKey = _keyGenerator.Generate(); + IIesEngine iesEngine = MakeIesEngine(true, recipientPublicKey, ephemeralPrivateKey, iv); + byte[] cipher = iesEngine.ProcessBlock(plainText, 0, plainText.Length, macData); - byte[] prefixedBytes = ephemeralPrivateKey.PublicKey.PrefixedBytes; + byte[] prefixedBytes = ephemeralPrivateKey.PublicKey.PrefixedBytes; - byte[] outputArray = new byte[prefixedBytes.Length + iv.Length + cipher.Length]; - Span outputSpan = outputArray; + byte[] outputArray = new byte[prefixedBytes.Length + iv.Length + cipher.Length]; + Span outputSpan = outputArray; - prefixedBytes.AsSpan().CopyTo(outputSpan); - outputSpan = outputSpan[prefixedBytes.Length..]; + prefixedBytes.AsSpan().CopyTo(outputSpan); + outputSpan = outputSpan[prefixedBytes.Length..]; - iv.AsSpan().CopyTo(outputSpan); - outputSpan = outputSpan[iv.Length..]; + iv.AsSpan().CopyTo(outputSpan); + outputSpan = outputSpan[iv.Length..]; - cipher.AsSpan().CopyTo(outputSpan); + cipher.AsSpan().CopyTo(outputSpan); - return outputArray; - } + return outputArray; + } - private readonly OptimizedKdf _optimizedKdf = new(); + private readonly OptimizedKdf _optimizedKdf = new(); - private static byte[] Decrypt(PublicKey ephemeralPublicKey, PrivateKey privateKey, byte[] iv, byte[] ciphertextBody, byte[] macData) - { - IIesEngine iesEngine = MakeIesEngine(false, ephemeralPublicKey, privateKey, iv); - return iesEngine.ProcessBlock(ciphertextBody, 0, ciphertextBody.Length, macData); - } + private static byte[] Decrypt(PublicKey ephemeralPublicKey, PrivateKey privateKey, byte[] iv, byte[] ciphertextBody, byte[] macData) + { + IIesEngine iesEngine = MakeIesEngine(false, ephemeralPublicKey, privateKey, iv); + return iesEngine.ProcessBlock(ciphertextBody, 0, ciphertextBody.Length, macData); + } - private static readonly IesParameters _iesParameters = new IesWithCipherParameters(Array.Empty(), Array.Empty(), KeySize, KeySize); + private static readonly IesParameters _iesParameters = new IesWithCipherParameters(Array.Empty(), Array.Empty(), KeySize, KeySize); - private static IIesEngine MakeIesEngine(bool isEncrypt, PublicKey publicKey, PrivateKey privateKey, byte[] iv) - { - IBlockCipher aesFastEngine = AesEngineX86Intrinsic.IsSupported ? new AesEngineX86Intrinsic() : new AesEngine(); + private static IIesEngine MakeIesEngine(bool isEncrypt, PublicKey publicKey, PrivateKey privateKey, byte[] iv) + { + IBlockCipher aesFastEngine = AesUtilities.CreateEngine(); - EthereumIesEngine iesEngine = new( - new HMac(new Sha256Digest()), - new Sha256Digest(), - new BufferedBlockCipher(new SicBlockCipher(aesFastEngine))); + EthereumIesEngine iesEngine = new( + new HMac(new Sha256Digest()), + new Sha256Digest(), + new BufferedBlockCipher(new SicBlockCipher(aesFastEngine))); - ParametersWithIV parametersWithIV = new(_iesParameters, iv); - byte[] secret = SecP256k1.EcdhSerialized(publicKey.Bytes, privateKey.KeyBytes); - iesEngine.Init(isEncrypt, OptimizedKdf.Derive(secret), parametersWithIV); - return iesEngine; - } + ParametersWithIV parametersWithIV = new(_iesParameters, iv); + byte[] secret = SecP256k1.EcdhSerialized(publicKey.Bytes, privateKey.KeyBytes); + iesEngine.Init(isEncrypt, OptimizedKdf.Derive(secret), parametersWithIV); + return iesEngine; } } diff --git a/src/Nethermind/Nethermind.Crypto/EthereumIesEngine.cs b/src/Nethermind/Nethermind.Crypto/EthereumIesEngine.cs index 5520deab0fc..d50a2631c97 100644 --- a/src/Nethermind/Nethermind.Crypto/EthereumIesEngine.cs +++ b/src/Nethermind/Nethermind.Crypto/EthereumIesEngine.cs @@ -158,7 +158,7 @@ private byte[] DecryptBlock(byte[] inEnc, int inOff, int inLen, byte[]? macData) _mac.DoFinal(t2, 0); - if (!Arrays.ConstantTimeAreEqual(t1, t2)) + if (!Arrays.FixedTimeEquals(t1, t2)) { throw new InvalidCipherTextException("Invalid MAC."); } diff --git a/src/Nethermind/Nethermind.Crypto/Nethermind.Crypto.csproj b/src/Nethermind/Nethermind.Crypto/Nethermind.Crypto.csproj index 0c5702026d5..cbd9d76af1a 100644 --- a/src/Nethermind/Nethermind.Crypto/Nethermind.Crypto.csproj +++ b/src/Nethermind/Nethermind.Crypto/Nethermind.Crypto.csproj @@ -11,10 +11,10 @@ + - diff --git a/src/Nethermind/Nethermind.Network/Rlpx/FrameCipher.cs b/src/Nethermind/Nethermind.Network/Rlpx/FrameCipher.cs index 7d5822f8d75..10cb64da1a9 100644 --- a/src/Nethermind/Nethermind.Network/Rlpx/FrameCipher.cs +++ b/src/Nethermind/Nethermind.Network/Rlpx/FrameCipher.cs @@ -2,46 +2,41 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Diagnostics; - -using Nethermind.Crypto; - using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; -namespace Nethermind.Network.Rlpx +namespace Nethermind.Network.Rlpx; + +public class FrameCipher : IFrameCipher { - public class FrameCipher : IFrameCipher - { - private const int BlockSize = 16; - private const int KeySize = 32; + private const int BlockSize = 16; + private const int KeySize = 32; - private readonly IBufferedCipher _decryptionCipher; - private readonly IBufferedCipher _encryptionCipher; + private readonly IBufferedCipher _decryptionCipher; + private readonly IBufferedCipher _encryptionCipher; - public FrameCipher(byte[] aesKey) - { - IBlockCipher aes = AesEngineX86Intrinsic.IsSupported ? new AesEngineX86Intrinsic() : new AesEngine(); + public FrameCipher(byte[] aesKey) + { + IBlockCipher aes = AesUtilities.CreateEngine(); - Debug.Assert(aesKey.Length == KeySize, $"AES key expected to be {KeySize} bytes long"); + Debug.Assert(aesKey.Length == KeySize, $"AES key expected to be {KeySize} bytes long"); - _encryptionCipher = new BufferedBlockCipher(new SicBlockCipher(aes)); - _encryptionCipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", aesKey), new byte[BlockSize])); + _encryptionCipher = new BufferedBlockCipher(new SicBlockCipher(aes)); + _encryptionCipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", aesKey), new byte[BlockSize])); - _decryptionCipher = new BufferedBlockCipher(new SicBlockCipher(aes)); - _decryptionCipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", aesKey), new byte[BlockSize])); - } + _decryptionCipher = new BufferedBlockCipher(new SicBlockCipher(aes)); + _decryptionCipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", aesKey), new byte[BlockSize])); + } - public void Encrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) - { - _encryptionCipher.ProcessBytes(input, offset, length, output, outputOffset); - } + public void Encrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) + { + _encryptionCipher.ProcessBytes(input, offset, length, output, outputOffset); + } - public void Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) - { - _decryptionCipher.ProcessBytes(input, offset, length, output, outputOffset); - } + public void Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) + { + _decryptionCipher.ProcessBytes(input, offset, length, output, outputOffset); } } diff --git a/src/Nethermind/Nethermind.Network/Rlpx/FrameMacProcessor.cs b/src/Nethermind/Nethermind.Network/Rlpx/FrameMacProcessor.cs index 990637126e2..f4d9ef42f57 100644 --- a/src/Nethermind/Nethermind.Network/Rlpx/FrameMacProcessor.cs +++ b/src/Nethermind/Nethermind.Network/Rlpx/FrameMacProcessor.cs @@ -4,219 +4,215 @@ using System; using System.IO; using Nethermind.Core.Crypto; -using Nethermind.Crypto; - using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; -namespace Nethermind.Network.Rlpx +namespace Nethermind.Network.Rlpx; + +/// +/// partially adapted from ethereumJ +/// +public sealed class FrameMacProcessor : IFrameMacProcessor { - /// - /// partially adapted from ethereumJ - /// - public sealed class FrameMacProcessor : IFrameMacProcessor + private readonly PublicKey _remoteNodeId; + private readonly KeccakHash _egressMac; + private readonly KeccakHash _ingressMac; + private readonly KeccakHash _egressMacCopy; + private readonly KeccakHash _ingressMacCopy; + private readonly IBlockCipher _aesEngine; + private readonly byte[] _macSecret; + + public FrameMacProcessor(PublicKey remoteNodeId, EncryptionSecrets secrets) { - private readonly PublicKey _remoteNodeId; - private readonly KeccakHash _egressMac; - private readonly KeccakHash _ingressMac; - private readonly KeccakHash _egressMacCopy; - private readonly KeccakHash _ingressMacCopy; - private readonly IBlockCipher _aesEngine; - private readonly byte[] _macSecret; - - public FrameMacProcessor(PublicKey remoteNodeId, EncryptionSecrets secrets) - { - _remoteNodeId = remoteNodeId; - _macSecret = secrets.MacSecret; - _egressMac = secrets.EgressMac; - _egressMacCopy = _egressMac.Copy(); - _ingressMac = secrets.IngressMac; - _ingressMacCopy = _ingressMac.Copy(); - _aesEngine = MakeMacCipher(); - _checkMacBuffer = new byte[_ingressMac.HashSize]; - _addMacBuffer = new byte[_ingressMac.HashSize]; - _ingressAesBlockBuffer = new byte[_ingressMac.HashSize]; - _egressAesBlockBuffer = new byte[_ingressMac.HashSize]; - } + _remoteNodeId = remoteNodeId; + _macSecret = secrets.MacSecret; + _egressMac = secrets.EgressMac; + _egressMacCopy = _egressMac.Copy(); + _ingressMac = secrets.IngressMac; + _ingressMacCopy = _ingressMac.Copy(); + _aesEngine = MakeMacCipher(); + _checkMacBuffer = new byte[_ingressMac.HashSize]; + _addMacBuffer = new byte[_ingressMac.HashSize]; + _ingressAesBlockBuffer = new byte[_ingressMac.HashSize]; + _egressAesBlockBuffer = new byte[_ingressMac.HashSize]; + } - private IBlockCipher MakeMacCipher() + private IBlockCipher MakeMacCipher() + { + IBlockCipher aesFastEngine = AesUtilities.CreateEngine(); + aesFastEngine.Init(true, new KeyParameter(_macSecret)); + return aesFastEngine; + } + + public void AddMac(byte[] input, int offset, int length, bool isHeader) + { + if (isHeader) { - IBlockCipher aesFastEngine = AesEngineX86Intrinsic.IsSupported ? new AesEngineX86Intrinsic() : new AesEngine(); - aesFastEngine.Init(true, new KeyParameter(_macSecret)); - return aesFastEngine; + input.AsSpan(0, 32).CopyTo(_addMacBuffer); + UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, offset, input, offset + length, true); // TODO: confirm header is seed } - - public void AddMac(byte[] input, int offset, int length, bool isHeader) + else { - if (isHeader) - { - input.AsSpan(0, 32).CopyTo(_addMacBuffer); - UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, offset, input, offset + length, true); // TODO: confirm header is seed - } - else - { - _egressMac.Update(input.AsSpan(offset, length)); + _egressMac.Update(input.AsSpan(offset, length)); - // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) - DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed - UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, input, offset + length, true); - } + // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) + DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed + UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, input, offset + length, true); } + } + + public void UpdateEgressMac(byte[] input) + { + _egressMac.Update(input); + } - public void UpdateEgressMac(byte[] input) + public void UpdateIngressMac(byte[] input, bool isHeader) + { + if (isHeader) { - _egressMac.Update(input); + input.AsSpan().CopyTo(_checkMacBuffer.AsSpan(0, 16)); } - - public void UpdateIngressMac(byte[] input, bool isHeader) + else { - if (isHeader) - { - input.AsSpan().CopyTo(_checkMacBuffer.AsSpan(0, 16)); - } - else - { - _ingressMac.Update(input); - } + _ingressMac.Update(input); } + } + + public void CalculateMac(byte[] output) + { + DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed + UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, output, 0, true); + } - public void CalculateMac(byte[] output) + public void AddMac(byte[] input, int offset, int length, byte[] output, int outputOffset, bool isHeader) + { + if (isHeader) { - DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed - UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, output, 0, true); + input.AsSpan(0, 16).CopyTo(_addMacBuffer); + UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, offset, output, outputOffset, true); // TODO: confirm header is seed } - - public void AddMac(byte[] input, int offset, int length, byte[] output, int outputOffset, bool isHeader) + else { - if (isHeader) - { - input.AsSpan(0, 16).CopyTo(_addMacBuffer); - UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, offset, output, outputOffset, true); // TODO: confirm header is seed - } - else - { - _egressMac.Update(input.AsSpan(offset, length)); + _egressMac.Update(input.AsSpan(offset, length)); - // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) - DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed - UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, output, outputOffset, true); - } + // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) + DoFinalNoReset(_egressMac, _egressMacCopy, _addMacBuffer); // frame MAC seed + UpdateMac(_egressMac, _egressMacCopy, _addMacBuffer, 0, output, outputOffset, true); } + } - private readonly byte[] _addMacBuffer; - private readonly byte[] _checkMacBuffer; - private readonly byte[] _ingressAesBlockBuffer; - private readonly byte[] _egressAesBlockBuffer; + private readonly byte[] _addMacBuffer; + private readonly byte[] _checkMacBuffer; + private readonly byte[] _ingressAesBlockBuffer; + private readonly byte[] _egressAesBlockBuffer; - public bool CheckMac(byte[] mac, bool isHeader) + public bool CheckMac(byte[] mac, bool isHeader) + { + if (!isHeader) { - if (!isHeader) - { - DoFinalNoReset(_ingressMac, _ingressMacCopy, _checkMacBuffer); // frame MAC seed - } + DoFinalNoReset(_ingressMac, _ingressMacCopy, _checkMacBuffer); // frame MAC seed + } - byte[] aesBlock = _ingressAesBlockBuffer; - DoFinalNoReset(_ingressMac, _ingressMacCopy, aesBlock); + byte[] aesBlock = _ingressAesBlockBuffer; + DoFinalNoReset(_ingressMac, _ingressMacCopy, aesBlock); - _aesEngine.ProcessBlock(aesBlock, 0, aesBlock, 0); + _aesEngine.ProcessBlock(aesBlock, 0, aesBlock, 0); - // Note that although the mac digest size is 32 bytes, we only use 16 bytes in the computation - int length = 16; - for (int i = 0; i < length; i++) - { - aesBlock[i] ^= _checkMacBuffer[i]; - } + // Note that although the mac digest size is 32 bytes, we only use 16 bytes in the computation + int length = 16; + for (int i = 0; i < length; i++) + { + aesBlock[i] ^= _checkMacBuffer[i]; + } - _ingressMac.Update(aesBlock.AsSpan(0, length)); - byte[] result = _checkMacBuffer; - DoFinalNoReset(_ingressMac, _ingressMacCopy, result); + _ingressMac.Update(aesBlock.AsSpan(0, length)); + byte[] result = _checkMacBuffer; + DoFinalNoReset(_ingressMac, _ingressMacCopy, result); - bool isMacSame = true; - for (int i = 0; i < length; i++) + bool isMacSame = true; + for (int i = 0; i < length; i++) + { + if (mac[i] != result[i]) { - if (mac[i] != result[i]) - { - isMacSame = false; - break; - } + isMacSame = false; + break; } - - return isMacSame; } - public void CheckMac(byte[] input, int offset, int length, bool isHeader) + return isMacSame; + } + + public void CheckMac(byte[] input, int offset, int length, bool isHeader) + { + if (isHeader) { - if (isHeader) - { - input.AsSpan(0, 32).CopyTo(_checkMacBuffer); - UpdateMac(_ingressMac, _ingressMacCopy, _checkMacBuffer, offset, input, offset + length, false); - } - else - { - _ingressMac.Update(input.AsSpan(offset, length)); + input.AsSpan(0, 32).CopyTo(_checkMacBuffer); + UpdateMac(_ingressMac, _ingressMacCopy, _checkMacBuffer, offset, input, offset + length, false); + } + else + { + _ingressMac.Update(input.AsSpan(offset, length)); - // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) - DoFinalNoReset(_ingressMac, _ingressMacCopy, _checkMacBuffer); // frame MAC seed - UpdateMac(_ingressMac, _ingressMacCopy, _checkMacBuffer, 0, input, offset + length, false); - } + // frame-mac: right128 of egress-mac.update(aes(mac-secret,egress-mac) ^ right128(egress-mac.update(frame-ciphertext).digest)) + DoFinalNoReset(_ingressMac, _ingressMacCopy, _checkMacBuffer); // frame MAC seed + UpdateMac(_ingressMac, _ingressMacCopy, _checkMacBuffer, 0, input, offset + length, false); } + } - /// - /// adapted from ethereumJ - /// - private void UpdateMac(KeccakHash mac, KeccakHash macCopy, byte[] seed, int offset, byte[] output, int outOffset, bool egress) - { - byte[] aesBlock = egress ? _egressAesBlockBuffer : _ingressAesBlockBuffer; - DoFinalNoReset(mac, macCopy, aesBlock); + /// + /// adapted from ethereumJ + /// + private void UpdateMac(KeccakHash mac, KeccakHash macCopy, byte[] seed, int offset, byte[] output, int outOffset, bool egress) + { + byte[] aesBlock = egress ? _egressAesBlockBuffer : _ingressAesBlockBuffer; + DoFinalNoReset(mac, macCopy, aesBlock); - _aesEngine.ProcessBlock(aesBlock, 0, aesBlock, 0); + _aesEngine.ProcessBlock(aesBlock, 0, aesBlock, 0); - // Note that although the mac digest size is 32 bytes, we only use 16 bytes in the computation - int length = 16; - for (int i = 0; i < length; i++) - { - aesBlock[i] ^= seed[i + offset]; - } + // Note that although the mac digest size is 32 bytes, we only use 16 bytes in the computation + int length = 16; + for (int i = 0; i < length; i++) + { + aesBlock[i] ^= seed[i + offset]; + } - mac.Update(aesBlock.AsSpan(0, length)); - byte[] result = seed; - DoFinalNoReset(mac, macCopy, result); + mac.Update(aesBlock.AsSpan(0, length)); + byte[] result = seed; + DoFinalNoReset(mac, macCopy, result); - if (egress) - { - Array.Copy(result, 0, output, outOffset, length); - } - else + if (egress) + { + Array.Copy(result, 0, output, outOffset, length); + } + else + { + bool isMacSame = true; + for (int i = 0; i < length; i++) { - bool isMacSame = true; - for (int i = 0; i < length; i++) + if (output[i + outOffset] != result[i]) { - if (output[i + outOffset] != result[i]) - { - isMacSame = false; - break; - } + isMacSame = false; + break; } + } - if (!isMacSame) - { - throw new IOException($"MAC mismatch from {_remoteNodeId}"); - } + if (!isMacSame) + { + throw new IOException($"MAC mismatch from {_remoteNodeId}"); } } + } - private static void DoFinalNoReset(KeccakHash mac, KeccakHash macCopy, byte[] output) - { - macCopy.ResetTo(mac); - macCopy.UpdateFinalTo(output); - } + private static void DoFinalNoReset(KeccakHash mac, KeccakHash macCopy, byte[] output) + { + macCopy.ResetTo(mac); + macCopy.UpdateFinalTo(output); + } - public void Dispose() - { - _egressMacCopy.Reset(); - _ingressMacCopy.Reset(); - } + public void Dispose() + { + _egressMacCopy.Reset(); + _ingressMacCopy.Reset(); } }