diff --git a/crypto/src/crypto/IDigest.cs b/crypto/src/crypto/IDigest.cs index 36caf3728e..c705723531 100644 --- a/crypto/src/crypto/IDigest.cs +++ b/crypto/src/crypto/IDigest.cs @@ -9,41 +9,41 @@ public interface IDigest string AlgorithmName { get; } /// Return the size, in bytes, of the digest produced by this message digest. - /// the size, in bytes, of the digest produced by this message digest. + /// The size, in bytes, of the digest produced by this message digest. int GetDigestSize(); /// Return the size, in bytes, of the internal buffer used by this digest. - /// the size, in bytes, of the internal buffer used by this digest. + /// The size, in bytes, of the internal buffer used by this digest. int GetByteLength(); /// Update the message digest with a single byte. - /// the input byte to be entered. + /// The input byte to be entered. void Update(byte input); /// Update the message digest with a block of bytes. - /// the byte array containing the data. - /// the offset into the byte array where the data starts. - /// the length of the data. + /// The byte array containing the data. + /// The offset into the byte array where the data starts. + /// The length of the data. void BlockUpdate(byte[] input, int inOff, int inLen); #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// Update the message digest with a span of bytes. - /// the span containing the data. + /// The span containing the data. void BlockUpdate(ReadOnlySpan input); #endif /// Close the digest, producing the final digest value. /// This call leaves the digest reset. - /// the byte array the digest is to be copied into. - /// the offset into the byte array the digest is to start at. - /// the number of bytes written + /// The byte array the digest is to be copied into. + /// The offset into the byte array the digest is to start at. + /// The number of bytes written. int DoFinal(byte[] output, int outOff); #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// Close the digest, producing the final digest value. /// This call leaves the digest reset. - /// the span the digest is to be copied into. - /// the number of bytes written + /// The span the digest is to be copied into. + /// The number of bytes written. int DoFinal(Span output); #endif diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs index b880bc5683..8d9c95be82 100644 --- a/crypto/src/crypto/digests/Blake2bDigest.cs +++ b/crypto/src/crypto/digests/Blake2bDigest.cs @@ -30,18 +30,22 @@ This implementation does not support the Tree Hashing Mode. ---------------+--------+-----------+------+------------+ */ - /** - * Implementation of the cryptographic hash function Blake2b. - *

- * Blake2b offers a built-in keying mechanism to be used directly - * for authentication ("Prefix-MAC") rather than a HMAC construction. - *

- * Blake2b offers a built-in support for a salt for randomized hashing - * and a personal string for defining a unique hash function for each application. - *

- * BLAKE2b is optimized for 64-bit platforms and produces digests of any size - * between 1 and 64 bytes. - */ + ///

+ /// Implementation of the cryptographic hash function Blake2b. + /// BLAKE2b is optimized for 64-bit platforms and produces digests of any size + /// between 1 and 64 bytes. + /// + /// + /// + /// + /// Blake2b offers a built-in keying mechanism to be used directly + /// for authentication ("Prefix-MAC") rather than a HMAC construction. + /// + /// + /// Blake2b offers a built-in support for a salt for randomized hashing + /// and a personal string for defining a unique hash function for each application. + /// + /// public sealed class Blake2bDigest : IDigest { @@ -87,7 +91,7 @@ public sealed class Blake2bDigest // Tree hashing parameters: // Because this class does not implement the Tree Hashing Mode, // these parameters can be treated as constants (see init() function) - /* + /* * private int fanout = 1; // 0-255 private int depth = 1; // 1 - 255 * private int leafLength= 0; private long nodeOffset = 0L; private int * nodeDepth = 0; private int innerHashLength = 0; @@ -112,11 +116,18 @@ public sealed class Blake2bDigest // For Tree Hashing Mode, not used here: // private long f1 = 0L; // finalization flag, for last node: ~0L + /// + /// Initializes a new instance of . + /// public Blake2bDigest() : this(512) { } + /// + /// Constructs a new instance of from another ./>. + /// + /// The original instance of that is copied. public Blake2bDigest(Blake2bDigest digest) { this.bufferPos = digest.bufferPos; @@ -132,11 +143,11 @@ public Blake2bDigest(Blake2bDigest digest) this.f0 = digest.f0; } - /** - * Basic sized constructor - size in bits. - * - * @param digestSize size of the digest in bits - */ + /// + /// Initializes a new instance of with a given digest size. + /// + /// Digest size in bits. + /// public Blake2bDigest(int digestSize) { if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0) @@ -148,15 +159,18 @@ public Blake2bDigest(int digestSize) Init(); } - /** - * Blake2b for authentication ("Prefix-MAC mode"). - * After calling the doFinal() method, the key will - * remain to be used for further computations of - * this instance. - * The key can be overwritten using the clearKey() method. - * - * @param key A key up to 64 bytes or null - */ + /// + /// + /// Initializes a new instance of with a key. + /// + /// + /// Blake2b for authentication ("Prefix-MAC mode"). + /// After calling the method, the key will + /// remain to be used for further computations of this instance. + /// The key can be cleared using the method. + /// + /// A key up to 64 bytes or null. + /// public Blake2bDigest(byte[] key) { buffer = new byte[BLOCK_LENGTH_BYTES]; @@ -176,18 +190,21 @@ public Blake2bDigest(byte[] key) Init(); } - /** - * Blake2b with key, required digest length (in bytes), salt and personalization. - * After calling the doFinal() method, the key, the salt and the personal string - * will remain and might be used for further computations with this instance. - * The key can be overwritten using the clearKey() method, the salt (pepper) - * can be overwritten using the clearSalt() method. - * - * @param key A key up to 64 bytes or null - * @param digestLength from 1 up to 64 bytes - * @param salt 16 bytes or null - * @param personalization 16 bytes or null - */ + /// + /// + /// Initializes a new instance of with a key, required digest length (in bytes), salt and personalization. + /// + /// + /// After calling the method, the key, the salt and the personalization + /// will remain and might be used for further computations with this instance. + /// The key can be overwritten using the method, the salt (pepper) + /// can be overwritten using the method. + /// + /// A key up to 64 bytes or null. + /// Digest length from 1 to 64 bytes. + /// A 16 bytes or nullable salt. + /// A 16 bytes or null personalization. + /// public Blake2bDigest(byte[] key, int digestLength, byte[] salt, byte[] personalization) { if (digestLength < 1 || digestLength > 64) @@ -274,11 +291,7 @@ private void InitializeInternalState() internalState[15] = blake2b_IV[7];// ^ f1 with f1 = 0 } - /** - * update the message digest with a single byte. - * - * @param b the input byte to be entered. - */ + /// public void Update(byte b) { // process the buffer if full else add to buffer: @@ -306,13 +319,7 @@ public void Update(byte b) } } - /** - * update the message digest with a block of bytes. - * - * @param message the byte array containing the data. - * @param offset the offset into the byte array where the data starts. - * @param len the length of the data. - */ + /// public void BlockUpdate(byte[] message, int offset, int len) { if (message == null || len == 0) @@ -369,6 +376,7 @@ public void BlockUpdate(byte[] message, int offset, int len) } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// public void BlockUpdate(ReadOnlySpan input) { if (input.IsEmpty) @@ -421,14 +429,14 @@ public void BlockUpdate(ReadOnlySpan input) } #endif - /** - * close the digest, producing the final digest value. The doFinal - * call leaves the digest reset. - * Key, salt and personal string remain. - * - * @param out the array the digest is to be copied into. - * @param outOffset the offset into the out array the digest is to start at. - */ + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The byte array the digest is to be copied into. + /// The offset into the byte array the digest is to start at. + /// The number of bytes written. public int DoFinal(byte[] output, int outOffset) { Check.OutputLength(output, outOffset, digestLength, "output buffer too short"); @@ -464,6 +472,13 @@ public int DoFinal(byte[] output, int outOffset) } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The span the digest is to be copied into. + /// The number of bytes written. public int DoFinal(Span output) { Check.OutputLength(output, digestLength, "output buffer too short"); @@ -495,11 +510,10 @@ public int DoFinal(Span output) } #endif - /** - * Reset the digest back to it's initial state. - * The key, the salt and the personal string will - * remain for further computations. - */ + /// + /// Reset the digest back to it's initial state. + /// The key, the salt and the personalization will remain for further computations. + /// public void Reset() { bufferPos = 0; @@ -519,6 +533,13 @@ public void Reset() #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private void Compress(ReadOnlySpan message) { +#if NETCOREAPP3_0_OR_GREATER + if (Blake2b_X86.IsSupported) + { + Blake2b_X86.Compress(f0 == ulong.MaxValue, chainValue, message, t0, t1, blake2b_IV); + return; + } +#endif InitializeInternalState(); Span m = stackalloc ulong[16]; @@ -594,38 +615,28 @@ private static ulong Rotr64(ulong x, int rot) return x >> rot | x << -rot; } - /** - * return the algorithm name - * - * @return the algorithm name - */ + /// public string AlgorithmName => "BLAKE2b"; - /** - * return the size, in bytes, of the digest produced by this message digest. - * - * @return the size, in bytes, of the digest produced by this message digest. - */ + /// public int GetDigestSize() { return digestLength; } - /** - * Return the size in bytes of the internal buffer the digest applies it's compression - * function to. - * - * @return byte length of the digests internal buffer. - */ + /// + /// Return the size in bytes of the internal buffer the digest applies it's compression + /// function to. + /// + /// The byte length of the digests internal buffer. public int GetByteLength() { return BLOCK_LENGTH_BYTES; } - /** - * Overwrite the key - * if it is no longer used (zeroization) - */ + /// + /// Clears the key. + /// public void ClearKey() { if (key != null) @@ -635,10 +646,9 @@ public void ClearKey() } } - /** - * Overwrite the salt (pepper) if it - * is secret and no longer used (zeroization) - */ + /// + /// Clears the salt (pepper). + /// public void ClearSalt() { if (salt != null) diff --git a/crypto/src/crypto/digests/Blake2b_X86.cs b/crypto/src/crypto/digests/Blake2b_X86.cs new file mode 100644 index 0000000000..f121d3c1a1 --- /dev/null +++ b/crypto/src/crypto/digests/Blake2b_X86.cs @@ -0,0 +1,419 @@ +#if NETCOREAPP3_0_OR_GREATER +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Org.BouncyCastle.Crypto.Digests +{ + // License from the original code created by Clinton Ingram (saucecontrol) for Blake2Fast + // at https://github.com/saucecontrol/Blake2Fast. The code has been copied and modified. + + // The MIT License + + // Copyright(c) 2018-2021 Clinton Ingram + + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + + internal static class Blake2b_X86 + { + public static bool IsSupported => Avx2.IsSupported && BitConverter.IsLittleEndian; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Compress(bool isFinal, Span hashBuffer, ReadOnlySpan message, ulong totalSegmentsLow, ulong totalSegmentsHigh, ReadOnlySpan blakeIV) + { + if (!IsSupported) + throw new PlatformNotSupportedException(nameof(Blake2b_X86)); + + Debug.Assert(message.Length >= Unsafe.SizeOf() * 8); + Debug.Assert(hashBuffer.Length >= 8); + + var hashBytes = MemoryMarshal.AsBytes(hashBuffer); + var ivBytes = MemoryMarshal.AsBytes(blakeIV); + + var r_14 = isFinal ? ulong.MaxValue : 0; + var t_0 = Vector256.Create(totalSegmentsLow, totalSegmentsHigh, r_14, 0); + + Vector256 row1 = LoadVector256(hashBytes); + Vector256 row2 = LoadVector256(hashBytes[Vector256.Count..]); + Vector256 row3 = LoadVector256(ivBytes); + Vector256 row4 = LoadVector256(ivBytes[Vector256.Count..]); + row4 = Avx2.Xor(row4, t_0); + + Vector256 orig_1 = row1; + Vector256 orig_2 = row2; + + Perform12Rounds(message, ref row1, ref row2, ref row3, ref row4); + + row1 = Avx2.Xor(row1, row3); + row2 = Avx2.Xor(row2, row4); + row1 = Avx2.Xor(row1, orig_1); + row2 = Avx2.Xor(row2, orig_2); + + Store(row1, hashBytes); + Store(row2, hashBytes[Vector256.Count..]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Perform12Rounds(ReadOnlySpan m, ref Vector256 row1, ref Vector256 row2, ref Vector256 row3, ref Vector256 row4) + { + Debug.Assert(m.Length >= 128); + + #region Rounds + //ROUND 1 + var m0 = BroadcastVector128ToVector256(m); + var m1 = BroadcastVector128ToVector256(m[Unsafe.SizeOf>()..]); + var m2 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 2)..]); + var m3 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 3)..]); + + var t0 = Avx2.UnpackLow(m0, m1); + var t1 = Avx2.UnpackLow(m2, m3); + var b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m0, m1); + t1 = Avx2.UnpackHigh(m2, m3); + var b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + var m4 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 4)..]); + var m5 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 5)..]); + var m6 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 6)..]); + var m7 = BroadcastVector128ToVector256(m[(Unsafe.SizeOf>() * 7)..]); + + t0 = Avx2.UnpackLow(m7, m4); + t1 = Avx2.UnpackLow(m5, m6); + var b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m7, m4); + t1 = Avx2.UnpackHigh(m5, m6); + var b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 2 + t0 = Avx2.UnpackLow(m7, m2); + t1 = Avx2.UnpackHigh(m4, m6); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m5, m4); + t1 = Avx2.AlignRight(m3, m7, 8); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m2, m0); + t1 = Avx2.Blend(m0.AsUInt32(), m5.AsUInt32(), 0b_1100_1100).AsUInt64(); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m6, m1, 8); + t1 = Avx2.Blend(m1.AsUInt32(), m3.AsUInt32(), 0b_1100_1100).AsUInt64(); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 3 + t0 = Avx2.AlignRight(m6, m5, 8); + t1 = Avx2.UnpackHigh(m2, m7); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m4, m0); + t1 = Avx2.Blend(m1.AsUInt32(), m6.AsUInt32(), 0b_1100_1100).AsUInt64(); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m5, m4, 8); + t1 = Avx2.UnpackHigh(m1, m3); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m2, m7); + t1 = Avx2.Blend(m3.AsUInt32(), m0.AsUInt32(), 0b_1100_1100).AsUInt64(); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 4 + t0 = Avx2.UnpackHigh(m3, m1); + t1 = Avx2.UnpackHigh(m6, m5); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m4, m0); + t1 = Avx2.UnpackLow(m6, m7); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m1, m7, 8); + t1 = Avx2.Shuffle(m2.AsUInt32(), 0b_01_00_11_10).AsUInt64(); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m4, m3); + t1 = Avx2.UnpackLow(m5, m0); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 5 + t0 = Avx2.UnpackHigh(m4, m2); + t1 = Avx2.UnpackLow(m1, m5); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.Blend(m0.AsUInt32(), m3.AsUInt32(), 0b_1100_1100).AsUInt64(); + t1 = Avx2.Blend(m2.AsUInt32(), m7.AsUInt32(), 0b_1100_1100).AsUInt64(); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m7, m1, 8); + t1 = Avx2.AlignRight(m3, m5, 8); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m6, m0); + t1 = Avx2.UnpackLow(m6, m4); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 6 + t0 = Avx2.UnpackLow(m1, m3); + t1 = Avx2.UnpackLow(m0, m4); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m6, m5); + t1 = Avx2.UnpackHigh(m5, m1); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m2, m0, 8); + t1 = Avx2.UnpackHigh(m3, m7); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m4, m6); + t1 = Avx2.AlignRight(m7, m2, 8); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 7 + t0 = Avx2.Blend(m6.AsUInt32(), m0.AsUInt32(), 0b_1100_1100).AsUInt64(); + t1 = Avx2.UnpackLow(m7, m2); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m2, m7); + t1 = Avx2.AlignRight(m5, m6, 8); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m4, m0); + t1 = Avx2.Blend(m3.AsUInt32(), m4.AsUInt32(), 0b_1100_1100).AsUInt64(); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m5, m3); + t1 = Avx2.Shuffle(m1.AsUInt32(), 0b_01_00_11_10).AsUInt64(); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 8 + t0 = Avx2.UnpackHigh(m6, m3); + t1 = Avx2.Blend(m6.AsUInt32(), m1.AsUInt32(), 0b_1100_1100).AsUInt64(); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m7, m5, 8); + t1 = Avx2.UnpackHigh(m0, m4); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.Blend(m1.AsUInt32(), m2.AsUInt32(), 0b_1100_1100).AsUInt64(); + t1 = Avx2.AlignRight(m4, m7, 8); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m5, m0); + t1 = Avx2.UnpackLow(m2, m3); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 9 + t0 = Avx2.UnpackLow(m3, m7); + t1 = Avx2.AlignRight(m0, m5, 8); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m7, m4); + t1 = Avx2.AlignRight(m4, m1, 8); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m5, m6); + t1 = Avx2.UnpackHigh(m6, m0); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m1, m2, 8); + t1 = Avx2.AlignRight(m2, m3, 8); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 10 + t0 = Avx2.UnpackLow(m5, m4); + t1 = Avx2.UnpackHigh(m3, m0); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m1, m2); + t1 = Avx2.Blend(m3.AsUInt32(), m2.AsUInt32(), 0b_1100_1100).AsUInt64(); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m6, m7); + t1 = Avx2.UnpackHigh(m4, m1); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.Blend(m0.AsUInt32(), m5.AsUInt32(), 0b_1100_1100).AsUInt64(); + t1 = Avx2.UnpackLow(m7, m6); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 11 + t0 = Avx2.UnpackLow(m0, m1); + t1 = Avx2.UnpackLow(m2, m3); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m0, m1); + t1 = Avx2.UnpackHigh(m2, m3); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m7, m4); + t1 = Avx2.UnpackLow(m5, m6); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m7, m4); + t1 = Avx2.UnpackHigh(m5, m6); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 12 + t0 = Avx2.UnpackLow(m7, m2); + t1 = Avx2.UnpackHigh(m4, m6); + b1 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackLow(m5, m4); + t1 = Avx2.AlignRight(m3, m7, 8); + b2 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.UnpackHigh(m2, m0); + t1 = Avx2.Blend(m0.AsUInt32(), m5.AsUInt32(), 0b_1100_1100).AsUInt64(); + b3 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + t0 = Avx2.AlignRight(m6, m1, 8); + t1 = Avx2.Blend(m1.AsUInt32(), m3.AsUInt32(), 0b_1100_1100).AsUInt64(); + b4 = Avx2.Blend(t0.AsUInt32(), t1.AsUInt32(), 0b_1111_0000).AsUInt64(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + #endregion + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Round(ref Vector256 row1, ref Vector256 row2, ref Vector256 row3, ref Vector256 row4, Vector256 b1, Vector256 b2, Vector256 b3, Vector256 b4) + { + Vector256 r24 = Vector256.Create((byte)3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10); + Vector256 r16 = Vector256.Create((byte)2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9); + + G1(r24, ref row1, ref row2, ref row3, ref row4, b1); + G2(r16, ref row1, ref row2, ref row3, ref row4, b2); + + Diagonalize(ref row1, ref row3, ref row4); + + G1(r24, ref row1, ref row2, ref row3, ref row4, b3); + G2(r16, ref row1, ref row2, ref row3, ref row4, b4); + + Undiagonalize(ref row1, ref row3, ref row4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Diagonalize(ref Vector256 row1, ref Vector256 row3, ref Vector256 row4) + { + // +-------------------+ +-------------------+ + // | 0 | 1 | 2 | 3 | | 3 | 0 | 1 | 2 | + // +-------------------+ +-------------------+ + // | 8 | 9 | 10 | 11 | ---> | 9 | 10 | 11 | 8 | + // +-------------------+ +-------------------+ + // | 12 | 13 | 14 | 15 | | 14 | 15 | 12 | 13 | + // +-------------------+ +-------------------+ + + row1 = Avx2.Permute4x64(row1, 0b_10_01_00_11); + row3 = Avx2.Permute4x64(row3, 0b_00_11_10_01); + row4 = Avx2.Permute4x64(row4, 0b_01_00_11_10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void G1(Vector256 r24, ref Vector256 row1, ref Vector256 row2, ref Vector256 row3, ref Vector256 row4, Vector256 b0) + { + row1 = Avx2.Add(Avx2.Add(row1, b0), row2); + row4 = Avx2.Xor(row4, row1); + row4 = Avx2.Shuffle(row4.AsUInt32(), 0b_10_11_00_01).AsUInt64(); + + row3 = Avx2.Add(row3, row4); + row2 = Avx2.Xor(row2, row3); + row2 = Avx2.Shuffle(row2.AsByte(), r24).AsUInt64(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void G2(Vector256 r16, ref Vector256 row1, ref Vector256 row2, ref Vector256 row3, ref Vector256 row4, Vector256 b0) + { + row1 = Avx2.Add(Avx2.Add(row1, b0), row2); + row4 = Avx2.Xor(row4, row1); + row4 = Avx2.Shuffle(row4.AsByte(), r16).AsUInt64(); + + row3 = Avx2.Add(row3, row4); + row2 = Avx2.Xor(row2, row3); + row2 = Avx2.Xor(Avx2.ShiftRightLogical(row2, 63), Avx2.Add(row2, row2)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Undiagonalize(ref Vector256 row1, ref Vector256 row3, ref Vector256 row4) + { + // +-------------------+ +-------------------+ + // | 3 | 0 | 1 | 2 | | 0 | 1 | 2 | 3 | + // +-------------------+ +-------------------+ + // | 9 | 10 | 11 | 8 | ---> | 8 | 9 | 10 | 11 | + // +-------------------+ +-------------------+ + // | 14 | 15 | 12 | 13 | | 12 | 13 | 14 | 15 | + // +-------------------+ +-------------------+ + + row1 = Avx2.Permute4x64(row1, 0b_00_11_10_01); + row3 = Avx2.Permute4x64(row3, 0b_10_01_00_11); + row4 = Avx2.Permute4x64(row4, 0b_01_00_11_10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 BroadcastVector128ToVector256(ReadOnlySpan source) where T : struct + { + Debug.Assert(source.Length >= Unsafe.SizeOf>()); + + var vector = MemoryMarshal.Read>(source); + Vector256 result = vector.ToVector256Unsafe(); + return result.WithUpper(vector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 LoadVector256(ReadOnlySpan source) where T : struct + { + Debug.Assert(source.Length >= Unsafe.SizeOf>()); + return MemoryMarshal.Read>(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Store(Vector256 vector, Span destination) where T : struct + { + Debug.Assert(destination.Length >= Unsafe.SizeOf>()); + MemoryMarshal.Write(destination, ref vector); + } + } +} +#endif diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs index 591095fbf2..91d73bf620 100644 --- a/crypto/src/crypto/digests/Blake2sDigest.cs +++ b/crypto/src/crypto/digests/Blake2sDigest.cs @@ -31,18 +31,22 @@ This implementation does not support the Tree Hashing Mode. ---------------+--------+-----------+------+------------+ */ - /** - * Implementation of the cryptographic hash function BLAKE2s. - *

- * BLAKE2s offers a built-in keying mechanism to be used directly - * for authentication ("Prefix-MAC") rather than a HMAC construction. - *

- * BLAKE2s offers a built-in support for a salt for randomized hashing - * and a personal string for defining a unique hash function for each application. - *

- * BLAKE2s is optimized for 32-bit platforms and produces digests of any size - * between 1 and 32 bytes. - */ + ///

+ /// Implementation of the cryptographic hash function BLAKE2s. + /// BLAKE2s is optimized for 32-bit platforms and produces digests of any size + /// between 1 and 32 bytes. + /// + /// + /// + /// + /// BLAKE2s offers a built-in keying mechanism to be used directly + /// for authentication ("Prefix-MAC") rather than a HMAC construction. + /// + /// + /// BLAKE2s is optimized for 32-bit platforms and produces digests of any size + /// between 1 and 32 bytes. + /// + /// public sealed class Blake2sDigest : IDigest { @@ -130,14 +134,18 @@ public sealed class Blake2sDigest // For Tree Hashing Mode, not used here: // private long f1 = 0L; // finalization flag, for last node: ~0L - /** - * BLAKE2s-256 for hashing. - */ + /// + /// Initializes a new instance of . + /// public Blake2sDigest() : this(256) { } + /// + /// Constructs a new instance of from another ./>. + /// + /// The original instance of that is copied. public Blake2sDigest(Blake2sDigest digest) { this.bufferPos = digest.bufferPos; @@ -160,11 +168,11 @@ public Blake2sDigest(Blake2sDigest digest) this.innerHashLength = digest.innerHashLength; } - /** - * BLAKE2s for hashing. - * - * @param digestBits the desired digest length in bits. Must be a multiple of 8 and less than 256. - */ + /// + /// Initializes a new instance of with a given digest size. + /// + /// Digest size in bits. + /// public Blake2sDigest(int digestBits) { if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0) @@ -175,33 +183,38 @@ public Blake2sDigest(int digestBits) Init(null, null, null); } - /** - * BLAKE2s for authentication ("Prefix-MAC mode"). - *

- * After calling the doFinal() method, the key will remain to be used for - * further computations of this instance. The key can be overwritten using - * the clearKey() method. - * - * @param key a key up to 32 bytes or null - */ + ///

+ /// + /// Initializes a new instance of with a key. + /// + /// + /// Blake2s for authentication ("Prefix-MAC mode"). + /// After calling the method, the key will + /// remain to be used for further computations of this instance. + /// The key can be cleared using the method. + /// + /// A key up to 32 bytes or null. + /// public Blake2sDigest(byte[] key) { Init(null, null, key); } - /** - * BLAKE2s with key, required digest length, salt and personalization. - *

- * After calling the doFinal() method, the key, the salt and the personal - * string will remain and might be used for further computations with this - * instance. The key can be overwritten using the clearKey() method, the - * salt (pepper) can be overwritten using the clearSalt() method. - * - * @param key a key up to 32 bytes or null - * @param digestBytes from 1 up to 32 bytes - * @param salt 8 bytes or null - * @param personalization 8 bytes or null - */ + ///

+ /// + /// Initializes a new instance of with a key, required digest length (in bytes), salt and personalization. + /// + /// + /// After calling the method, the key, the salt and the personalization + /// will remain and might be used for further computations with this instance. + /// The key can be overwritten using the method, the salt (pepper) + /// can be overwritten using the method. + /// + /// A key up to 32 bytes or null. + /// Digest length from 1 to 32 bytes. + /// A 8 bytes or nullable salt. + /// A 8 bytes or null personalization. + /// public Blake2sDigest(byte[] key, int digestBytes, byte[] salt, byte[] personalization) { if (digestBytes < 1 || digestBytes > 32) @@ -306,11 +319,7 @@ private void InitializeInternalState() internalState[15] = blake2s_IV[7];// ^ f1 with f1 = 0 } - /** - * Update the message digest with a single byte. - * - * @param b the input byte to be entered. - */ + /// public void Update(byte b) { // process the buffer if full else add to buffer: @@ -338,13 +347,7 @@ public void Update(byte b) } } - /** - * Update the message digest with a block of bytes. - * - * @param message the byte array containing the data. - * @param offset the offset into the byte array where the data starts. - * @param len the length of the data. - */ + /// public void BlockUpdate(byte[] message, int offset, int len) { if (message == null || len == 0) @@ -404,6 +407,7 @@ public void BlockUpdate(byte[] message, int offset, int len) } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// public void BlockUpdate(ReadOnlySpan input) { if (input.IsEmpty) @@ -458,13 +462,14 @@ public void BlockUpdate(ReadOnlySpan input) } #endif - /** - * Close the digest, producing the final digest value. The doFinal() call - * leaves the digest reset. Key, salt and personal string remain. - * - * @param out the array the digest is to be copied into. - * @param outOffset the offset into the out array the digest is to start at. - */ + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The byte array the digest is to be copied into. + /// The offset into the byte array the digest is to start at. + /// The number of bytes written. public int DoFinal(byte[] output, int outOffset) { Check.OutputLength(output, outOffset, digestLength, "output buffer too short"); @@ -502,6 +507,13 @@ public int DoFinal(byte[] output, int outOffset) } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The span the digest is to be copied into. + /// The number of bytes written. public int DoFinal(Span output) { Check.OutputLength(output, digestLength, "output buffer too short"); @@ -535,10 +547,10 @@ public int DoFinal(Span output) } #endif - /** - * Reset the digest back to its initial state. The key, the salt and the - * personal string will remain for further computations. - */ + /// + /// Reset the digest back to it's initial state. + /// The key, the salt and the personalization will remain for further computations. + /// public void Reset() { bufferPos = 0; @@ -559,6 +571,13 @@ public void Reset() #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private void Compress(ReadOnlySpan message) { +#if NETCOREAPP3_0_OR_GREATER + if (Blake2s_X86.IsSupported) + { + Blake2s_X86.Compress(f0 == uint.MaxValue, chainValue, message, t0, t1, blake2s_IV); + return; + } +#endif InitializeInternalState(); Span m = stackalloc uint[16]; @@ -629,37 +648,28 @@ private void G(uint m1, uint m2, int posA, int posB, int posC, int posD) internalState[posB] = Integers.RotateRight(internalState[posB] ^ internalState[posC], 7); } - /** - * Return the algorithm name. - * - * @return the algorithm name - */ + /// public string AlgorithmName => "BLAKE2s"; - /** - * Return the size in bytes of the digest produced by this message digest. - * - * @return the size in bytes of the digest produced by this message digest. - */ + /// public int GetDigestSize() { return digestLength; } - /** - * Return the size in bytes of the internal buffer the digest applies its - * compression function to. - * - * @return byte length of the digest's internal buffer. - */ + /// + /// Return the size in bytes of the internal buffer the digest applies it's compression + /// function to. + /// + /// The byte length of the digests internal buffer. public int GetByteLength() { return BLOCK_LENGTH_BYTES; } - /** - * Overwrite the key if it is no longer used (zeroization). - */ + /// + /// Clears the key. + /// public void ClearKey() { if (key != null) @@ -669,10 +679,9 @@ public void ClearKey() } } - /** - * Overwrite the salt (pepper) if it is secret and no longer used - * (zeroization). - */ + /// + /// Clears the salt (pepper). + /// public void ClearSalt() { if (salt != null) diff --git a/crypto/src/crypto/digests/Blake2s_X86.cs b/crypto/src/crypto/digests/Blake2s_X86.cs new file mode 100644 index 0000000000..06a24aabba --- /dev/null +++ b/crypto/src/crypto/digests/Blake2s_X86.cs @@ -0,0 +1,384 @@ +#if NETCOREAPP3_0_OR_GREATER +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Org.BouncyCastle.Crypto.Digests +{ + // License from the original code created by Clinton Ingram (saucecontrol) for Blake2Fast + // at https://github.com/saucecontrol/Blake2Fast. The code has been copied and modified. + + // The MIT License + + // Copyright(c) 2018-2021 Clinton Ingram + + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + + internal static class Blake2s_X86 + { + public static bool IsSupported => Avx2.IsSupported && BitConverter.IsLittleEndian; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Compress(bool isFinal, Span hashBuffer, ReadOnlySpan message, uint totalSegmentsLow, uint totalSegmentsHigh, ReadOnlySpan blakeIV) + { + if (!IsSupported) + throw new PlatformNotSupportedException(nameof(Blake2s_X86)); + + Debug.Assert(message.Length >= Unsafe.SizeOf() * 8); + Debug.Assert(hashBuffer.Length >= 8); + + var hashBytes = MemoryMarshal.AsBytes(hashBuffer); + var ivBytes = MemoryMarshal.AsBytes(blakeIV); + + var r_14 = isFinal ? uint.MaxValue : 0; + var t_0 = Vector128.Create(totalSegmentsLow, totalSegmentsHigh, r_14, 0); + + Vector128 row1 = LoadVector128(hashBytes); + Vector128 row2 = LoadVector128(hashBytes[Vector128.Count..]); + Vector128 row3 = LoadVector128(ivBytes); + Vector128 row4 = LoadVector128(ivBytes[Vector128.Count..]); + row4 = Sse2.Xor(row4, t_0); + + Vector128 orig_1 = row1; + Vector128 orig_2 = row2; + + Perform10Rounds(message, ref row1, ref row2, ref row3, ref row4); + + row1 = Sse2.Xor(row1, row3); + row2 = Sse2.Xor(row2, row4); + row1 = Sse2.Xor(row1, orig_1); + row2 = Sse2.Xor(row2, orig_2); + + Store(row1, hashBytes); + Store(row2, hashBytes[Vector128.Count..]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Perform10Rounds(ReadOnlySpan m, ref Vector128 row1, ref Vector128 row2, ref Vector128 row3, ref Vector128 row4) + { + Debug.Assert(m.Length >= Unsafe.SizeOf() * 16); + + #region Rounds + var m0 = LoadVector128(m); + var m1 = LoadVector128(m[Vector128.Count..]); + var m2 = LoadVector128(m[(Vector128.Count * 2)..]); + var m3 = LoadVector128(m[(Vector128.Count * 3)..]); + + //ROUND 1 + var b1 = Sse.Shuffle(m0.AsSingle(), m1.AsSingle(), 0b_10_00_10_00).AsUInt32(); + + var b2 = Sse.Shuffle(m0.AsSingle(), m1.AsSingle(), 0b_11_01_11_01).AsUInt32(); + + var t0 = Sse2.Shuffle(m2, 0b_11_10_00_01); + var t1 = Sse2.Shuffle(m3, 0b_00_01_11_10); + var b3 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_11_00_00_11).AsUInt32(); + + t0 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_11_11_00).AsUInt32(); + var b4 = Sse2.Shuffle(t0, 0b_10_11_00_01); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 2 + t0 = Sse41.Blend(m1.AsUInt16(), m2.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + t1 = Sse2.ShiftLeftLogical128BitLane(m3, 4); + var t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_11_11_00_00).AsUInt32(); + b1 = Sse2.Shuffle(t2, 0b_10_01_00_11); + + t0 = Sse2.Shuffle(m2, 0b_00_00_10_00); + t1 = Sse41.Blend(m1.AsUInt16(), m3.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_11_11_00_00).AsUInt32(); + b2 = Sse2.Shuffle(t2, 0b_10_11_00_01); + + t0 = Sse2.ShiftLeftLogical128BitLane(m1, 4); + t1 = Sse41.Blend(m2.AsUInt16(), t0.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + t2 = Sse41.Blend(m0.AsUInt16(), t1.AsUInt16(), 0b_11_11_00_00).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_11_00_01_10); + + t0 = Sse2.UnpackHigh(m0, m1); + t1 = Sse2.ShiftLeftLogical128BitLane(m3, 4); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + b4 = Sse2.Shuffle(t2, 0b_11_00_01_10); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 3 + t0 = Sse2.UnpackHigh(m2, m3); + t1 = Sse41.Blend(m3.AsUInt16(), m1.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + b1 = Sse2.Shuffle(t2, 0b_11_01_00_10); + + t0 = Sse2.UnpackLow(m2, m0); + t1 = Sse41.Blend(t0.AsUInt16(), m0.AsUInt16(), 0b_11_11_00_00).AsUInt32(); + t2 = Sse2.ShiftLeftLogical128BitLane(m3, 8); + b2 = Sse41.Blend(t1.AsUInt16(), t2.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + + t0 = Sse41.Blend(m0.AsUInt16(), m2.AsUInt16(), 0b_00_11_11_00).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m1, 12); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_00_11_10_01); + + t0 = Sse2.ShiftLeftLogical128BitLane(m3, 4); + t1 = Sse41.Blend(m0.AsUInt16(), m1.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + t2 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + b4 = Sse2.Shuffle(t2, 0b_01_10_11_00); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 4 + t0 = Sse2.UnpackHigh(m0, m1); + t1 = Sse2.UnpackHigh(t0, m2); + t2 = Sse41.Blend(t1.AsUInt16(), m3.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + b1 = Sse2.Shuffle(t2, 0b_11_01_00_10); + + t0 = Sse2.ShiftLeftLogical128BitLane(m2, 8); + t1 = Sse41.Blend(m3.AsUInt16(), m0.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + t2 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + b2 = Sse2.Shuffle(t2, 0b_10_00_01_11); + + t0 = Sse41.Blend(m0.AsUInt16(), m1.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + t1 = Sse41.Blend(t0.AsUInt16(), m3.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + b3 = Sse2.Shuffle(t1, 0b_00_01_10_11); + + t0 = Ssse3.AlignRight(m0, m1, 4); + b4 = Sse41.Blend(t0.AsUInt16(), m2.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 5 + t0 = Sse2.UnpackLow(m1.AsUInt64(), m2.AsUInt64()).AsUInt32(); + t1 = Sse2.UnpackHigh(m0.AsUInt64(), m2.AsUInt64()).AsUInt32(); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + b1 = Sse2.Shuffle(t2, 0b_10_00_01_11); + + t0 = Sse2.UnpackHigh(m1.AsUInt64(), m3.AsUInt64()).AsUInt32(); + t1 = Sse2.UnpackLow(m0.AsUInt64(), m1.AsUInt64()).AsUInt32(); + b2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + + t0 = Sse2.UnpackHigh(m3.AsUInt64(), m1.AsUInt64()).AsUInt32(); + t1 = Sse2.UnpackHigh(m2.AsUInt64(), m0.AsUInt64()).AsUInt32(); + t2 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_10_01_00_11); + + t0 = Sse41.Blend(m0.AsUInt16(), m2.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + t1 = Sse2.ShiftLeftLogical128BitLane(t0, 8); + t2 = Sse41.Blend(t1.AsUInt16(), m3.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + b4 = Sse2.Shuffle(t2, 0b_10_00_11_01); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 6 + t0 = Sse2.UnpackHigh(m0, m1); + t1 = Sse2.UnpackLow(m0, m2); + b1 = Sse2.UnpackLow(t0.AsUInt64(), t1.AsUInt64()).AsUInt32(); + + t0 = Sse2.ShiftRightLogical128BitLane(m2, 4); + t1 = Sse41.Blend(m0.AsUInt16(), m3.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + b2 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_00_11_11_00).AsUInt32(); + + t0 = Sse41.Blend(m1.AsUInt16(), m0.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m3, 4); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_10_11_00_01); + + t0 = Sse2.UnpackLow(m2.AsUInt64(), m1.AsUInt64()).AsUInt32(); + t1 = Sse2.Shuffle(m3, 0b_10_00_01_00); + t2 = Sse2.ShiftRightLogical128BitLane(t0, 4); + b4 = Sse41.Blend(t1.AsUInt16(), t2.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 7 + t0 = Sse2.ShiftLeftLogical128BitLane(m1, 12); + t1 = Sse41.Blend(m0.AsUInt16(), m3.AsUInt16(), 0b_00_11_00_11).AsUInt32(); + b1 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + + t0 = Sse41.Blend(m3.AsUInt16(), m2.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m1, 4); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + b2 = Sse2.Shuffle(t2, 0b_10_01_11_00); + + t0 = Sse2.UnpackLow(m0.AsUInt64(), m2.AsUInt64()).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m1, 4); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_11_00).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_11_01_00_10); + + t0 = Sse2.UnpackHigh(m1, m2); + t1 = Sse2.UnpackHigh(m0.AsUInt64(), t0.AsUInt64()).AsUInt32(); + b4 = Sse2.Shuffle(t1, 0b_00_01_10_11); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 8 + t0 = Sse2.UnpackHigh(m0, m1); + t1 = Sse41.Blend(t0.AsUInt16(), m3.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + b1 = Sse2.Shuffle(t1, 0b_10_00_11_01); + + t0 = Sse41.Blend(m2.AsUInt16(), m3.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m0, 4); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + b2 = Sse2.Shuffle(t2, 0b_01_00_10_11); + + t0 = Sse2.UnpackHigh(m0.AsUInt64(), m3.AsUInt64()).AsUInt32(); + t1 = Sse2.UnpackLow(m1.AsUInt64(), m2.AsUInt64()).AsUInt32(); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_11_11_00).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_10_11_01_00); + + t0 = Sse2.UnpackLow(m0, m1); + t1 = Sse2.UnpackHigh(m1, m2); + t2 = Sse2.UnpackLow(t0.AsUInt64(), t1.AsUInt64()).AsUInt32(); + b4 = Sse2.Shuffle(t2, 0b_10_01_00_11); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 9 + t0 = Sse2.UnpackHigh(m1, m3); + t1 = Sse2.UnpackLow(t0.AsUInt64(), m0.AsUInt64()).AsUInt32(); + t2 = Sse41.Blend(t1.AsUInt16(), m2.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + b1 = Sse2.ShuffleHigh(t2.AsUInt16(), 0b_01_00_11_10).AsUInt32(); + + t0 = Sse2.UnpackHigh(m0, m3); + t1 = Sse41.Blend(m2.AsUInt16(), t0.AsUInt16(), 0b_11_11_00_00).AsUInt32(); + b2 = Sse2.Shuffle(t1, 0b_00_10_01_11); + + t0 = Sse2.UnpackLow(m0.AsUInt64(), m3.AsUInt64()).AsUInt32(); + t1 = Sse2.ShiftRightLogical128BitLane(m2, 8); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_01_11_10_00); + + t0 = Sse41.Blend(m1.AsUInt16(), m0.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + b4 = Sse2.Shuffle(t0, 0b_00_11_10_01); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + + //ROUND 10 + t0 = Sse41.Blend(m0.AsUInt16(), m2.AsUInt16(), 0b_00_00_00_11).AsUInt32(); + t1 = Sse41.Blend(m1.AsUInt16(), m2.AsUInt16(), 0b_00_11_00_00).AsUInt32(); + t2 = Sse41.Blend(t1.AsUInt16(), t0.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + b1 = Sse2.Shuffle(t2, 0b_01_11_00_10); + + t0 = Sse2.ShiftLeftLogical128BitLane(m0, 4); + t1 = Sse41.Blend(m1.AsUInt16(), t0.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + b2 = Sse2.Shuffle(t1, 0b_01_10_00_11); + + t0 = Sse2.UnpackHigh(m0, m3); + t1 = Sse2.UnpackLow(m2, m3); + t2 = Sse2.UnpackHigh(t0.AsUInt64(), t1.AsUInt64()).AsUInt32(); + b3 = Sse2.Shuffle(t2, 0b_00_10_01_11); + + t0 = Sse41.Blend(m3.AsUInt16(), m2.AsUInt16(), 0b_11_00_00_00).AsUInt32(); + t1 = Sse2.UnpackLow(m0, m3); + t2 = Sse41.Blend(t0.AsUInt16(), t1.AsUInt16(), 0b_00_00_11_11).AsUInt32(); + b4 = Sse2.Shuffle(t2, 0b_01_10_11_00); + + Round(ref row1, ref row2, ref row3, ref row4, b1, b2, b3, b4); + #endregion + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Round(ref Vector128 row1, ref Vector128 row2, ref Vector128 row3, ref Vector128 row4, Vector128 b1, Vector128 b2, Vector128 b3, Vector128 b4) + { + Vector128 r8 = Vector128.Create((byte)1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12); + Vector128 r16 = Vector128.Create((byte)2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13); + + G1(r16, ref row1, ref row2, ref row3, ref row4, b1); + G2(r8, ref row1, ref row2, ref row3, ref row4, b2); + + Diagonalize(ref row1, ref row3, ref row4); + + G1(r16, ref row1, ref row2, ref row3, ref row4, b3); + G2(r8, ref row1, ref row2, ref row3, ref row4, b4); + + Undiagonalize(ref row1, ref row3, ref row4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Diagonalize(ref Vector128 row1, ref Vector128 row3, ref Vector128 row4) + { + // +-------------------+ +-------------------+ + // | 0 | 1 | 2 | 3 | | 3 | 0 | 1 | 2 | + // +-------------------+ +-------------------+ + // | 8 | 9 | 10 | 11 | ---> | 9 | 10 | 11 | 8 | + // +-------------------+ +-------------------+ + // | 12 | 13 | 14 | 15 | | 14 | 15 | 12 | 13 | + // +-------------------+ +-------------------+ + + row1 = Sse2.Shuffle(row1, 0b_10_01_00_11); + row3 = Sse2.Shuffle(row3, 0b_00_11_10_01); + row4 = Sse2.Shuffle(row4, 0b_01_00_11_10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void G1(Vector128 r16, ref Vector128 row1, ref Vector128 row2, ref Vector128 row3, ref Vector128 row4, Vector128 b0) + { + row1 = Sse2.Add(Sse2.Add(row1, b0), row2); + row4 = Sse2.Xor(row4, row1); + row4 = Ssse3.Shuffle(row4.AsByte(), r16).AsUInt32(); + + row3 = Sse2.Add(row3, row4); + row2 = Sse2.Xor(row2, row3); + row2 = Sse2.Xor(Sse2.ShiftRightLogical(row2, 12), Sse2.ShiftLeftLogical(row2, 32 - 12)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void G2(Vector128 r8, ref Vector128 row1, ref Vector128 row2, ref Vector128 row3, ref Vector128 row4, Vector128 b0) + { + row1 = Sse2.Add(Sse2.Add(row1, b0), row2); + row4 = Sse2.Xor(row4, row1); + row4 = Ssse3.Shuffle(row4.AsByte(), r8).AsUInt32(); + + row3 = Sse2.Add(row3, row4); + row2 = Sse2.Xor(row2, row3); + row2 = Sse2.Xor(Sse2.ShiftRightLogical(row2, 7), Sse2.ShiftLeftLogical(row2, 32 - 7)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Undiagonalize(ref Vector128 row1, ref Vector128 row3, ref Vector128 row4) + { + // +-------------------+ +-------------------+ + // | 3 | 0 | 1 | 2 | | 0 | 1 | 2 | 3 | + // +-------------------+ +-------------------+ + // | 9 | 10 | 11 | 8 | ---> | 8 | 9 | 10 | 11 | + // +-------------------+ +-------------------+ + // | 14 | 15 | 12 | 13 | | 12 | 13 | 14 | 15 | + // +-------------------+ +-------------------+ + + row1 = Sse2.Shuffle(row1, 0b_00_11_10_01); + row3 = Sse2.Shuffle(row3, 0b_10_01_00_11); + row4 = Sse2.Shuffle(row4, 0b_01_00_11_10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 LoadVector128(ReadOnlySpan source) where T : struct + { + Debug.Assert(source.Length >= Unsafe.SizeOf>()); + return MemoryMarshal.Read>(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Store(Vector128 vector, Span destination) where T : struct + { + Debug.Assert(destination.Length >= Unsafe.SizeOf>()); + MemoryMarshal.Write(destination, ref vector); + } + } +} +#endif diff --git a/crypto/src/crypto/digests/Blake2xsDigest.cs b/crypto/src/crypto/digests/Blake2xsDigest.cs index 05323d14a0..c80bde78a9 100644 --- a/crypto/src/crypto/digests/Blake2xsDigest.cs +++ b/crypto/src/crypto/digests/Blake2xsDigest.cs @@ -12,19 +12,23 @@ The BLAKE2 cryptographic hash function was designed by Jean- Reference Implementation and Description can be found at: https://blake2.net/blake2x.pdf */ - /** - * Implementation of the eXtendable Output Function (XOF) BLAKE2xs. - *

- * BLAKE2xs offers a built-in keying mechanism to be used directly - * for authentication ("Prefix-MAC") rather than a HMAC construction. - *

- * BLAKE2xs offers a built-in support for a salt for randomized hashing - * and a personal string for defining a unique hash function for each application. - *

- * BLAKE2xs is optimized for 32-bit platforms and produces digests of any size - * between 1 and 2^16-2 bytes. The length can also be unknown and then the maximum - * length will be 2^32 blocks of 32 bytes. - */ + ///

+ /// Implementation of the eXtendable Output Function (XOF) BLAKE2xs. + /// BLAKE2xs is optimized for 32-bit platforms and produces digests of any size + /// between 1 and 2^16-2 bytes. The length can also be unknown and then the maximum + /// length will be 2^32 blocks of 32 bytes. + /// + /// + /// + /// + /// BLAKE2xs offers a built-in keying mechanism to be used directly + /// for authentication ("Prefix-MAC") rather than a HMAC construction. + /// + /// + /// BLAKE2xs offers a built-in support for a salt for randomized hashing + /// and a personal string for defining a unique hash function for each application. + /// + /// public sealed class Blake2xsDigest : IXof { @@ -78,43 +82,48 @@ public sealed class Blake2xsDigest */ private long nodeOffset; - /** - * BLAKE2xs for hashing with unknown digest length - */ + /// + /// Initializes a new instance of for hashing an unknown digest length. + /// public Blake2xsDigest() : this(UnknownDigestLength) { } - /** - * BLAKE2xs for hashing - * - * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1 - */ + /// + /// Initializes a new instance of with a given digest size. + /// + /// The desired digest length in bytes. Must be above 1 and less than 2^16-1. public Blake2xsDigest(int digestBytes) : this(digestBytes, null, null, null) { } - /** - * BLAKE2xs with key - * - * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1 - * @param key A key up to 32 bytes or null - */ + /// + /// + /// Initializes a new instance of with a key and given digest length. + /// + /// After calling the method, the key will + /// remain to be used for further computations of this instance. + /// + /// The desired digest length in bytes. Must be above 1 and less than 2^16-1. + /// A key up to 32 bytes or null. public Blake2xsDigest(int digestBytes, byte[] key) : this(digestBytes, key, null, null) { } - /** - * BLAKE2xs with key, salt and personalization - * - * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1 - * @param key A key up to 32 bytes or null - * @param salt 8 bytes or null - * @param personalization 8 bytes or null - */ + + /// + /// + /// Initializes a new instance of with a key, required digest length (in bytes), salt and personalization. + /// + /// + /// The desired digest length in bytes. Must be above 1 and less than 2^16-1. + /// A key up to 32 bytes or null. + /// A 8 bytes or null salt. + /// A 8 bytes or null personalization. + /// public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization) { if (digestBytes < 1 || digestBytes > UnknownDigestLength) @@ -125,6 +134,10 @@ public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personali hash = new Blake2sDigest(DigestLength, key, salt, personalization, nodeOffset); } + /// + /// Constructs a new instance of from another ./>. + /// + /// The original instance of that is copied. public Blake2xsDigest(Blake2xsDigest digest) { digestLength = digest.digestLength; @@ -137,72 +150,53 @@ public Blake2xsDigest(Blake2xsDigest digest) nodeOffset = digest.nodeOffset; } - /** - * Return the algorithm name. - * - * @return the algorithm name - */ + /// public string AlgorithmName => "BLAKE2xs"; - /** - * Return the size in bytes of the digest produced by this message digest. - * - * @return the size in bytes of the digest produced by this message digest. - */ + /// public int GetDigestSize() => digestLength; - /** - * Return the size in bytes of the internal buffer the digest applies its - * compression function to. - * - * @return byte length of the digest's internal buffer. - */ + /// + /// Return the size in bytes of the internal buffer the digest applies it's compression + /// function to. + /// + /// The byte length of the digests internal buffer. public int GetByteLength() => hash.GetByteLength(); - /** - * Return the maximum size in bytes the digest can produce when the length - * is unknown - * - * @return byte length of the largest digest with unknown length - */ + /// + /// Return the maximum size in bytes the digest can produce when the length + /// is unknown + /// + /// The byte length of the largest digest with unknown length public long GetUnknownMaxLength() { return MaxNumberBlocks * DigestLength; } - /** - * Update the message digest with a single byte. - * - * @param in the input byte to be entered. - */ + /// public void Update(byte b) { hash.Update(b); } - /** - * Update the message digest with a block of bytes. - * - * @param in the byte array containing the data. - * @param inOff the offset into the byte array where the data starts. - * @param len the length of the data. - */ + /// public void BlockUpdate(byte[] input, int inOff, int inLen) { hash.BlockUpdate(input, inOff, inLen); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// public void BlockUpdate(ReadOnlySpan input) { hash.BlockUpdate(input); } #endif - /** - * Reset the digest back to its initial state. The key, the salt and the - * personal string will remain for further computations. - */ + /// + /// Reset the digest back to it's initial state. + /// The key, the salt and the personalization will remain for further computations. + /// public void Reset() { hash.Reset(); @@ -214,26 +208,28 @@ public void Reset() nodeOffset = ComputeNodeOffset(); } - /** - * Close the digest, producing the final digest value. The doFinal() call - * leaves the digest reset. Key, salt and personal string remain. - * - * @param out the array the digest is to be copied into. - * @param outOffset the offset into the out array the digest is to start at. - */ + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The byte array the digest is to be copied into. + /// The offset into the byte array the digest is to start at. + /// The number of bytes written. public int DoFinal(byte[] output, int outOff) { return OutputFinal(output, outOff, digestLength); } - /** - * Close the digest, producing the final digest value. The doFinal() call - * leaves the digest reset. Key, salt, personal string remain. - * - * @param out output array to write the output bytes to. - * @param outOff offset to start writing the bytes at. - * @param outLen the number of output bytes requested. - */ + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The output array to write the output bytes to. + /// The offset to start writing the bytes at. + /// The number of output bytes requested. + /// The number of bytes written. public int OutputFinal(byte[] output, int outOff, int outLen) { int ret = Output(output, outOff, outLen); @@ -243,15 +239,14 @@ public int OutputFinal(byte[] output, int outOff, int outLen) return ret; } - /** - * Start outputting the results of the final calculation for this digest. Unlike doFinal, this method - * will continue producing output until the Xof is explicitly reset, or signals otherwise. - * - * @param out output array to write the output bytes to. - * @param outOff offset to start writing the bytes at. - * @param outLen the number of output bytes requested. - * @return the number of bytes written - */ + /// + /// Start outputting the results of the final calculation for this digest. Unlike , this method + /// will continue producing output until the Xof is explicitly reset, or signals otherwise. + /// + /// The output array to write the output bytes to. + /// The offset to start writing the bytes at. + /// The number of output bytes requested. + /// The number of bytes written. public int Output(byte[] output, int outOff, int outLen) { Check.OutputLength(output, outOff, outLen, "output buffer too short"); @@ -298,6 +293,13 @@ public int Output(byte[] output, int outOff, int outLen) } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The output span to write the output bytes to. + /// The number of bytes written. public int DoFinal(Span output) { Check.OutputLength(output, digestLength, "output buffer too short"); @@ -305,6 +307,13 @@ public int DoFinal(Span output) return OutputFinal(output[..digestLength]); } + /// Close the digest, producing the final digest value. + /// + /// The call leaves the digest reset. + /// Key, salt and personal string remain. + /// + /// The output span to write the output bytes to. + /// The number of bytes written. public int OutputFinal(Span output) { int ret = Output(output); @@ -314,6 +323,12 @@ public int OutputFinal(Span output) return ret; } + /// + /// Start outputting the results of the final calculation for this digest. Unlike , this method + /// will continue producing output until the Xof is explicitly reset, or signals otherwise. + /// + /// The output span to write the output bytes to. + /// The number of bytes written. public int Output(Span output) { int outLen = output.Length;