diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/.editorconfig b/src/core/IronPython.Modules/hashlib/BouncyCastle/.editorconfig
new file mode 100644
index 000000000..ace22db56
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+trim_trailing_whitespace = false
+insert_final_newline = false
+
+[*.cs]
+# leave upstream code formatting and style intact
+generated_code = true
+
+dotnet_diagnostic.CS1574.severity = none # CS1574: XML comment has cref attribute that could not be resolved
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2bDigest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2bDigest.cs
new file mode 100644
index 000000000..e9d21d3cd
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2bDigest.cs
@@ -0,0 +1,663 @@
+using System;
+using System.Diagnostics;
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /*
+ * The BLAKE2 cryptographic hash function was designed by Jean-Philippe Aumasson, Samuel Neves, Zooko
+ * Wilcox-O'Hearn, and Christian Winnerlein.
+ *
+ * Reference Implementation and Description can be found at: https://blake2.net/
+ * RFC: https://tools.ietf.org/html/rfc7693
+ *
+ * This implementation does not support the Tree Hashing Mode.
+ *
+ * For unkeyed hashing, developers adapting BLAKE2 to ASN.1-based message formats SHOULD use the OID tree at:
+ * x = 1.3.6.1.4.1.1722.12.2.
+ *
+ * +---------------+--------+-----------+------+------------+
+ * | Algorithm | Target | Collision | Hash | Hash ASN.1 |
+ * | Identifier | Arch | Security | nn | OID Suffix |
+ * +---------------+--------+-----------+------+------------+
+ * | id-blake2b160 | 64-bit | 2**80 | 20 | x.1.20 |
+ * | id-blake2b256 | 64-bit | 2**128 | 32 | x.1.32 |
+ * | id-blake2b384 | 64-bit | 2**192 | 48 | x.1.48 |
+ * | id-blake2b512 | 64-bit | 2**256 | 64 | x.1.64 |
+ * +---------------+--------+-----------+------+------------+
+ */
+
+ ///
+ /// 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 an
+ /// HMAC construction.
+ ///
+ ///
+ /// BLAKE2b offers built-in support for a salt for randomized hashing and a personal string for defining a unique
+ /// hash function for each application.
+ ///
+ ///
+ internal sealed class Blake2bDigest
+ : IDigest
+ {
+ /*
+ * BLAKE2b Initialization Vector (the same as SHA-512 IV).
+ *
+ * Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
+ */
+ private static readonly ulong[] IV =
+ {
+ 0x6a09e667f3bcc908UL, 0xbb67ae8584caa73bUL, 0x3c6ef372fe94f82bUL, 0xa54ff53a5f1d36f1UL,
+ 0x510e527fade682d1UL, 0x9b05688c2b3e6c1fUL, 0x1f83d9abfb41bd6bUL, 0x5be0cd19137e2179UL
+ };
+
+ // Message word permutations
+ private static readonly byte[] Sigma =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
+ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
+ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
+ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
+ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
+ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
+ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
+ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
+ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
+ };
+
+ private const int ROUNDS = 12; // to use for Catenas H'
+ private const int BLOCK_LENGTH_BYTES = 128;// bytes
+
+ private readonly ulong[] chainValue = new ulong[8]; // State vector, in the BLAKE2 paper it is called h
+
+ // Whenever this buffer overflows, it will be processed in the Compress() function.
+ // For performance issues, long messages will not use this buffer.
+ private readonly byte[] buffer = new byte[BLOCK_LENGTH_BYTES];
+
+ // General parameters:
+ private int digestLength = 64; // 1 - 64 bytes
+ private byte[] m_salt = null;
+ private byte[] m_personalization = null;
+ private byte[] m_key = null;
+
+ /*
+ * 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;
+ //private bool isLastNode = false;
+
+ // Position of last inserted byte:
+ private int bufferPos = 0; // a value from 0 up to BLOCK_LENGTH_BYTES
+
+ private ulong t0 = 0UL; // holds last significant bits, counter (counts bytes)
+ private ulong t1 = 0UL; // counter: Length up to 2^128 are supported
+ private ulong f0 = 0UL; // finalization flag, for last block: ~0UL
+
+ // For Tree Hashing Mode, not used here:
+ //private ulong f1 = 0UL; // finalization flag, for last node: ~0UL
+
+ ///
+ /// 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)
+ {
+ Array.Copy(digest.chainValue, 0, chainValue, 0, 8);
+ Array.Copy(digest.buffer, 0, buffer, 0, BLOCK_LENGTH_BYTES);
+
+ this.bufferPos = digest.bufferPos;
+ this.m_key = Arrays.Clone(digest.m_key);
+ this.digestLength = digest.digestLength;
+ this.m_personalization = Arrays.Clone(digest.m_personalization);
+ this.m_salt = Arrays.Clone(digest.m_salt);
+ this.t0 = digest.t0;
+ this.t1 = digest.t1;
+ this.f0 = digest.f0;
+ }
+
+ ///
+ /// 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)
+ throw new ArgumentException("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512");
+
+ this.digestLength = digestSize / 8;
+
+ Init();
+ }
+
+ ///
+ ///
+ /// 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)
+ {
+ this.digestLength = 64;
+
+ if (!Arrays.IsNullOrEmpty(key))
+ {
+ if (key.Length > 64)
+ throw new ArgumentException("Keys > 64 bytes are not supported", nameof(key));
+
+ m_key = Arrays.CopyBuffer(key);
+ }
+
+ Init();
+ }
+
+ ///
+ ///
+ /// 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)
+ throw new ArgumentException("Invalid digest length (required: 1 - 64)");
+
+ this.digestLength = digestLength;
+
+ if (!Arrays.IsNullOrEmpty(key))
+ {
+ if (key.Length > 64)
+ throw new ArgumentException("Keys > 64 bytes are not supported", nameof(key));
+
+ m_key = Arrays.CopyBuffer(key);
+ }
+
+ if (salt != null)
+ {
+ if (salt.Length != 16)
+ throw new ArgumentException("salt length must be exactly 16 bytes", nameof(salt));
+
+ m_salt = Arrays.CopyBuffer(salt);
+ }
+
+ if (personalization != null)
+ {
+ if (personalization.Length != 16)
+ throw new ArgumentException("personalization length must be exactly 16 bytes",
+ nameof(personalization));
+
+ m_personalization = Arrays.CopyBuffer(personalization);
+ }
+
+ Init();
+ }
+
+ private void Init()
+ {
+ int keyLength = 0;
+ if (m_key != null)
+ {
+ keyLength = m_key.Length;
+ Array.Copy(m_key, 0, buffer, 0, keyLength);
+ //Arrays.Fill(buffer, keyLength, BLOCK_LENGTH_BYTES, 0);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+
+ chainValue[0] = IV[0] ^ (ulong)(digestLength | (keyLength << 8) | 0x1010000);
+ // 0x1010000 = ((fanout << 16) | (depth << 24) | (leafLength << 32));
+ // with fanout = 1; depth = 0; leafLength = 0;
+ chainValue[1] = IV[1];// ^ nodeOffset; with nodeOffset = 0;
+ chainValue[2] = IV[2];// ^ ( nodeDepth | (innerHashLength << 8) );
+ // with nodeDepth = 0; innerHashLength = 0;
+ chainValue[3] = IV[3];
+
+ chainValue[4] = IV[4];
+ chainValue[5] = IV[5];
+ if (m_salt != null)
+ {
+ chainValue[4] ^= Pack.LE_To_UInt64(m_salt, 0);
+ chainValue[5] ^= Pack.LE_To_UInt64(m_salt, 8);
+ }
+
+ chainValue[6] = IV[6];
+ chainValue[7] = IV[7];
+ if (m_personalization != null)
+ {
+ chainValue[6] ^= Pack.LE_To_UInt64(m_personalization, 0);
+ chainValue[7] ^= Pack.LE_To_UInt64(m_personalization, 8);
+ }
+ }
+
+ ///
+ public void Update(byte b)
+ {
+ // process the buffer if full else add to buffer:
+ int remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength == 0)
+ {
+ // full buffer
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Compress(buffer);
+#else
+ Compress(buffer, 0);
+#endif
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ buffer[0] = b;
+ bufferPos = 1;
+ }
+ else
+ {
+ buffer[bufferPos++] = b;
+ }
+ }
+
+ ///
+ public void BlockUpdate(byte[] message, int offset, int len)
+ {
+ if (message == null || len == 0)
+ return;
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ BlockUpdate(message.AsSpan(offset, len));
+#else
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ {
+ // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength >= len)
+ {
+ Array.Copy(message, offset, buffer, bufferPos, len);
+ bufferPos += len;
+ return;
+ }
+
+ // full buffer + at least 1 byte
+ Array.Copy(message, offset, buffer, bufferPos, remainingLength);
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(buffer, 0);
+ bufferPos = 0;
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ }
+
+ // process blocks except last block (also if last block is full)
+ int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
+ int messagePos = offset + remainingLength;
+ while (messagePos < blockWiseLastPos)
+ {
+ // block wise 128 bytes without buffer:
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(message, messagePos);
+ messagePos += BLOCK_LENGTH_BYTES;
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos);
+ bufferPos += offset + len - messagePos;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ///
+ public void BlockUpdate(ReadOnlySpan input)
+ {
+ if (input.IsEmpty)
+ return;
+
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ {
+ // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength >= input.Length)
+ {
+ input.CopyTo(buffer.AsSpan(bufferPos));
+ bufferPos += input.Length;
+ return;
+ }
+
+ // full buffer + at least 1 byte
+ input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos));
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(buffer);
+ bufferPos = 0;
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ }
+
+ // process blocks except last block (also if last block is full)
+ int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES;
+ int messagePos = remainingLength;
+ while (messagePos < blockWiseLastPos)
+ {
+ // block wise 128 bytes without buffer:
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(input[messagePos..]);
+ messagePos += BLOCK_LENGTH_BYTES;
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ input[messagePos..].CopyTo(buffer.AsSpan());
+ bufferPos += input.Length - messagePos;
+ }
+#endif
+
+ /// 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");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return DoFinal(output.AsSpan(outOffset));
+#else
+ f0 = 0xFFFFFFFFFFFFFFFFUL;
+ //if (isLastNode)
+ //{
+ // f1 = 0xFFFFFFFFFFFFFFFFUL;
+ //}
+
+ if (bufferPos > 0)
+ {
+ IncrementCounter(bufferPos);
+ }
+
+ Compress(buffer, 0);
+
+ int full = digestLength >> 3, partial = digestLength & 7;
+ Pack.UInt64_To_LE(chainValue, 0, full, output, outOffset);
+ if (partial > 0)
+ {
+ Pack.UInt64_To_LE_Low(chainValue[full], output, outOffset + digestLength - partial, partial);
+ }
+
+ Reset();
+
+ return digestLength;
+#endif
+ }
+
+#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");
+
+ f0 = 0xFFFFFFFFFFFFFFFFUL;
+ //if (isLastNode)
+ //{
+ // f1 = 0xFFFFFFFFFFFFFFFFUL;
+ //}
+
+ if (bufferPos > 0)
+ {
+ IncrementCounter(bufferPos);
+ }
+
+ Compress(buffer);
+
+ int full = digestLength >> 3, partial = digestLength & 7;
+ Pack.UInt64_To_LE(chainValue.AsSpan(0, full), output);
+ if (partial > 0)
+ {
+ Pack.UInt64_To_LE_Low(chainValue[full], output.Slice(digestLength - partial, partial));
+ }
+
+ Reset();
+
+ return digestLength;
+ }
+#endif
+
+ ///
+ /// 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;
+ f0 = 0UL;
+ t0 = 0UL;
+ t1 = 0UL;
+
+ Array.Clear(buffer, 0, buffer.Length);
+
+ Init();
+ }
+
+#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(chainValue, IV, t0, t1, f0, message);
+ return;
+ }
+#endif
+
+ Span m = stackalloc ulong[16];
+ Pack.LE_To_UInt64(message, m);
+
+ ulong v0 = chainValue[0];
+ ulong v1 = chainValue[1];
+ ulong v2 = chainValue[2];
+ ulong v3 = chainValue[3];
+ ulong v4 = chainValue[4];
+ ulong v5 = chainValue[5];
+ ulong v6 = chainValue[6];
+ ulong v7 = chainValue[7];
+ ulong v8 = IV[0];
+ ulong v9 = IV[1];
+ ulong va = IV[2];
+ ulong vb = IV[3];
+ ulong vc = IV[4] ^ t0;
+ ulong vd = IV[5] ^ t1;
+ ulong ve = IV[6] ^ f0;
+ ulong vf = IV[7]; // ^ f1, with f1 == 0
+
+ for (int round = 0; round < ROUNDS; round++)
+ {
+ int pos = round * 16;
+
+ // Apply G to columns of internal state
+ G(m[Sigma[pos + 0]], m[Sigma[pos + 1]], ref v0, ref v4, ref v8, ref vc);
+ G(m[Sigma[pos + 2]], m[Sigma[pos + 3]], ref v1, ref v5, ref v9, ref vd);
+ G(m[Sigma[pos + 4]], m[Sigma[pos + 5]], ref v2, ref v6, ref va, ref ve);
+ G(m[Sigma[pos + 6]], m[Sigma[pos + 7]], ref v3, ref v7, ref vb, ref vf);
+
+ // Apply G to diagonals of internal state
+ G(m[Sigma[pos + 8]], m[Sigma[pos + 9]], ref v0, ref v5, ref va, ref vf);
+ G(m[Sigma[pos + 10]], m[Sigma[pos + 11]], ref v1, ref v6, ref vb, ref vc);
+ G(m[Sigma[pos + 12]], m[Sigma[pos + 13]], ref v2, ref v7, ref v8, ref vd);
+ G(m[Sigma[pos + 14]], m[Sigma[pos + 15]], ref v3, ref v4, ref v9, ref ve);
+ }
+
+ chainValue[0] ^= v0 ^ v8;
+ chainValue[1] ^= v1 ^ v9;
+ chainValue[2] ^= v2 ^ va;
+ chainValue[3] ^= v3 ^ vb;
+ chainValue[4] ^= v4 ^ vc;
+ chainValue[5] ^= v5 ^ vd;
+ chainValue[6] ^= v6 ^ ve;
+ chainValue[7] ^= v7 ^ vf;
+ }
+#else
+ private void Compress(byte[] message, int messagePos)
+ {
+ ulong[] m = new ulong[16];
+ Pack.LE_To_UInt64(message, messagePos, m);
+
+ ulong v0 = chainValue[0];
+ ulong v1 = chainValue[1];
+ ulong v2 = chainValue[2];
+ ulong v3 = chainValue[3];
+ ulong v4 = chainValue[4];
+ ulong v5 = chainValue[5];
+ ulong v6 = chainValue[6];
+ ulong v7 = chainValue[7];
+ ulong v8 = IV[0];
+ ulong v9 = IV[1];
+ ulong va = IV[2];
+ ulong vb = IV[3];
+ ulong vc = IV[4] ^ t0;
+ ulong vd = IV[5] ^ t1;
+ ulong ve = IV[6] ^ f0;
+ ulong vf = IV[7]; // ^ f1, with f1 == 0
+
+ for (int round = 0; round < ROUNDS; round++)
+ {
+ int pos = round * 16;
+
+ // Apply G to columns of internal state
+ G(m[Sigma[pos + 0]], m[Sigma[pos + 1]], ref v0, ref v4, ref v8, ref vc);
+ G(m[Sigma[pos + 2]], m[Sigma[pos + 3]], ref v1, ref v5, ref v9, ref vd);
+ G(m[Sigma[pos + 4]], m[Sigma[pos + 5]], ref v2, ref v6, ref va, ref ve);
+ G(m[Sigma[pos + 6]], m[Sigma[pos + 7]], ref v3, ref v7, ref vb, ref vf);
+
+ // Apply G to diagonals of internal state
+ G(m[Sigma[pos + 8]], m[Sigma[pos + 9]], ref v0, ref v5, ref va, ref vf);
+ G(m[Sigma[pos + 10]], m[Sigma[pos + 11]], ref v1, ref v6, ref vb, ref vc);
+ G(m[Sigma[pos + 12]], m[Sigma[pos + 13]], ref v2, ref v7, ref v8, ref vd);
+ G(m[Sigma[pos + 14]], m[Sigma[pos + 15]], ref v3, ref v4, ref v9, ref ve);
+ }
+
+ chainValue[0] ^= v0 ^ v8;
+ chainValue[1] ^= v1 ^ v9;
+ chainValue[2] ^= v2 ^ va;
+ chainValue[3] ^= v3 ^ vb;
+ chainValue[4] ^= v4 ^ vc;
+ chainValue[5] ^= v5 ^ vd;
+ chainValue[6] ^= v6 ^ ve;
+ chainValue[7] ^= v7 ^ vf;
+ }
+#endif
+
+ ///
+ public string AlgorithmName => "BLAKE2b";
+
+ ///
+ public int GetDigestSize() => digestLength;
+
+ ///
+ /// 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() => BLOCK_LENGTH_BYTES;
+
+ ///
+ /// Clears the key.
+ ///
+ public void ClearKey()
+ {
+ if (m_key != null)
+ {
+ Array.Clear(m_key, 0, m_key.Length);
+ Array.Clear(buffer, 0, buffer.Length);
+ }
+ }
+
+ ///
+ /// Clears the salt (pepper).
+ ///
+ public void ClearSalt()
+ {
+ if (m_salt != null)
+ {
+ Array.Clear(m_salt, 0, m_salt.Length);
+ }
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void IncrementCounter(int count)
+ {
+ Debug.Assert(count > 0);
+
+ ulong count64 = (ulong)count;
+ t0 += count64;
+ if (t0 < count64)
+ {
+ ++t1;
+ }
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private static void G(ulong m1, ulong m2, ref ulong a, ref ulong b, ref ulong c, ref ulong d)
+ {
+ a += b + m1;
+ d = Longs.RotateRight(d ^ a, 32);
+ c += d;
+ b = Longs.RotateRight(b ^ c, 24);
+
+ a += b + m2;
+ d = Longs.RotateRight(d ^ a, 16);
+ c += d;
+ b = Longs.RotateRight(b ^ c, 63);
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2b_X86.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2b_X86.cs
new file mode 100644
index 000000000..efb283673
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2b_X86.cs
@@ -0,0 +1,399 @@
+#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
+ {
+ internal static bool IsSupported =>
+ Org.BouncyCastle.Runtime.Intrinsics.X86.Avx2.IsEnabled &&
+ Org.BouncyCastle.Runtime.Intrinsics.Vector.IsPackedLittleEndian;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void Compress(Span hashBuffer, ReadOnlySpan blakeIV, ulong t0, ulong t1, ulong f0,
+ ReadOnlySpan message)
+ {
+ if (!IsSupported)
+ throw new PlatformNotSupportedException(nameof(Blake2b_X86));
+
+ Debug.Assert(hashBuffer.Length >= 8);
+ Debug.Assert(blakeIV.Length >= 8);
+ Debug.Assert(message.Length >= 128);
+
+ var hashBytes = MemoryMarshal.AsBytes(hashBuffer);
+ var ivBytes = MemoryMarshal.AsBytes(blakeIV);
+
+ var t_0 = Vector256.Create(t0, t1, f0, 0);
+
+ var row1 = MemoryMarshal.Read>(hashBytes);
+ var row2 = MemoryMarshal.Read>(hashBytes[32..]);
+ var row3 = MemoryMarshal.Read>(ivBytes);
+ var row4 = MemoryMarshal.Read>(ivBytes[32..]);
+ row4 = Avx2.Xor(row4, t_0);
+
+ var orig_1 = row1;
+ var 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);
+
+#if NET8_0_OR_GREATER
+ MemoryMarshal.Write(hashBytes, in row1);
+ MemoryMarshal.Write(hashBytes[32..], in row2);
+#else
+ MemoryMarshal.Write(hashBytes, ref row1);
+ MemoryMarshal.Write(hashBytes[32..], ref row2);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Perform12Rounds(ReadOnlySpan m, ref Vector256 row1, ref Vector256 row2, ref Vector256 row3, ref Vector256 row4)
+ {
+#region Rounds
+ //ROUND 1
+ var m0 = Broadcast128ToVector256(m);
+ var m1 = Broadcast128ToVector256(m[16..]);
+ var m2 = Broadcast128ToVector256(m[32..]);
+ var m3 = Broadcast128ToVector256(m[48..]);
+
+ 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 = Broadcast128ToVector256(m[64..]);
+ var m5 = Broadcast128ToVector256(m[80..]);
+ var m6 = Broadcast128ToVector256(m[96..]);
+ var m7 = Broadcast128ToVector256(m[112..]);
+
+ 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);
+
+ Diagonalize(ref row3, ref row1, 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 Vector256 Broadcast128ToVector256(ReadOnlySpan source) where T : struct
+ {
+ var vector = MemoryMarshal.Read>(source);
+ Vector256 result = vector.ToVector256Unsafe();
+ return result.WithUpper(vector);
+ }
+ }
+}
+#endif
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2sDigest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2sDigest.cs
new file mode 100644
index 000000000..3d7c2bf3f
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2sDigest.cs
@@ -0,0 +1,688 @@
+using System;
+using System.Diagnostics;
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /*
+ * The BLAKE2 cryptographic hash function was designed by Jean-Philippe Aumasson, Samuel Neves, Zooko
+ * Wilcox-O'Hearn, and Christian Winnerlein.
+ *
+ * Reference Implementation and Description can be found at: https://blake2.net/
+ * RFC: https://tools.ietf.org/html/rfc7693
+ *
+ * This implementation does not support the Tree Hashing Mode.
+ *
+ * For unkeyed hashing, developers adapting BLAKE2 to ASN.1-based message formats SHOULD use the OID tree at:
+ * x = 1.3.6.1.4.1.1722.12.2.
+ *
+ * +---------------+--------+-----------+------+------------+
+ * | Algorithm | Target | Collision | Hash | Hash ASN.1 |
+ * | Identifier | Arch | Security | nn | OID Suffix |
+ * +---------------+--------+-----------+------+------------+
+ * | id-blake2s128 | 32-bit | 2**64 | 16 | x.2.4 |
+ * | id-blake2s160 | 32-bit | 2**80 | 20 | x.2.5 |
+ * | id-blake2s224 | 32-bit | 2**112 | 28 | x.2.7 |
+ * | id-blake2s256 | 32-bit | 2**128 | 32 | x.2.8 |
+ * +---------------+--------+-----------+------+------------+
+ */
+
+ ///
+ /// 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 an
+ /// HMAC construction.
+ ///
+ ///
+ /// BLAKE2s offers built-in support for a salt for randomized hashing and a personal string for defining a unique
+ /// hash function for each application.
+ ///
+ ///
+ internal sealed class Blake2sDigest
+ : IDigest
+ {
+ /*
+ * BLAKE2s Initialization Vector (the same as SHA-256 IV).
+ *
+ * Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
+ */
+ private static readonly uint[] IV =
+ {
+ 0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU, 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U
+ };
+
+ // Message word permutations
+ private static readonly byte[] Sigma =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
+ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
+ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
+ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
+ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
+ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
+ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
+ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
+ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
+ };
+
+ private const int ROUNDS = 10; // to use for Catenas H'
+ private const int BLOCK_LENGTH_BYTES = 64;// bytes
+
+ private readonly uint[] chainValue = new uint[8]; // State vector, in the BLAKE2 paper it is called h
+
+ // Whenever this buffer overflows, it will be processed in the Compress() function.
+ // For performance issues, long messages will not use this buffer.
+ private readonly byte[] buffer = new byte[BLOCK_LENGTH_BYTES];
+
+ // General parameters:
+ private int digestLength = 32; // 1 - 32 bytes
+ private byte[] m_salt = null;
+ private byte[] m_personalization = null;
+ private byte[] m_key = null;
+
+ /*
+ * Tree hashing parameters; the Tree Hashing Mode is not supported but these are used for the XOF
+ * implementation.
+ */
+ 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;
+ //private bool isLastNode = false;
+
+ // Position of last inserted byte:
+ private int bufferPos = 0; // a value from 0 up to BLOCK_LENGTH_BYTES
+
+ private uint t0 = 0U; // holds last significant bits, counter (counts bytes)
+ private uint t1 = 0U; // counter: Length up to 2^64 are supported
+ private uint f0 = 0U; // finalization flag, for last block: ~0U
+
+ // For Tree Hashing Mode, not used here:
+ //private uint f1 = 0U; // finalization flag, for last node: ~0U
+
+ ///
+ /// 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)
+ {
+ Array.Copy(digest.chainValue, 0, chainValue, 0, 8);
+ Array.Copy(digest.buffer, 0, buffer, 0, BLOCK_LENGTH_BYTES);
+
+ this.bufferPos = digest.bufferPos;
+ this.m_key = Arrays.Clone(digest.m_key);
+ this.digestLength = digest.digestLength;
+ this.t0 = digest.t0;
+ this.t1 = digest.t1;
+ this.f0 = digest.f0;
+ this.m_salt = Arrays.Clone(digest.m_salt);
+ this.m_personalization = Arrays.Clone(digest.m_personalization);
+ this.fanout = digest.fanout;
+ this.depth = digest.depth;
+ this.leafLength = digest.leafLength;
+ this.nodeOffset = digest.nodeOffset;
+ this.nodeDepth = digest.nodeDepth;
+ this.innerHashLength = digest.innerHashLength;
+ }
+
+ ///
+ /// 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)
+ throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256");
+
+ this.digestLength = digestBits / 8;
+
+ Init();
+ }
+
+ ///
+ ///
+ /// 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)
+ {
+ this.digestLength = 32;
+
+ if (!Arrays.IsNullOrEmpty(key))
+ {
+ if (key.Length > 32)
+ throw new ArgumentException("Keys > 32 bytes are not supported", nameof(key));
+
+ m_key = Arrays.CopyBuffer(key);
+ }
+
+ Init();
+ }
+
+ ///
+ ///
+ /// 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)
+ : this(digestBytes, key, salt, personalization, offset: 0L)
+ {
+ }
+
+ // XOF root hash parameters
+ internal Blake2sDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization, long offset)
+ {
+ if (digestBytes < 1 || digestBytes > 32)
+ throw new ArgumentException("Invalid digest length (required: 1 - 32)");
+
+ this.digestLength = digestBytes;
+
+ if (!Arrays.IsNullOrEmpty(key))
+ {
+ if (key.Length > 32)
+ throw new ArgumentException("Keys > 32 bytes are not supported", nameof(key));
+
+ m_key = Arrays.CopyBuffer(key);
+ }
+
+ if (salt != null)
+ {
+ if (salt.Length != 8)
+ throw new ArgumentException("salt length must be exactly 8 bytes", nameof(salt));
+
+ m_salt = Arrays.CopyBuffer(salt);
+ }
+
+ if (personalization != null)
+ {
+ if (personalization.Length != 8)
+ throw new ArgumentException("personalization length must be exactly 8 bytes",
+ nameof(personalization));
+
+ m_personalization = Arrays.CopyBuffer(personalization);
+ }
+
+ this.nodeOffset = offset;
+
+ Init();
+ }
+
+ // XOF internal hash parameters
+ internal Blake2sDigest(int digestBytes, int hashLength, long offset)
+ {
+ digestLength = digestBytes;
+ nodeOffset = offset;
+ fanout = 0;
+ depth = 0;
+ leafLength = hashLength;
+ innerHashLength = hashLength;
+ nodeDepth = 0;
+
+ Init();
+ }
+
+ private void Init()
+ {
+ int keyLength = 0;
+ if (m_key != null)
+ {
+ keyLength = m_key.Length;
+ Array.Copy(m_key, 0, buffer, 0, keyLength);
+ //Arrays.Fill(buffer, keyLength, BLOCK_LENGTH_BYTES, 0);
+ bufferPos = BLOCK_LENGTH_BYTES; // zero padding
+ }
+
+ chainValue[0] = IV[0] ^ (uint)(digestLength | (keyLength << 8) | ((fanout << 16) | (depth << 24)));
+ chainValue[1] = IV[1] ^ (uint)leafLength;
+
+ int nofHi = (int)(nodeOffset >> 32);
+ int nofLo = (int)nodeOffset;
+ chainValue[2] = IV[2] ^ (uint)nofLo;
+ chainValue[3] = IV[3] ^ (uint)(nofHi | (nodeDepth << 16) | (innerHashLength << 24));
+
+ chainValue[4] = IV[4];
+ chainValue[5] = IV[5];
+ if (m_salt != null)
+ {
+ chainValue[4] ^= Pack.LE_To_UInt32(m_salt, 0);
+ chainValue[5] ^= Pack.LE_To_UInt32(m_salt, 4);
+ }
+
+ chainValue[6] = IV[6];
+ chainValue[7] = IV[7];
+ if (m_personalization != null)
+ {
+ chainValue[6] ^= Pack.LE_To_UInt32(m_personalization, 0);
+ chainValue[7] ^= Pack.LE_To_UInt32(m_personalization, 4);
+ }
+ }
+
+ ///
+ public void Update(byte b)
+ {
+ // process the buffer if full else add to buffer:
+ int remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength == 0)
+ {
+ // full buffer
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Compress(buffer);
+#else
+ Compress(buffer, 0);
+#endif
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ buffer[0] = b;
+ bufferPos = 1;
+ }
+ else
+ {
+ buffer[bufferPos++] = b;
+ }
+ }
+
+ ///
+ public void BlockUpdate(byte[] message, int offset, int len)
+ {
+ if (message == null || len == 0)
+ return;
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ BlockUpdate(message.AsSpan(offset, len));
+#else
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ {
+ // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength >= len)
+ {
+ Array.Copy(message, offset, buffer, bufferPos, len);
+ bufferPos += len;
+ return;
+ }
+
+ // full buffer + at least 1 byte
+ Array.Copy(message, offset, buffer, bufferPos, remainingLength);
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(buffer, 0);
+ bufferPos = 0;
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ }
+
+ // process blocks except last block (also if last block is full)
+ int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
+ int messagePos = offset + remainingLength;
+ while (messagePos < blockWiseLastPos)
+ {
+ // block wise 64 bytes without buffer:
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(message, messagePos);
+ messagePos += BLOCK_LENGTH_BYTES;
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos);
+ bufferPos += offset + len - messagePos;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ///
+ public void BlockUpdate(ReadOnlySpan input)
+ {
+ if (input.IsEmpty)
+ return;
+
+ int remainingLength = 0; // left bytes of buffer
+
+ if (bufferPos != 0)
+ {
+ // commenced, incomplete buffer
+
+ // complete the buffer:
+ remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ if (remainingLength >= input.Length)
+ {
+ input.CopyTo(buffer.AsSpan(bufferPos));
+ bufferPos += input.Length;
+ return;
+ }
+
+ // full buffer + at least 1 byte
+ input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos));
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(buffer);
+ bufferPos = 0;
+ Array.Clear(buffer, 0, buffer.Length);// clear buffer
+ }
+
+ // process blocks except last block (also if last block is full)
+ int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES;
+ int messagePos = remainingLength;
+ while (messagePos < blockWiseLastPos)
+ {
+ // block wise 64 bytes without buffer:
+ IncrementCounter(BLOCK_LENGTH_BYTES);
+ Compress(input[messagePos..]);
+ messagePos += BLOCK_LENGTH_BYTES;
+ }
+
+ // fill the buffer with left bytes, this might be a full block
+ input[messagePos..].CopyTo(buffer.AsSpan());
+ bufferPos += input.Length - messagePos;
+ }
+#endif
+
+ /// 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");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return DoFinal(output.AsSpan(outOffset));
+#else
+ f0 = 0xFFFFFFFFU;
+ //if (isLastNode)
+ //{
+ // f1 = 0xFFFFFFFFU;
+ //}
+
+ if (bufferPos > 0)
+ {
+ IncrementCounter(bufferPos);
+ }
+
+ Compress(buffer, 0);
+
+ int full = digestLength >> 2, partial = digestLength & 3;
+ Pack.UInt32_To_LE(chainValue, 0, full, output, outOffset);
+ if (partial > 0)
+ {
+ Pack.UInt32_To_LE_Low(chainValue[full], output, outOffset + digestLength - partial, partial);
+ }
+
+ Reset();
+
+ return digestLength;
+#endif
+ }
+
+#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");
+
+ f0 = 0xFFFFFFFFU;
+ //if (isLastNode)
+ //{
+ // f1 = 0xFFFFFFFFU;
+ //}
+
+ if (bufferPos > 0)
+ {
+ IncrementCounter(bufferPos);
+ }
+
+ Compress(buffer);
+
+ int full = digestLength >> 2, partial = digestLength & 3;
+ Pack.UInt32_To_LE(chainValue.AsSpan(0, full), output);
+ if (partial > 0)
+ {
+ Pack.UInt32_To_LE_Low(chainValue[full], output.Slice(digestLength - partial, partial));
+ }
+
+ Reset();
+
+ return digestLength;
+ }
+#endif
+
+ ///
+ /// 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;
+ f0 = 0U;
+ t0 = 0U;
+ t1 = 0U;
+
+ Array.Clear(buffer, 0, buffer.Length);
+
+ Init();
+ }
+
+#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(chainValue, IV, t0, t1, f0, message);
+ return;
+ }
+#endif
+
+ Span m = stackalloc uint[16];
+ Pack.LE_To_UInt32(message, m);
+
+ uint v0 = chainValue[0];
+ uint v1 = chainValue[1];
+ uint v2 = chainValue[2];
+ uint v3 = chainValue[3];
+ uint v4 = chainValue[4];
+ uint v5 = chainValue[5];
+ uint v6 = chainValue[6];
+ uint v7 = chainValue[7];
+ uint v8 = IV[0];
+ uint v9 = IV[1];
+ uint va = IV[2];
+ uint vb = IV[3];
+ uint vc = IV[4] ^ t0;
+ uint vd = IV[5] ^ t1;
+ uint ve = IV[6] ^ f0;
+ uint vf = IV[7]; // ^ f1, with f1 == 0
+
+ for (int round = 0; round < ROUNDS; round++)
+ {
+ int pos = round * 16;
+
+ // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
+ G(m[Sigma[pos + 0]], m[Sigma[pos + 1]], ref v0, ref v4, ref v8, ref vc);
+ G(m[Sigma[pos + 2]], m[Sigma[pos + 3]], ref v1, ref v5, ref v9, ref vd);
+ G(m[Sigma[pos + 4]], m[Sigma[pos + 5]], ref v2, ref v6, ref va, ref ve);
+ G(m[Sigma[pos + 6]], m[Sigma[pos + 7]], ref v3, ref v7, ref vb, ref vf);
+
+ // G apply to diagonals of internalState:
+ G(m[Sigma[pos + 8]], m[Sigma[pos + 9]], ref v0, ref v5, ref va, ref vf);
+ G(m[Sigma[pos + 10]], m[Sigma[pos + 11]], ref v1, ref v6, ref vb, ref vc);
+ G(m[Sigma[pos + 12]], m[Sigma[pos + 13]], ref v2, ref v7, ref v8, ref vd);
+ G(m[Sigma[pos + 14]], m[Sigma[pos + 15]], ref v3, ref v4, ref v9, ref ve);
+ }
+
+ chainValue[0] ^= v0 ^ v8;
+ chainValue[1] ^= v1 ^ v9;
+ chainValue[2] ^= v2 ^ va;
+ chainValue[3] ^= v3 ^ vb;
+ chainValue[4] ^= v4 ^ vc;
+ chainValue[5] ^= v5 ^ vd;
+ chainValue[6] ^= v6 ^ ve;
+ chainValue[7] ^= v7 ^ vf;
+ }
+#else
+ private void Compress(byte[] message, int messagePos)
+ {
+ uint[] m = new uint[16];
+ Pack.LE_To_UInt32(message, messagePos, m);
+
+ uint v0 = chainValue[0];
+ uint v1 = chainValue[1];
+ uint v2 = chainValue[2];
+ uint v3 = chainValue[3];
+ uint v4 = chainValue[4];
+ uint v5 = chainValue[5];
+ uint v6 = chainValue[6];
+ uint v7 = chainValue[7];
+ uint v8 = IV[0];
+ uint v9 = IV[1];
+ uint va = IV[2];
+ uint vb = IV[3];
+ uint vc = IV[4] ^ t0;
+ uint vd = IV[5] ^ t1;
+ uint ve = IV[6] ^ f0;
+ uint vf = IV[7]; // ^ f1, with f1 == 0
+
+ for (int round = 0; round < ROUNDS; round++)
+ {
+ int pos = round * 16;
+
+ // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
+ G(m[Sigma[pos + 0]], m[Sigma[pos + 1]], ref v0, ref v4, ref v8, ref vc);
+ G(m[Sigma[pos + 2]], m[Sigma[pos + 3]], ref v1, ref v5, ref v9, ref vd);
+ G(m[Sigma[pos + 4]], m[Sigma[pos + 5]], ref v2, ref v6, ref va, ref ve);
+ G(m[Sigma[pos + 6]], m[Sigma[pos + 7]], ref v3, ref v7, ref vb, ref vf);
+
+ // G apply to diagonals of internalState:
+ G(m[Sigma[pos + 8]], m[Sigma[pos + 9]], ref v0, ref v5, ref va, ref vf);
+ G(m[Sigma[pos + 10]], m[Sigma[pos + 11]], ref v1, ref v6, ref vb, ref vc);
+ G(m[Sigma[pos + 12]], m[Sigma[pos + 13]], ref v2, ref v7, ref v8, ref vd);
+ G(m[Sigma[pos + 14]], m[Sigma[pos + 15]], ref v3, ref v4, ref v9, ref ve);
+ }
+
+ chainValue[0] ^= v0 ^ v8;
+ chainValue[1] ^= v1 ^ v9;
+ chainValue[2] ^= v2 ^ va;
+ chainValue[3] ^= v3 ^ vb;
+ chainValue[4] ^= v4 ^ vc;
+ chainValue[5] ^= v5 ^ vd;
+ chainValue[6] ^= v6 ^ ve;
+ chainValue[7] ^= v7 ^ vf;
+ }
+#endif
+
+ ///
+ public string AlgorithmName => "BLAKE2s";
+
+ ///
+ public int GetDigestSize() => digestLength;
+
+ ///
+ /// 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() => BLOCK_LENGTH_BYTES;
+
+ ///
+ /// Clears the key.
+ ///
+ public void ClearKey()
+ {
+ if (m_key != null)
+ {
+ Array.Clear(m_key, 0, m_key.Length);
+ Array.Clear(buffer, 0, buffer.Length);
+ }
+ }
+
+ ///
+ /// Clears the salt (pepper).
+ ///
+ public void ClearSalt()
+ {
+ if (m_salt != null)
+ {
+ Array.Clear(m_salt, 0, m_salt.Length);
+ }
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void IncrementCounter(int count)
+ {
+ Debug.Assert(count > 0);
+
+ uint count32 = (uint)count;
+ t0 += count32;
+ if (t0 < count32)
+ {
+ ++t1;
+ }
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private static void G(uint m1, uint m2, ref uint a, ref uint b, ref uint c, ref uint d)
+ {
+ a += b + m1;
+ d = Integers.RotateRight(d ^ a, 16);
+ c += d;
+ b = Integers.RotateRight(b ^ c, 12);
+
+ a += b + m2;
+ d = Integers.RotateRight(d ^ a, 8);
+ c += d;
+ b = Integers.RotateRight(b ^ c, 7);
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2s_X86.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2s_X86.cs
new file mode 100644
index 000000000..3fdb8f0f2
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/Blake2s_X86.cs
@@ -0,0 +1,364 @@
+#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
+ {
+ internal static bool IsSupported =>
+ Org.BouncyCastle.Runtime.Intrinsics.X86.Sse41.IsEnabled &&
+ Org.BouncyCastle.Runtime.Intrinsics.Vector.IsPackedLittleEndian;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void Compress(Span hashBuffer, ReadOnlySpan blakeIV, uint t0, uint t1, uint f0,
+ ReadOnlySpan message)
+ {
+ if (!IsSupported)
+ throw new PlatformNotSupportedException(nameof(Blake2s_X86));
+
+ Debug.Assert(hashBuffer.Length >= 8);
+ Debug.Assert(blakeIV.Length >= 8);
+ Debug.Assert(message.Length >= 64);
+
+ var hashBytes = MemoryMarshal.AsBytes(hashBuffer);
+ var ivBytes = MemoryMarshal.AsBytes(blakeIV);
+
+ var t_0 = Vector128.Create(t0, t1, f0, 0);
+
+ var row1 = MemoryMarshal.Read>(hashBytes);
+ var row2 = MemoryMarshal.Read>(hashBytes[16..]);
+ var row3 = MemoryMarshal.Read>(ivBytes);
+ var row4 = MemoryMarshal.Read>(ivBytes[16..]);
+ row4 = Sse2.Xor(row4, t_0);
+
+ var orig_1 = row1;
+ var 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);
+
+#if NET8_0_OR_GREATER
+ MemoryMarshal.Write(hashBytes, in row1);
+ MemoryMarshal.Write(hashBytes[16..], in row2);
+#else
+ MemoryMarshal.Write(hashBytes, ref row1);
+ MemoryMarshal.Write(hashBytes[16..], ref row2);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Perform10Rounds(ReadOnlySpan m, ref Vector128 row1, ref Vector128 row2,
+ ref Vector128 row3, ref Vector128 row4)
+ {
+#region Rounds
+ var m0 = MemoryMarshal.Read>(m);
+ var m1 = MemoryMarshal.Read>(m[16..]);
+ var m2 = MemoryMarshal.Read>(m[32..]);
+ var m3 = MemoryMarshal.Read>(m[48..]);
+
+ //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);
+
+ Diagonalize(ref row3, ref row1, 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));
+ }
+ }
+}
+#endif
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/IDigest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/IDigest.cs
new file mode 100644
index 000000000..eaf04c05d
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/IDigest.cs
@@ -0,0 +1,53 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// Base interface for a message digest.
+ internal interface IDigest
+ {
+ /// The algorithm name.
+ 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.
+ 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.
+ int GetByteLength();
+
+ /// Update the message digest with a single byte.
+ /// 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.
+ 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.
+ 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.
+ 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.
+ int DoFinal(Span output);
+#endif
+
+ /// Reset the digest back to its initial state.
+ void Reset();
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/IMemoable.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/IMemoable.cs
new file mode 100644
index 000000000..75c0c1e0f
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/IMemoable.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities
+{
+ internal interface IMemoable
+ {
+ ///
+ /// Produce a copy of this object with its configuration and in its current state.
+ ///
+ ///
+ /// The returned object may be used simply to store the state, or may be used as a similar object
+ /// starting from the copied state.
+ ///
+ IMemoable Copy();
+
+ ///
+ /// Restore a copied object state into this object.
+ ///
+ ///
+ /// Implementations of this method should try to avoid or minimise memory allocation to perform the reset.
+ ///
+ /// an object originally {@link #copy() copied} from an object of the same type as this instance.
+ /// if the provided object is not of the correct type.
+ /// if the other parameter is in some other way invalid.
+ void Reset(IMemoable other);
+ }
+
+}
+
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/IXof.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/IXof.cs
new file mode 100644
index 000000000..684530671
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/IXof.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ ///
+ /// With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes.
+ /// This interface provides the extra methods required to support variable output on a digest implementation.
+ ///
+ internal interface IXof
+ : IDigest
+ {
+ ///
+ /// Output the results of the final calculation for this XOF to outLen number of bytes.
+ ///
+ /// output array to write the output bytes to.
+ /// offset to start writing the bytes at.
+ /// the number of output bytes requested.
+ /// the number of bytes written
+ int OutputFinal(byte[] output, int outOff, int outLen);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ///
+ /// Output the results of the final calculation for this XOF to fill the output span.
+ ///
+ /// span to fill with the output bytes.
+ /// the number of bytes written
+ int OutputFinal(Span output);
+#endif
+
+ ///
+ /// Start outputting the results of the final calculation for this XOF. Unlike DoFinal, this method
+ /// will continue producing output until the XOF is explicitly reset, or signals otherwise.
+ ///
+ /// output array to write the output bytes to.
+ /// offset to start writing the bytes at.
+ /// the number of output bytes requested.
+ /// the number of bytes written
+ int Output(byte[] output, int outOff, int outLen);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ///
+ /// Start outputting the results of the final calculation for this XOF. Unlike OutputFinal, this method
+ /// will continue producing output until the XOF is explicitly reset, or signals otherwise.
+ ///
+ /// span to fill with the output bytes.
+ /// the number of bytes written
+ int Output(Span output);
+#endif
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/KeccakDigest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/KeccakDigest.cs
new file mode 100644
index 000000000..0f678ea94
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/KeccakDigest.cs
@@ -0,0 +1,636 @@
+using System;
+using System.Diagnostics;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#endif
+
+using Org.BouncyCastle.Crypto.Utilities;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using Org.BouncyCastle.Math.Raw;
+#endif
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ ///
+ /// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ ///
+ ///
+ /// Following the naming conventions used in the C source code to enable easy review of the implementation.
+ ///
+ internal class KeccakDigest
+ : IDigest, IMemoable
+ {
+ private static readonly ulong[] KeccakRoundConstants = new ulong[]{
+ 0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL,
+ 0x000000000000808bUL, 0x0000000080000001UL, 0x8000000080008081UL, 0x8000000000008009UL,
+ 0x000000000000008aUL, 0x0000000000000088UL, 0x0000000080008009UL, 0x000000008000000aUL,
+ 0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
+ 0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
+ 0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL
+ };
+
+ private readonly ulong[] state = new ulong[25];
+ protected byte[] dataQueue = new byte[192];
+ protected int rate;
+ protected int bitsInQueue;
+ protected internal int fixedOutputLength;
+ protected bool squeezing;
+
+ public KeccakDigest()
+ : this(288)
+ {
+ }
+
+ public KeccakDigest(int bitLength)
+ {
+ Init(bitLength);
+ }
+
+ public KeccakDigest(KeccakDigest source)
+ {
+ CopyIn(source);
+ }
+
+ private void CopyIn(KeccakDigest source)
+ {
+ Array.Copy(source.state, 0, this.state, 0, source.state.Length);
+ Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length);
+ this.rate = source.rate;
+ this.bitsInQueue = source.bitsInQueue;
+ this.fixedOutputLength = source.fixedOutputLength;
+ this.squeezing = source.squeezing;
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return "Keccak-" + fixedOutputLength; }
+ }
+
+ public virtual int GetDigestSize()
+ {
+ return fixedOutputLength >> 3;
+ }
+
+ public virtual void Update(byte input)
+ {
+ Absorb(input);
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ Absorb(input, inOff, len);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void BlockUpdate(ReadOnlySpan input)
+ {
+ Absorb(input);
+ }
+#endif
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ Squeeze(output, outOff, fixedOutputLength);
+
+ Reset();
+
+ return GetDigestSize();
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual int DoFinal(Span output)
+ {
+ int digestSize = GetDigestSize();
+ Squeeze(output[..digestSize]);
+
+ Reset();
+
+ return digestSize;
+ }
+#endif
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
+ {
+ if (partialBits > 0)
+ {
+ AbsorbBits(partialByte, partialBits);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Squeeze(output.AsSpan(outOff, fixedOutputLength >> 3));
+#else
+ Squeeze(output, outOff, fixedOutputLength);
+#endif
+
+ Reset();
+
+ return GetDigestSize();
+ }
+
+ public virtual void Reset()
+ {
+ Init(fixedOutputLength);
+ }
+
+ /**
+ * Return the size of block that the compression function is applied to in bytes.
+ *
+ * @return internal byte length of a block.
+ */
+ public virtual int GetByteLength()
+ {
+ return rate >> 3;
+ }
+
+ private void Init(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 128:
+ case 224:
+ case 256:
+ case 288:
+ case 384:
+ case 512:
+ InitSponge(1600 - (bitLength << 1));
+ break;
+ default:
+ throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength");
+ }
+ }
+
+ private void InitSponge(int rate)
+ {
+ if (rate <= 0 || rate >= 1600 || (rate & 63) != 0)
+ throw new InvalidOperationException("invalid rate value");
+
+ this.rate = rate;
+ Array.Clear(state, 0, state.Length);
+ Arrays.Fill(this.dataQueue, (byte)0);
+ this.bitsInQueue = 0;
+ this.squeezing = false;
+ this.fixedOutputLength = (1600 - rate) >> 1;
+ }
+
+ protected void Absorb(byte data)
+ {
+ if ((bitsInQueue & 7) != 0)
+ throw new InvalidOperationException("attempt to absorb with odd length queue");
+ if (squeezing)
+ throw new InvalidOperationException("attempt to absorb while squeezing");
+
+ dataQueue[bitsInQueue >> 3] = data;
+ if ((bitsInQueue += 8) == rate)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ KeccakAbsorb(dataQueue);
+#else
+ KeccakAbsorb(dataQueue, 0);
+#endif
+ bitsInQueue = 0;
+ }
+ }
+
+ protected void Absorb(byte[] data, int off, int len)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Absorb(data.AsSpan(off, len));
+#else
+ if ((bitsInQueue & 7) != 0)
+ throw new InvalidOperationException("attempt to absorb with odd length queue");
+ if (squeezing)
+ throw new InvalidOperationException("attempt to absorb while squeezing");
+
+ int bytesInQueue = bitsInQueue >> 3;
+ int rateBytes = rate >> 3;
+
+ int available = rateBytes - bytesInQueue;
+ if (len < available)
+ {
+ Array.Copy(data, off, dataQueue, bytesInQueue, len);
+ this.bitsInQueue += len << 3;
+ return;
+ }
+
+ int count = 0;
+ if (bytesInQueue > 0)
+ {
+ Array.Copy(data, off, dataQueue, bytesInQueue, available);
+ count += available;
+ KeccakAbsorb(dataQueue, 0);
+ }
+
+ int remaining;
+ while ((remaining = (len - count)) >= rateBytes)
+ {
+ KeccakAbsorb(data, off + count);
+ count += rateBytes;
+ }
+
+ Array.Copy(data, off + count, dataQueue, 0, remaining);
+ this.bitsInQueue = remaining << 3;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ protected void Absorb(ReadOnlySpan data)
+ {
+ if ((bitsInQueue & 7) != 0)
+ throw new InvalidOperationException("attempt to absorb with odd length queue");
+ if (squeezing)
+ throw new InvalidOperationException("attempt to absorb while squeezing");
+
+ int bytesInQueue = bitsInQueue >> 3;
+ int rateBytes = rate >> 3;
+
+ int len = data.Length;
+ int available = rateBytes - bytesInQueue;
+ if (len < available)
+ {
+ data.CopyTo(dataQueue.AsSpan(bytesInQueue));
+ this.bitsInQueue += len << 3;
+ return;
+ }
+
+ int count = 0;
+ if (bytesInQueue > 0)
+ {
+ data[..available].CopyTo(dataQueue.AsSpan(bytesInQueue));
+ count += available;
+ KeccakAbsorb(dataQueue);
+ }
+
+ int remaining;
+ while ((remaining = len - count) >= rateBytes)
+ {
+ KeccakAbsorb(data[count..]);
+ count += rateBytes;
+ }
+
+ data[count..].CopyTo(dataQueue.AsSpan());
+ this.bitsInQueue = remaining << 3;
+ }
+#endif
+
+ protected void AbsorbBits(int data, int bits)
+ {
+ if (bits < 1 || bits > 7)
+ throw new ArgumentException("must be in the range 1 to 7", "bits");
+ if ((bitsInQueue & 7) != 0)
+ throw new InvalidOperationException("attempt to absorb with odd length queue");
+ if (squeezing)
+ throw new InvalidOperationException("attempt to absorb while squeezing");
+
+ int mask = (1 << bits) - 1;
+ dataQueue[bitsInQueue >> 3] = (byte)(data & mask);
+
+ // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work
+ bitsInQueue += bits;
+ }
+
+ private void PadAndSwitchToSqueezingPhase()
+ {
+ Debug.Assert(bitsInQueue < rate);
+
+ dataQueue[bitsInQueue >> 3] |= (byte)(1 << (bitsInQueue & 7));
+
+ if (++bitsInQueue == rate)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ KeccakAbsorb(dataQueue);
+#else
+ KeccakAbsorb(dataQueue, 0);
+#endif
+ }
+ else
+ {
+ int full = bitsInQueue >> 6, partial = bitsInQueue & 63;
+ int off = 0;
+ for (int i = 0; i < full; ++i)
+ {
+ state[i] ^= Pack.LE_To_UInt64(dataQueue, off);
+ off += 8;
+ }
+ if (partial > 0)
+ {
+ ulong mask = (1UL << partial) - 1UL;
+ state[full] ^= Pack.LE_To_UInt64(dataQueue, off) & mask;
+ }
+ }
+
+ state[(rate - 1) >> 6] ^= (1UL << 63);
+
+ bitsInQueue = 0;
+ squeezing = true;
+ }
+
+ // TODO[api] Not needed under NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ protected void Squeeze(byte[] output, int offset, long outputLength)
+ {
+ if ((outputLength & 7L) != 0L)
+ throw new InvalidOperationException("outputLength not a multiple of 8");
+
+ if (!squeezing)
+ {
+ PadAndSwitchToSqueezingPhase();
+ }
+ else if (bitsInQueue > 0)
+ {
+ int availableBits = bitsInQueue;
+ int dataQueuePos = (rate - bitsInQueue) >> 3;
+
+ if (outputLength <= availableBits)
+ {
+ SqueezePartialState(dataQueuePos, (int)outputLength >> 3, output, offset);
+ bitsInQueue -= (int)outputLength;
+ return;
+ }
+
+ SqueezePartialState(dataQueuePos, availableBits >> 3, output, offset);
+ offset += availableBits >> 3;
+ outputLength -= availableBits;
+ bitsInQueue = 0;
+ }
+
+ while (outputLength >= rate)
+ {
+ KeccakPermutation(state);
+ SqueezeFullState(output, offset);
+ offset += rate >> 3;
+ outputLength -= rate;
+ }
+
+ if (outputLength > 0)
+ {
+ KeccakPermutation(state);
+ SqueezePartialState(0, (int)outputLength >> 3, output, offset);
+ bitsInQueue = rate - (int)outputLength;
+ }
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ protected void Squeeze(Span output)
+ {
+ int rateBytes = rate >> 3;
+
+ if (!squeezing)
+ {
+ PadAndSwitchToSqueezingPhase();
+ }
+ else if (bitsInQueue > 0)
+ {
+ int available = bitsInQueue >> 3;
+ int dataQueuePos = rateBytes - available;
+
+ if (output.Length <= available)
+ {
+ SqueezePartialState(dataQueuePos, output.Length, output);
+ bitsInQueue -= output.Length << 3;
+ return;
+ }
+
+ SqueezePartialState(dataQueuePos, available, output);
+ output = output[available..];
+ bitsInQueue = 0;
+ }
+
+ while (output.Length >= rateBytes)
+ {
+ KeccakPermutation(state);
+ SqueezeFullState(output);
+ output = output[rateBytes..];
+ }
+
+ if (!output.IsEmpty)
+ {
+ KeccakPermutation(state);
+ SqueezePartialState(0, output.Length, output);
+ bitsInQueue = rate - (output.Length << 3);
+ }
+ }
+#endif
+
+ private void EnsureQueuePacked(int byteOff, int byteLen)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ if (BitConverter.IsLittleEndian)
+ return;
+#endif
+
+ int from = (byteOff + 7) / 8;
+ int to = (byteOff + byteLen + 7) / 8;
+ Pack.UInt64_To_LE(state, from, to - from, dataQueue, from * 8);
+ }
+
+ private void SqueezeFullState(byte[] output, int outputOff)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ SqueezeFullState(output.AsSpan(outputOff));
+#else
+ Pack.UInt64_To_LE(state, 0, rate >> 6, output, outputOff);
+#endif
+ }
+
+ private void SqueezePartialState(int byteOff, int byteLen, byte[] output, int outputOff)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ SqueezePartialState(byteOff, byteLen, output.AsSpan(outputOff));
+#else
+ EnsureQueuePacked(byteOff, byteLen);
+ Array.Copy(dataQueue, byteOff, output, outputOff, byteLen);
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void SqueezeFullState(Span output)
+ {
+ var fullState = state.AsSpan(0, rate >> 6);
+ if (BitConverter.IsLittleEndian)
+ {
+ MemoryMarshal.AsBytes(fullState).CopyTo(output);
+ }
+ else
+ {
+ Pack.UInt64_To_LE(fullState, output);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void SqueezePartialState(int byteOff, int byteLen, Span output)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ MemoryMarshal.AsBytes(state.AsSpan()).Slice(byteOff, byteLen).CopyTo(output);
+ }
+ else
+ {
+ EnsureQueuePacked(byteOff, byteLen);
+ dataQueue.AsSpan(byteOff, byteLen).CopyTo(output);
+ }
+ }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void KeccakAbsorb(ReadOnlySpan data)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ Nat.XorTo64(rate >> 6, MemoryMarshal.Cast(data), state);
+ }
+ else
+ {
+ int count = rate >> 6, off = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ state[i] ^= Pack.LE_To_UInt64(data[off..]);
+ off += 8;
+ }
+ }
+
+ KeccakPermutation(state);
+ }
+#else
+ private void KeccakAbsorb(byte[] data, int off)
+ {
+ int count = rate >> 6;
+ for (int i = 0; i < count; ++i)
+ {
+ state[i] ^= Pack.LE_To_UInt64(data, off);
+ off += 8;
+ }
+
+ KeccakPermutation(state);
+ }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void KeccakPermutation(Span A)
+#else
+ internal static void KeccakPermutation(ulong[] A)
+#endif
+ {
+ var bounds = A[24];
+
+ ulong a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
+ ulong a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
+ ulong a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
+ ulong a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
+ ulong a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
+
+ for (int i = 0; i < 24; i++)
+ {
+ // theta
+ ulong c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
+ ulong c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
+ ulong c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
+ ulong c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
+ ulong c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
+
+ ulong d1 = Longs.RotateLeft(c1, 1) ^ c4;
+ ulong d2 = Longs.RotateLeft(c2, 1) ^ c0;
+ ulong d3 = Longs.RotateLeft(c3, 1) ^ c1;
+ ulong d4 = Longs.RotateLeft(c4, 1) ^ c2;
+ ulong d0 = Longs.RotateLeft(c0, 1) ^ c3;
+
+ a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
+ a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
+ a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
+ a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
+ a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
+
+ // rho/pi
+ c1 = Longs.RotateLeft(a01, 1);
+ a01 = Longs.RotateLeft(a06, 44);
+ a06 = Longs.RotateLeft(a09, 20);
+ a09 = Longs.RotateLeft(a22, 61);
+ a22 = Longs.RotateLeft(a14, 39);
+ a14 = Longs.RotateLeft(a20, 18);
+ a20 = Longs.RotateLeft(a02, 62);
+ a02 = Longs.RotateLeft(a12, 43);
+ a12 = Longs.RotateLeft(a13, 25);
+ a13 = Longs.RotateLeft(a19, 8);
+ a19 = Longs.RotateLeft(a23, 56);
+ a23 = Longs.RotateLeft(a15, 41);
+ a15 = Longs.RotateLeft(a04, 27);
+ a04 = Longs.RotateLeft(a24, 14);
+ a24 = Longs.RotateLeft(a21, 2);
+ a21 = Longs.RotateLeft(a08, 55);
+ a08 = Longs.RotateLeft(a16, 45);
+ a16 = Longs.RotateLeft(a05, 36);
+ a05 = Longs.RotateLeft(a03, 28);
+ a03 = Longs.RotateLeft(a18, 21);
+ a18 = Longs.RotateLeft(a17, 15);
+ a17 = Longs.RotateLeft(a11, 10);
+ a11 = Longs.RotateLeft(a07, 6);
+ a07 = Longs.RotateLeft(a10, 3);
+ a10 = c1;
+
+ // chi
+ c0 = a00 ^ (~a01 & a02);
+ c1 = a01 ^ (~a02 & a03);
+ a02 ^= ~a03 & a04;
+ a03 ^= ~a04 & a00;
+ a04 ^= ~a00 & a01;
+ a00 = c0;
+ a01 = c1;
+
+ c0 = a05 ^ (~a06 & a07);
+ c1 = a06 ^ (~a07 & a08);
+ a07 ^= ~a08 & a09;
+ a08 ^= ~a09 & a05;
+ a09 ^= ~a05 & a06;
+ a05 = c0;
+ a06 = c1;
+
+ c0 = a10 ^ (~a11 & a12);
+ c1 = a11 ^ (~a12 & a13);
+ a12 ^= ~a13 & a14;
+ a13 ^= ~a14 & a10;
+ a14 ^= ~a10 & a11;
+ a10 = c0;
+ a11 = c1;
+
+ c0 = a15 ^ (~a16 & a17);
+ c1 = a16 ^ (~a17 & a18);
+ a17 ^= ~a18 & a19;
+ a18 ^= ~a19 & a15;
+ a19 ^= ~a15 & a16;
+ a15 = c0;
+ a16 = c1;
+
+ c0 = a20 ^ (~a21 & a22);
+ c1 = a21 ^ (~a22 & a23);
+ a22 ^= ~a23 & a24;
+ a23 ^= ~a24 & a20;
+ a24 ^= ~a20 & a21;
+ a20 = c0;
+ a21 = c1;
+
+ // iota
+ a00 ^= KeccakRoundConstants[i];
+ }
+
+ A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
+ A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
+ A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
+ A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
+ A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
+ }
+
+ public virtual IMemoable Copy()
+ {
+ return new KeccakDigest(this);
+ }
+
+ public virtual void Reset(IMemoable other)
+ {
+ CopyIn((KeccakDigest)other);
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/LICENSE.md b/src/core/IronPython.Modules/hashlib/BouncyCastle/LICENSE.md
new file mode 100644
index 000000000..1f0172c5b
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/LICENSE.md
@@ -0,0 +1,15 @@
+MIT License (https://opensource.org/licenses/MIT)
+
+Copyright (c) 2000-2026 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org).
+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,
+sub license, 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.**
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/README.md b/src/core/IronPython.Modules/hashlib/BouncyCastle/README.md
new file mode 100644
index 000000000..107a39ae9
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/README.md
@@ -0,0 +1,27 @@
+# Bouncy Castle digests
+
+The files in this folder implement the SHA-3, SHAKE and BLAKE2 hash algorithms used by the `_sha3` and `_blake2` modules. They are copied from [Bouncy Castle C#](https://github.com/bcgit/bc-csharp) and are covered by its MIT license, see [LICENSE.md](LICENSE.md).
+
+## Provenance
+
+Copied from commit [`4ccf1e6`](https://github.com/bcgit/bc-csharp/tree/4ccf1e6d2785aa8c3745723c21a4aa027eace7df).
+
+| File | Upstream path |
+|---|---|
+| `Blake2bDigest.cs` | `crypto/src/crypto/digests/Blake2bDigest.cs` |
+| `Blake2b_X86.cs` | `crypto/src/crypto/digests/Blake2b_X86.cs` |
+| `Blake2sDigest.cs` | `crypto/src/crypto/digests/Blake2sDigest.cs` |
+| `Blake2s_X86.cs` | `crypto/src/crypto/digests/Blake2s_X86.cs` |
+| `IDigest.cs` | `crypto/src/crypto/IDigest.cs` |
+| `IMemoable.cs` | `crypto/src/util/IMemoable.cs` |
+| `IXof.cs` | `crypto/src/crypto/IXof.cs` |
+| `KeccakDigest.cs` | `crypto/src/crypto/digests/KeccakDigest.cs` |
+| `SHA3Digest.cs` | `crypto/src/crypto/digests/SHA3Digest.cs` |
+| `ShakeDigest.cs` | `crypto/src/crypto/digests/ShakeDigest.cs` |
+| `LICENSE.md` | `LICENSE.md` |
+
+### Local changes
+
+The copied files are unmodified, except that every top-level `public` type (class or interface) is changed to `internal`.
+
+The copied files depend on a few Bouncy Castle utility classes which are implemented in [`../BouncyCastleSupport.cs`](../BouncyCastleSupport.cs) (outside this folder so the repository style is enforced).
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/SHA3Digest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/SHA3Digest.cs
new file mode 100644
index 000000000..3a09aac7e
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/SHA3Digest.cs
@@ -0,0 +1,236 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math.Raw;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ ///
+ /// Implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ ///
+ ///
+ /// Following the naming conventions used in the C source code to enable easy review of the implementation.
+ ///
+ internal class Sha3Digest
+ : KeccakDigest
+ {
+ internal static void CalculateDigest(ulong[] input, int inputOffset, int inputLengthBits,
+ byte[] output, int outputOffset, int outputLengthBits)
+ {
+ int requiredInputLength = (inputLengthBits + 63) >> 6;
+ Check.DataLength(inputOffset > (input.Length - requiredInputLength), "input buffer too short");
+
+ int requiredOutputLength = (outputLengthBits + 7) >> 3;
+ Check.OutputLength(output, outputOffset, requiredOutputLength, "output buffer too short");
+
+ // Require byte-alignment (could be improved later)
+ if ((inputLengthBits & 7) != 0)
+ throw new ArgumentOutOfRangeException(nameof(inputLengthBits));
+
+ switch (outputLengthBits)
+ {
+ case 224:
+ case 256:
+ case 384:
+ case 512:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(outputLengthBits));
+ }
+
+ int rate = 1600 - (outputLengthBits << 1);
+ int rate64 = rate >> 6;
+
+ ulong[] state = new ulong[25];
+
+ // ABSORB
+
+ while (inputLengthBits >= rate)
+ {
+ Nat.XorTo64(rate64, input, inputOffset, state, 0);
+ inputOffset += rate64;
+ inputLengthBits -= rate;
+
+ KeccakPermutation(state);
+ }
+
+ int remaining64 = inputLengthBits >> 6;
+ int remainingPartial = inputLengthBits & 63;
+
+ Nat.XorTo64(remaining64, input, inputOffset, state, 0);
+
+ // If input not byte-aligned, the padding would be more complicated
+ ulong pad = 0b00000110UL;
+ if (remainingPartial != 0)
+ {
+ pad <<= remainingPartial;
+ pad |= input[inputOffset + remaining64] & ~(ulong.MaxValue << remainingPartial);
+ }
+
+ state[remaining64] ^= pad;
+ state[rate64 - 1] ^= 1UL << 63;
+
+ // SQUEEZE
+
+ KeccakPermutation(state);
+
+ Debug.Assert(outputLengthBits <= rate);
+ int count64 = outputLengthBits >> 6;
+ Pack.UInt64_To_LE(state, 0, count64, output, outputOffset);
+ if ((outputLengthBits & 32) != 0)
+ {
+ Pack.UInt32_To_LE((uint)state[count64], output, outputOffset + (count64 << 3));
+ }
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void CalculateDigest(ReadOnlySpan input, int inputLengthBits, Span output,
+ int outputLengthBits)
+ {
+ int requiredInputLength = (inputLengthBits + 63) >> 6;
+ Check.DataLength(input, requiredInputLength, "input buffer too short");
+
+ int requiredOutputLength = (outputLengthBits + 7) >> 3;
+ Check.OutputLength(output, requiredOutputLength, "output buffer too short");
+
+ // Require byte-alignment (could be improved later)
+ if ((inputLengthBits & 7) != 0)
+ throw new ArgumentOutOfRangeException(nameof(inputLengthBits));
+
+ switch (outputLengthBits)
+ {
+ case 224:
+ case 256:
+ case 384:
+ case 512:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(outputLengthBits));
+ }
+
+ int rate = 1600 - (outputLengthBits << 1);
+ int rate64 = rate >> 6;
+
+ Span state = stackalloc ulong[25];
+
+ // ABSORB
+
+ while (inputLengthBits >= rate)
+ {
+ Nat.XorTo64(rate64, input, state);
+ input = input[rate64..];
+ inputLengthBits -= rate;
+
+ KeccakPermutation(state);
+ }
+
+ int remaining64 = inputLengthBits >> 6;
+ int remainingPartial = inputLengthBits & 63;
+
+ Nat.XorTo64(remaining64, input, state);
+
+ // If input not byte-aligned, the padding would be more complicated
+ ulong pad = 0b00000110UL;
+ if (remainingPartial != 0)
+ {
+ pad <<= remainingPartial;
+ pad |= input[remaining64] & ~(ulong.MaxValue << remainingPartial);
+ }
+
+ state[remaining64] ^= pad;
+ state[rate64 - 1] ^= 1UL << 63;
+
+ // SQUEEZE
+
+ KeccakPermutation(state);
+
+ Debug.Assert(outputLengthBits <= rate);
+ int count64 = outputLengthBits >> 6;
+ Pack.UInt64_To_LE(state[..count64], output);
+ if ((outputLengthBits & 32) != 0)
+ {
+ Pack.UInt32_To_LE((uint)state[count64], output[(count64 << 3)..]);
+ }
+ }
+#endif
+
+ private static int CheckBitLength(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 224:
+ case 256:
+ case 384:
+ case 512:
+ return bitLength;
+ default:
+ throw new ArgumentException(bitLength + " not supported for SHA-3", "bitLength");
+ }
+ }
+
+ public Sha3Digest()
+ : this(256)
+ {
+ }
+
+ public Sha3Digest(int bitLength)
+ : base(CheckBitLength(bitLength))
+ {
+ }
+
+ public Sha3Digest(Sha3Digest source)
+ : base(source)
+ {
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHA3-" + fixedOutputLength; }
+ }
+
+ public override int DoFinal(byte[] output, int outOff)
+ {
+ AbsorbBits(0x02, 2);
+
+ return base.DoFinal(output, outOff);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public override int DoFinal(Span output)
+ {
+ AbsorbBits(0x02, 2);
+
+ return base.DoFinal(output);
+ }
+#endif
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected override int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
+ {
+ if (partialBits < 0 || partialBits > 7)
+ throw new ArgumentException("must be in the range [0,7]", "partialBits");
+
+ int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x02 << partialBits);
+ Debug.Assert(finalInput >= 0);
+ int finalBits = partialBits + 2;
+
+ if (finalBits >= 8)
+ {
+ Absorb((byte)finalInput);
+ finalBits -= 8;
+ finalInput >>= 8;
+ }
+
+ return base.DoFinal(output, outOff, (byte)finalInput, finalBits);
+ }
+
+ public override IMemoable Copy()
+ {
+ return new Sha3Digest(this);
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastle/ShakeDigest.cs b/src/core/IronPython.Modules/hashlib/BouncyCastle/ShakeDigest.cs
new file mode 100644
index 000000000..1f075b8f2
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastle/ShakeDigest.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ ///
+ /// Implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
+ ///
+ ///
+ /// Following the naming conventions used in the C source code to enable easy review of the implementation.
+ ///
+ internal class ShakeDigest
+ : KeccakDigest, IXof
+ {
+ private static int CheckBitLength(int bitLength)
+ {
+ switch (bitLength)
+ {
+ case 128:
+ case 256:
+ return bitLength;
+ default:
+ throw new ArgumentException(bitLength + " not supported for SHAKE", "bitLength");
+ }
+ }
+
+ public ShakeDigest()
+ : this(128)
+ {
+ }
+
+ public ShakeDigest(int bitLength)
+ : base(CheckBitLength(bitLength))
+ {
+ }
+
+ public ShakeDigest(ShakeDigest source)
+ : base(source)
+ {
+ }
+
+ public override string AlgorithmName
+ {
+ get { return "SHAKE" + fixedOutputLength; }
+ }
+
+ public override int GetDigestSize()
+ {
+ return fixedOutputLength >> 2;
+ }
+
+ public override int DoFinal(byte[] output, int outOff)
+ {
+ return OutputFinal(output, outOff, GetDigestSize());
+ }
+
+ public virtual int OutputFinal(byte[] output, int outOff, int outLen)
+ {
+ Check.OutputLength(output, outOff, outLen, "output buffer is too short");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return OutputFinal(output.AsSpan(outOff, outLen));
+#else
+ int length = Output(output, outOff, outLen);
+
+ Reset();
+
+ return length;
+#endif
+ }
+
+ public virtual int Output(byte[] output, int outOff, int outLen)
+ {
+ Check.OutputLength(output, outOff, outLen, "output buffer is too short");
+
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return Output(output.AsSpan(outOff, outLen));
+#else
+ if (!squeezing)
+ {
+ AbsorbBits(0x0F, 4);
+ }
+
+ Squeeze(output, outOff, (long)outLen << 3);
+
+ return outLen;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public override int DoFinal(Span output)
+ {
+ return OutputFinal(output[..GetDigestSize()]);
+ }
+
+ public virtual int OutputFinal(Span output)
+ {
+ int length = Output(output);
+
+ Reset();
+
+ return length;
+ }
+
+ public virtual int Output(Span output)
+ {
+ if (!squeezing)
+ {
+ AbsorbBits(0x0F, 4);
+ }
+
+ Squeeze(output);
+
+ return output.Length;
+ }
+#endif
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected override int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
+ {
+ return OutputFinal(output, outOff, GetDigestSize(), partialByte, partialBits);
+ }
+
+ /*
+ * TODO Possible API change to support partial-byte suffixes.
+ */
+ protected virtual int OutputFinal(byte[] output, int outOff, int outLen, byte partialByte, int partialBits)
+ {
+ if (partialBits < 0 || partialBits > 7)
+ throw new ArgumentException("must be in the range [0,7]", "partialBits");
+
+ int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits);
+ Debug.Assert(finalInput >= 0);
+ int finalBits = partialBits + 4;
+
+ if (finalBits >= 8)
+ {
+ Absorb((byte)finalInput);
+ finalBits -= 8;
+ finalInput >>= 8;
+ }
+
+ if (finalBits > 0)
+ {
+ AbsorbBits(finalInput, finalBits);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Squeeze(output.AsSpan(outOff, outLen));
+#else
+ Squeeze(output, outOff, (long)outLen << 3);
+#endif
+
+ Reset();
+
+ return outLen;
+ }
+
+ public override IMemoable Copy()
+ {
+ return new ShakeDigest(this);
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/BouncyCastleSupport.cs b/src/core/IronPython.Modules/hashlib/BouncyCastleSupport.cs
new file mode 100644
index 000000000..afd989ac9
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/BouncyCastleSupport.cs
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information.
+
+// Minimal replacements for the Bouncy Castle utility classes used by the digests in the BouncyCastle folder.
+// Only the members needed by those files are implemented. The digest files themselves are kept
+// identical to upstream so that they can be refreshed easily (see BouncyCastle/README.md).
+
+using System;
+
+namespace Org.BouncyCastle.Crypto {
+ internal static class Check {
+ internal static void DataLength(bool condition, string message) {
+ if (condition) throw new ArgumentException(message);
+ }
+
+ internal static void OutputLength(byte[] buf, int off, int len, string message) {
+ if (off > buf.Length - len) throw new ArgumentException(message);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void DataLength(ReadOnlySpan input, int len, string message) {
+ if (input.Length < len) throw new ArgumentException(message);
+ }
+
+ internal static void OutputLength(Span output, int len, string message) {
+ if (output.Length < len) throw new ArgumentException(message);
+ }
+#endif
+ }
+}
+
+namespace Org.BouncyCastle.Crypto.Utilities {
+ internal static class Pack {
+ internal static uint LE_To_UInt32(byte[] bs, int off) {
+ return bs[off]
+ | (uint)bs[off + 1] << 8
+ | (uint)bs[off + 2] << 16
+ | (uint)bs[off + 3] << 24;
+ }
+
+ internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns) {
+ for (int i = 0; i < ns.Length; ++i) {
+ ns[i] = LE_To_UInt32(bs, off);
+ off += 4;
+ }
+ }
+
+ internal static ulong LE_To_UInt64(byte[] bs, int off) {
+ uint lo = LE_To_UInt32(bs, off);
+ uint hi = LE_To_UInt32(bs, off + 4);
+ return (ulong)hi << 32 | lo;
+ }
+
+ internal static void LE_To_UInt64(byte[] bs, int off, ulong[] ns) {
+ for (int i = 0; i < ns.Length; ++i) {
+ ns[i] = LE_To_UInt64(bs, off);
+ off += 8;
+ }
+ }
+
+ internal static void UInt32_To_LE(uint n, byte[] bs, int off) {
+ bs[off] = (byte)n;
+ bs[off + 1] = (byte)(n >> 8);
+ bs[off + 2] = (byte)(n >> 16);
+ bs[off + 3] = (byte)(n >> 24);
+ }
+
+ internal static void UInt32_To_LE(uint[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) {
+ for (int i = 0; i < nsLen; ++i) {
+ UInt32_To_LE(ns[nsOff + i], bs, bsOff);
+ bsOff += 4;
+ }
+ }
+
+ internal static void UInt32_To_LE_Low(uint n, byte[] bs, int off, int len) {
+ for (int i = 0; i < len; ++i) {
+ bs[off + i] = (byte)n;
+ n >>= 8;
+ }
+ }
+
+ internal static void UInt64_To_LE(ulong n, byte[] bs, int off) {
+ UInt32_To_LE((uint)n, bs, off);
+ UInt32_To_LE((uint)(n >> 32), bs, off + 4);
+ }
+
+ internal static void UInt64_To_LE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) {
+ for (int i = 0; i < nsLen; ++i) {
+ UInt64_To_LE(ns[nsOff + i], bs, bsOff);
+ bsOff += 8;
+ }
+ }
+
+ internal static void UInt64_To_LE_Low(ulong n, byte[] bs, int off, int len) {
+ for (int i = 0; i < len; ++i) {
+ bs[off + i] = (byte)n;
+ n >>= 8;
+ }
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void LE_To_UInt32(ReadOnlySpan bs, Span ns) {
+ for (int i = 0; i < ns.Length; ++i) {
+ ns[i] = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(bs.Slice(i * 4));
+ }
+ }
+
+ internal static ulong LE_To_UInt64(ReadOnlySpan bs) {
+ return System.Buffers.Binary.BinaryPrimitives.ReadUInt64LittleEndian(bs);
+ }
+
+ internal static void LE_To_UInt64(ReadOnlySpan bs, Span ns) {
+ for (int i = 0; i < ns.Length; ++i) {
+ ns[i] = System.Buffers.Binary.BinaryPrimitives.ReadUInt64LittleEndian(bs.Slice(i * 8));
+ }
+ }
+
+ internal static void UInt32_To_LE(uint n, Span bs) {
+ System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(bs, n);
+ }
+
+ internal static void UInt32_To_LE(ReadOnlySpan ns, Span bs) {
+ for (int i = 0; i < ns.Length; ++i) {
+ System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(bs.Slice(i * 4), ns[i]);
+ }
+ }
+
+ internal static void UInt32_To_LE_Low(uint n, Span bs) {
+ for (int i = 0; i < bs.Length; ++i) {
+ bs[i] = (byte)n;
+ n >>= 8;
+ }
+ }
+
+ internal static void UInt64_To_LE(ReadOnlySpan ns, Span bs) {
+ for (int i = 0; i < ns.Length; ++i) {
+ System.Buffers.Binary.BinaryPrimitives.WriteUInt64LittleEndian(bs.Slice(i * 8), ns[i]);
+ }
+ }
+
+ internal static void UInt64_To_LE_Low(ulong n, Span bs) {
+ for (int i = 0; i < bs.Length; ++i) {
+ bs[i] = (byte)n;
+ n >>= 8;
+ }
+ }
+#endif
+ }
+}
+
+namespace Org.BouncyCastle.Math.Raw {
+ internal static class Nat {
+ internal static void XorTo64(int len, ulong[] x, int xOff, ulong[] z, int zOff) {
+ for (int i = 0; i < len; ++i) {
+ z[zOff + i] ^= x[xOff + i];
+ }
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void XorTo64(int len, ReadOnlySpan x, Span z) {
+ for (int i = 0; i < len; ++i) {
+ z[i] ^= x[i];
+ }
+ }
+#endif
+ }
+}
+
+namespace Org.BouncyCastle.Utilities {
+ internal static class Arrays {
+ internal static byte[] Clone(byte[] data) {
+ return data == null ? null : (byte[])data.Clone();
+ }
+
+ internal static byte[] CopyBuffer(byte[] buf) {
+ return (byte[])buf.Clone();
+ }
+
+ internal static void Fill(byte[] buf, byte b) {
+ for (int i = 0; i < buf.Length; ++i) {
+ buf[i] = b;
+ }
+ }
+
+ internal static bool IsNullOrEmpty(byte[] array) {
+ return array == null || array.Length < 1;
+ }
+ }
+
+ internal static class Integers {
+ internal static uint RotateRight(uint i, int distance) {
+ return (i >> distance) | (i << -distance);
+ }
+ }
+
+ internal static class Longs {
+ internal static ulong RotateLeft(ulong i, int distance) {
+ return (i << distance) | (i >> -distance);
+ }
+
+ internal static ulong RotateRight(ulong i, int distance) {
+ return (i >> distance) | (i << -distance);
+ }
+ }
+}
+
+#if NETCOREAPP3_0_OR_GREATER
+namespace Org.BouncyCastle.Runtime.Intrinsics {
+ internal static class Vector {
+ internal static bool IsPackedLittleEndian => BitConverter.IsLittleEndian;
+ }
+}
+
+namespace Org.BouncyCastle.Runtime.Intrinsics.X86 {
+ internal static class Avx2 {
+ internal static bool IsEnabled => System.Runtime.Intrinsics.X86.Avx2.IsSupported;
+ }
+
+ internal static class Sse41 {
+ internal static bool IsEnabled => System.Runtime.Intrinsics.X86.Sse41.IsSupported;
+ }
+}
+#endif
diff --git a/src/core/IronPython.Modules/hashlib/_blake2.cs b/src/core/IronPython.Modules/hashlib/_blake2.cs
new file mode 100644
index 000000000..21070bf1d
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/_blake2.cs
@@ -0,0 +1,211 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using System;
+
+using IronPython.Runtime;
+using IronPython.Runtime.Operations;
+
+using Microsoft.Scripting.Runtime;
+
+using Org.BouncyCastle.Crypto.Digests;
+
+[assembly: PythonModule("_blake2", typeof(IronPython.Modules.PythonBlake2))]
+namespace IronPython.Modules {
+ public static class PythonBlake2 {
+ public const string __doc__ = "BLAKE2b and BLAKE2s hash algorithms";
+
+ public const int BLAKE2B_SALT_SIZE = blake2b.SALT_SIZE;
+ public const int BLAKE2B_PERSON_SIZE = blake2b.PERSON_SIZE;
+ public const int BLAKE2B_MAX_KEY_SIZE = blake2b.MAX_KEY_SIZE;
+ public const int BLAKE2B_MAX_DIGEST_SIZE = blake2b.MAX_DIGEST_SIZE;
+
+ public const int BLAKE2S_SALT_SIZE = blake2s.SALT_SIZE;
+ public const int BLAKE2S_PERSON_SIZE = blake2s.PERSON_SIZE;
+ public const int BLAKE2S_MAX_KEY_SIZE = blake2s.MAX_KEY_SIZE;
+ public const int BLAKE2S_MAX_DIGEST_SIZE = blake2s.MAX_DIGEST_SIZE;
+
+ [PythonType]
+ public sealed class blake2b {
+ public const int SALT_SIZE = 16;
+ public const int PERSON_SIZE = 16;
+ public const int MAX_KEY_SIZE = 64;
+ public const int MAX_DIGEST_SIZE = 64;
+ private const long MAX_NODE_OFFSET = long.MaxValue;
+
+ private readonly Blake2bDigest _digest;
+
+ public blake2b(IBufferProtocol? data = null, int digest_size = MAX_DIGEST_SIZE,
+ IBufferProtocol? key = null, IBufferProtocol? salt = null, IBufferProtocol? person = null,
+ int fanout = 1, int depth = 1, long leaf_size = 0, long node_offset = 0, int node_depth = 0,
+ int inner_size = 0, bool last_node = false) {
+
+ CheckParameters(digest_size, MAX_DIGEST_SIZE, MAX_NODE_OFFSET, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node);
+
+ _digest = new Blake2bDigest(
+ GetParameter(key, MAX_KEY_SIZE, "key", pad: false),
+ digest_size,
+ GetParameter(salt, SALT_SIZE, "salt", pad: true),
+ GetParameter(person, PERSON_SIZE, "person", pad: true));
+
+ if (data != null) update(data);
+ }
+
+ private blake2b(Blake2bDigest digest) {
+ _digest = digest;
+ }
+
+ public string name => "blake2b";
+
+ public int digest_size => _digest.GetDigestSize();
+
+ public int block_size => _digest.GetByteLength();
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] IBufferProtocol data) {
+ lock (_digest) {
+ HashHelpers.Update(_digest, data);
+ }
+ }
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] string data) {
+ // TODO: error message changes in Python 3.9
+ throw PythonOps.TypeError("Unicode-objects must be encoded before hashing");
+ }
+
+ [Documentation("Return the digest value as a bytes object.")]
+ public Bytes digest() {
+ var copy = CopyDigest();
+ var res = new byte[copy.GetDigestSize()];
+ copy.DoFinal(res, 0);
+ return Bytes.Make(res);
+ }
+
+ [Documentation("Return the digest value as a string of hexadecimal digits.")]
+ public string hexdigest() => HashHelpers.ToHex(digest().UnsafeByteArray);
+
+ [Documentation("Return a copy of the hash object.")]
+ public blake2b copy() => new blake2b(CopyDigest());
+
+ private Blake2bDigest CopyDigest() {
+ lock (_digest) {
+ return new Blake2bDigest(_digest);
+ }
+ }
+ }
+
+ [PythonType]
+ public sealed class blake2s {
+ public const int SALT_SIZE = 8;
+ public const int PERSON_SIZE = 8;
+ public const int MAX_KEY_SIZE = 32;
+ public const int MAX_DIGEST_SIZE = 32;
+ private const long MAX_NODE_OFFSET = (1L << 48) - 1;
+
+ private readonly Blake2sDigest _digest;
+
+ public blake2s(IBufferProtocol? data = null, int digest_size = MAX_DIGEST_SIZE,
+ IBufferProtocol? key = null, IBufferProtocol? salt = null, IBufferProtocol? person = null,
+ int fanout = 1, int depth = 1, long leaf_size = 0, long node_offset = 0, int node_depth = 0,
+ int inner_size = 0, bool last_node = false) {
+
+ CheckParameters(digest_size, MAX_DIGEST_SIZE, MAX_NODE_OFFSET, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node);
+
+ _digest = new Blake2sDigest(
+ GetParameter(key, MAX_KEY_SIZE, "key", pad: false),
+ digest_size,
+ GetParameter(salt, SALT_SIZE, "salt", pad: true),
+ GetParameter(person, PERSON_SIZE, "person", pad: true));
+
+ if (data != null) update(data);
+ }
+
+ private blake2s(Blake2sDigest digest) {
+ _digest = digest;
+ }
+
+ public string name => "blake2s";
+
+ public int digest_size => _digest.GetDigestSize();
+
+ public int block_size => _digest.GetByteLength();
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] IBufferProtocol data) {
+ lock (_digest) {
+ HashHelpers.Update(_digest, data);
+ }
+ }
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] string data) {
+ throw PythonOps.TypeError("Unicode-objects must be encoded before hashing");
+ }
+
+ [Documentation("Return the digest value as a bytes object.")]
+ public Bytes digest() {
+ var copy = CopyDigest();
+ var res = new byte[copy.GetDigestSize()];
+ copy.DoFinal(res, 0);
+ return Bytes.Make(res);
+ }
+
+ [Documentation("Return the digest value as a string of hexadecimal digits.")]
+ public string hexdigest() => HashHelpers.ToHex(digest().UnsafeByteArray);
+
+ [Documentation("Return a copy of the hash object.")]
+ public blake2s copy() => new blake2s(CopyDigest());
+
+ private Blake2sDigest CopyDigest() {
+ lock (_digest) {
+ return new Blake2sDigest(_digest);
+ }
+ }
+ }
+
+ private static void CheckParameters(int digest_size, int maxDigestSize, long maxNodeOffset, int fanout, int depth, long leaf_size,
+ long node_offset, int node_depth, int inner_size, bool last_node) {
+
+ if (digest_size < 1 || digest_size > maxDigestSize)
+ throw PythonOps.ValueError("digest_size must be between 1 and {0} bytes", maxDigestSize);
+ if (fanout < 0 || fanout > 255)
+ throw PythonOps.ValueError("fanout must be between 0 and 255");
+ if (depth < 1 || depth > 255)
+ throw PythonOps.ValueError("depth must be between 1 and 255");
+ if (leaf_size < 0 || leaf_size > uint.MaxValue)
+ throw PythonOps.OverflowError("leaf_size is too large");
+ if (node_offset < 0 || node_offset > maxNodeOffset)
+ throw PythonOps.OverflowError("node_offset is too large");
+ if (node_depth < 0 || node_depth > 255)
+ throw PythonOps.ValueError("node_depth must be between 0 and 255");
+ if (inner_size < 0 || inner_size > maxDigestSize)
+ throw PythonOps.ValueError("inner_size must be between 0 and is {0}", maxDigestSize);
+
+ // The underlying implementation does not support the tree hashing mode.
+ if (fanout != 1 || depth != 1 || leaf_size != 0 || node_offset != 0 || node_depth != 0 || inner_size != 0 || last_node)
+ throw new NotImplementedException("BLAKE2 tree hashing parameters are not supported");
+ }
+
+ private static byte[]? GetParameter(IBufferProtocol? value, int maxSize, string name, bool pad) {
+ if (value is null) return null;
+
+ byte[] bytes;
+ using (var buffer = value.GetBuffer()) {
+ bytes = buffer.ToArray();
+ }
+
+ if (bytes.Length > maxSize)
+ throw PythonOps.ValueError("maximum {0} length is {1} bytes", name, maxSize);
+ if (bytes.Length == 0)
+ return null;
+ if (pad && bytes.Length < maxSize)
+ Array.Resize(ref bytes, maxSize);
+
+ return bytes;
+ }
+ }
+}
diff --git a/src/core/IronPython.Modules/hashlib/_hashlib.cs b/src/core/IronPython.Modules/hashlib/_hashlib.cs
index f463224ca..127a0cc85 100644
--- a/src/core/IronPython.Modules/hashlib/_hashlib.cs
+++ b/src/core/IronPython.Modules/hashlib/_hashlib.cs
@@ -41,9 +41,9 @@ internal HashBase(string name, int blocksize, int digestsize) {
[Documentation("update(string) -> None (update digest with string data)")]
public void update([NotNone] IBufferProtocol data) {
using var buffer = data.GetBuffer();
- byte[] bytes = buffer.ToArray();
+ byte[] bytes = buffer.AsUnsafeArray() ?? buffer.ToArray();
lock (_hasher) {
- _hasher.TransformBlock(bytes, 0, bytes.Length, bytes, 0);
+ _hasher.TransformBlock(bytes, 0, bytes.Length, null, 0);
}
}
public void update([NotNone] string data) {
@@ -58,16 +58,7 @@ public Bytes digest() {
}
[Documentation("hexdigest() -> string (current digest as hex digits)")]
- public string hexdigest() {
- T copy = CloneHasher();
- copy.TransformFinalBlock(_empty, 0, 0);
-
- StringBuilder result = new StringBuilder(2 * copy.Hash.Length);
- for (int i = 0; i < copy.Hash.Length; i++) {
- result.Append(copy.Hash[i].ToString("x2"));
- }
- return result.ToString();
- }
+ public string hexdigest() => HashHelpers.ToHex(digest().UnsafeByteArray);
public abstract HashBase copy();
@@ -106,4 +97,26 @@ protected T CloneHasher() {
return clone;
}
}
+
+ internal static class HashHelpers {
+ internal static void Update(Org.BouncyCastle.Crypto.IDigest digest, IBufferProtocol data) {
+ using var buffer = data.GetBuffer();
+#if NETCOREAPP
+ if (buffer.IsCContiguous()) {
+ digest.BlockUpdate(buffer.AsReadOnlySpan());
+ return;
+ }
+#endif
+ byte[] bytes = buffer.AsUnsafeArray() ?? buffer.ToArray();
+ digest.BlockUpdate(bytes, 0, bytes.Length);
+ }
+
+ internal static string ToHex(byte[] bytes) {
+ StringBuilder result = new StringBuilder(2 * bytes.Length);
+ for (int i = 0; i < bytes.Length; i++) {
+ result.Append(bytes[i].ToString("x2"));
+ }
+ return result.ToString();
+ }
+ }
}
diff --git a/src/core/IronPython.Modules/hashlib/_sha3.cs b/src/core/IronPython.Modules/hashlib/_sha3.cs
new file mode 100644
index 000000000..efb43a47e
--- /dev/null
+++ b/src/core/IronPython.Modules/hashlib/_sha3.cs
@@ -0,0 +1,178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using IronPython.Runtime;
+using IronPython.Runtime.Operations;
+
+using Microsoft.Scripting.Runtime;
+
+using Org.BouncyCastle.Crypto.Digests;
+
+[assembly: PythonModule("_sha3", typeof(IronPython.Modules.PythonSha3))]
+namespace IronPython.Modules {
+ public static class PythonSha3 {
+ public const string __doc__ = "SHA-3 and SHAKE hash algorithms";
+
+ [PythonHidden]
+ public abstract class Sha3Base {
+ private protected readonly KeccakDigest _digest;
+
+ private protected Sha3Base(KeccakDigest digest) {
+ _digest = digest;
+ }
+
+ public abstract string name { get; }
+
+ public int block_size => _digest.GetByteLength();
+
+ public int _rate_bits => _digest.GetByteLength() * 8;
+
+ public int _capacity_bits => 1600 - _rate_bits;
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] IBufferProtocol data) {
+ lock (_digest) {
+ HashHelpers.Update(_digest, data);
+ }
+ }
+
+ [Documentation("Update this hash object's state with the provided bytes-like object.")]
+ public void update([NotNone] string data) {
+ throw PythonOps.TypeError("Unicode-objects must be encoded before hashing");
+ }
+
+ private protected T CopyDigest() where T : KeccakDigest {
+ lock (_digest) {
+ return (T)_digest.Copy();
+ }
+ }
+ }
+
+ [PythonHidden]
+ public abstract class Sha3FixedBase : Sha3Base {
+ private protected Sha3FixedBase(int bitLength) : base(new Sha3Digest(bitLength)) { }
+
+ private protected Sha3FixedBase(Sha3Digest digest) : base(digest) { }
+
+ public int digest_size => _digest.GetDigestSize();
+
+ public Bytes _suffix => Bytes.Make([0x06]);
+
+ [Documentation("Return the digest value as a bytes object.")]
+ public Bytes digest() {
+ var copy = CopyDigest();
+ var res = new byte[copy.GetDigestSize()];
+ copy.DoFinal(res, 0);
+ return Bytes.Make(res);
+ }
+
+ [Documentation("Return the digest value as a string of hexadecimal digits.")]
+ public string hexdigest() => HashHelpers.ToHex(digest().UnsafeByteArray);
+ }
+
+ [PythonHidden]
+ public abstract class ShakeBase : Sha3Base {
+ private protected ShakeBase(int bitLength) : base(new ShakeDigest(bitLength)) { }
+
+ private protected ShakeBase(ShakeDigest digest) : base(digest) { }
+
+ public int digest_size => 0;
+
+ public Bytes _suffix => Bytes.Make([0x1f]);
+
+ [Documentation("Return the digest value as a bytes object.")]
+ public Bytes digest(int length) {
+ if (length < 0) throw PythonOps.ValueError("length must be non-negative");
+ if (length >= 1 << 29) throw PythonOps.ValueError("length is too large");
+ var copy = CopyDigest();
+ var res = new byte[length];
+ copy.OutputFinal(res, 0, length);
+ return Bytes.Make(res);
+ }
+
+ [Documentation("Return the digest value as a string of hexadecimal digits.")]
+ public string hexdigest(int length) => HashHelpers.ToHex(digest(length).UnsafeByteArray);
+ }
+
+ [PythonType]
+ public sealed class sha3_224 : Sha3FixedBase {
+ public sha3_224() : base(224) { }
+ public sha3_224([NotNone] IBufferProtocol data) : this() { update(data); }
+ public sha3_224([NotNone] string data) : this() { update(data); }
+ private sha3_224(Sha3Digest digest) : base(digest) { }
+
+ public override string name => "sha3_224";
+
+ [Documentation("Return a copy of the hash object.")]
+ public sha3_224 copy() => new sha3_224(CopyDigest());
+ }
+
+ [PythonType]
+ public sealed class sha3_256 : Sha3FixedBase {
+ public sha3_256() : base(256) { }
+ public sha3_256([NotNone] IBufferProtocol data) : this() { update(data); }
+ public sha3_256([NotNone] string data) : this() { update(data); }
+ private sha3_256(Sha3Digest digest) : base(digest) { }
+
+ public override string name => "sha3_256";
+
+ [Documentation("Return a copy of the hash object.")]
+ public sha3_256 copy() => new sha3_256(CopyDigest());
+ }
+
+ [PythonType]
+ public sealed class sha3_384 : Sha3FixedBase {
+ public sha3_384() : base(384) { }
+ public sha3_384([NotNone] IBufferProtocol data) : this() { update(data); }
+ public sha3_384([NotNone] string data) : this() { update(data); }
+ private sha3_384(Sha3Digest digest) : base(digest) { }
+
+ public override string name => "sha3_384";
+
+ [Documentation("Return a copy of the hash object.")]
+ public sha3_384 copy() => new sha3_384(CopyDigest());
+ }
+
+ [PythonType]
+ public sealed class sha3_512 : Sha3FixedBase {
+ public sha3_512() : base(512) { }
+ public sha3_512([NotNone] IBufferProtocol data) : this() { update(data); }
+ public sha3_512([NotNone] string data) : this() { update(data); }
+ private sha3_512(Sha3Digest digest) : base(digest) { }
+
+ public override string name => "sha3_512";
+
+ [Documentation("Return a copy of the hash object.")]
+ public sha3_512 copy() => new sha3_512(CopyDigest());
+ }
+
+ [PythonType]
+ public sealed class shake_128 : ShakeBase {
+ public shake_128() : base(128) { }
+ public shake_128([NotNone] IBufferProtocol data) : this() { update(data); }
+ public shake_128([NotNone] string data) : this() { update(data); }
+ private shake_128(ShakeDigest digest) : base(digest) { }
+
+ public override string name => "shake_128";
+
+ [Documentation("Return a copy of the hash object.")]
+ public shake_128 copy() => new shake_128(CopyDigest());
+ }
+
+ [PythonType]
+ public sealed class shake_256 : ShakeBase {
+ public shake_256() : base(256) { }
+ public shake_256([NotNone] IBufferProtocol data) : this() { update(data); }
+ public shake_256([NotNone] string data) : this() { update(data); }
+ private shake_256(ShakeDigest digest) : base(digest) { }
+
+ public override string name => "shake_256";
+
+ [Documentation("Return a copy of the hash object.")]
+ public shake_256 copy() => new shake_256(CopyDigest());
+ }
+ }
+}
diff --git a/tests/suite/modules/misc/test__blake2.py b/tests/suite/modules/misc/test__blake2.py
new file mode 100644
index 000000000..b4d5ac1a2
--- /dev/null
+++ b/tests/suite/modules/misc/test__blake2.py
@@ -0,0 +1,80 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the Apache 2.0 License.
+# See the LICENSE file in the project root for more information.
+
+'''
+Tests for the _blake2 module which are not covered by test_hashlib from the StdLib. The StdLib
+tests which check salt, person and parameter validation also use tree hashing which is not
+supported. Expected values were generated with CPython 3.12.
+'''
+
+import _blake2
+import unittest
+
+from iptest import is_cli, run_test
+
+def data(n):
+ return bytes((i * 7 + 3) & 0xff for i in range(n))
+
+PARAMS = [
+ ('blake2b', {'salt': b'salt'}, 0, 'e13acc0cbc033165279da570723a79e4f35d37141b094225b4d26d18e9a6fa7ad1a74ae3ed749b7aa9beb70a25e73d35b2e4a94477a8d54f00330331481c013a'),
+ ('blake2b', {'salt': b'salt'}, 200, 'c068456b3c55510c2766676e8ea00122628ce5cef06e82a1007c25c940e4fd885464fd2f1b80d395f8fec6c73c52a382b0c508b7f13dc729a912d5198090a439'),
+ ('blake2b', {'salt': b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f', 'person': b'me'}, 0, 'ca6aa446967c6f33d49e373173186554863b28ed672f4ed8a3641f23d66a4412c97f6b481fadace0560af4975125c7b40195eceef5508d560b5e0b5a72ee6fe3'),
+ ('blake2b', {'salt': b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f', 'person': b'me'}, 200, '50624555fe019e59e6c1bfb37ccf4a85579d71020d6422159477b5df2c9886ca8ff6b4c090c4f0b201db05907a84b37bc69bbb06f47a4744ddfdbd38f8a41ecf'),
+ ('blake2b', {'key': b'k', 'salt': b's', 'person': b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f', 'digest_size': 33}, 0, 'eefb2381791f179994c8be25dca7c0877fbe91096b2d9a4670190ad6f61e177297'),
+ ('blake2b', {'key': b'k', 'salt': b's', 'person': b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f', 'digest_size': 33}, 200, '764f4fbf33a2afbcafb25a511c6f34b3ea10975684c4e67036c922d0bf8c649b32'),
+ ('blake2s', {'salt': b'salt'}, 0, '57a3dca94c3e8c943d2007ad780932ff95f585ddc7c9dca3a0d9f2481b9ab4cc'),
+ ('blake2s', {'salt': b'salt'}, 200, '3b2edb1052b39c7647ad808e6f7d9ea7b28363f5e874538be7468cf0b2c70170'),
+ ('blake2s', {'salt': b'\x00\x01\x02\x03\x04\x05\x06\x07', 'person': b'me'}, 0, 'ced8049ec88e4dd872b5b038161dd8706ce9b386d3d2ab6cca127e16ece2c950'),
+ ('blake2s', {'salt': b'\x00\x01\x02\x03\x04\x05\x06\x07', 'person': b'me'}, 200, '195a611949a7860107838cb7366f5940208eb0c05328f97944b3d4959971a5a3'),
+ ('blake2s', {'key': b'k', 'salt': b's', 'person': b'\x00\x01\x02\x03\x04\x05\x06\x07', 'digest_size': 17}, 0, 'aae1c76391b9f3e5285a8c1d8bac9f7649'),
+ ('blake2s', {'key': b'k', 'salt': b's', 'person': b'\x00\x01\x02\x03\x04\x05\x06\x07', 'digest_size': 17}, 200, 'cfb552122d9f5ada0bc95cece7d44d861d'),
+]
+
+class _Blake2Test(unittest.TestCase):
+
+ def test_constants(self):
+ self.assertEqual(_blake2.BLAKE2B_SALT_SIZE, 16)
+ self.assertEqual(_blake2.BLAKE2B_PERSON_SIZE, 16)
+ self.assertEqual(_blake2.BLAKE2B_MAX_KEY_SIZE, 64)
+ self.assertEqual(_blake2.BLAKE2B_MAX_DIGEST_SIZE, 64)
+ self.assertEqual(_blake2.BLAKE2S_SALT_SIZE, 8)
+ self.assertEqual(_blake2.BLAKE2S_PERSON_SIZE, 8)
+ self.assertEqual(_blake2.BLAKE2S_MAX_KEY_SIZE, 32)
+ self.assertEqual(_blake2.BLAKE2S_MAX_DIGEST_SIZE, 32)
+
+ def test_parameters(self):
+ for name, kwargs, n, hexdigest in PARAMS:
+ with self.subTest(name=name, kwargs=kwargs, n=n):
+ ctor = getattr(_blake2, name)
+ self.assertEqual(ctor(data(n), **kwargs).hexdigest(), hexdigest)
+ h = ctor(**kwargs)
+ h.update(data(n))
+ self.assertEqual(h.copy().hexdigest(), hexdigest)
+
+ def test_errors(self):
+ for ctor in [_blake2.blake2b, _blake2.blake2s]:
+ with self.subTest(ctor=ctor):
+ self.assertRaises(ValueError, ctor, digest_size=0)
+ self.assertRaises(ValueError, ctor, digest_size=ctor.MAX_DIGEST_SIZE + 1)
+ self.assertRaises(ValueError, ctor, key=b'x' * (ctor.MAX_KEY_SIZE + 1))
+ self.assertRaises(ValueError, ctor, salt=b'x' * (ctor.SALT_SIZE + 1))
+ self.assertRaises(ValueError, ctor, person=b'x' * (ctor.PERSON_SIZE + 1))
+ self.assertRaises(ValueError, ctor, fanout=256)
+ self.assertRaises(ValueError, ctor, depth=0)
+ self.assertRaises(ValueError, ctor, node_depth=256)
+ self.assertRaises(ValueError, ctor, inner_size=ctor.MAX_DIGEST_SIZE + 1)
+ self.assertRaises((ValueError, OverflowError), ctor, leaf_size=-1)
+ self.assertRaises(OverflowError, ctor, leaf_size=1<<32)
+ self.assertRaises((ValueError, OverflowError), ctor, node_offset=-1)
+ self.assertRaises(OverflowError, _blake2.blake2s, node_offset=1<<48)
+
+ @unittest.skipUnless(is_cli, 'IronPython does not support tree hashing')
+ def test_tree_hashing_not_supported(self):
+ for ctor in [_blake2.blake2b, _blake2.blake2s]:
+ with self.subTest(ctor=ctor):
+ self.assertRaises(NotImplementedError, ctor, fanout=2)
+ self.assertRaises(NotImplementedError, ctor, last_node=True)
+ self.assertRaises(NotImplementedError, ctor, node_offset=1)
+
+run_test(__name__)
diff --git a/tests/suite/stdlib/test_hashlib.py b/tests/suite/stdlib/test_hashlib.py
new file mode 100644
index 000000000..80b7050b6
--- /dev/null
+++ b/tests/suite/stdlib/test_hashlib.py
@@ -0,0 +1,35 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the Apache 2.0 License.
+# See the LICENSE file in the project root for more information.
+
+##
+## Run selected tests from test_hashlib from StdLib
+##
+
+import sys
+
+from iptest import is_ironpython, generate_suite, run_test
+
+import test.test_hashlib
+
+def load_tests(loader, standard_tests, pattern):
+ tests = loader.loadTestsFromModule(test.test_hashlib, pattern=pattern)
+
+ if is_ironpython:
+ failing_tests = []
+ if sys.version_info >= (3, 6):
+ failing_tests += [
+ test.test_hashlib.HashLibTestCase('test_blake2b'), # NotImplementedError: BLAKE2 tree hashing parameters are not supported
+ test.test_hashlib.HashLibTestCase('test_blake2s'), # NotImplementedError: BLAKE2 tree hashing parameters are not supported
+ test.test_hashlib.HashLibTestCase('test_case_blake2b_all_parameters'), # NotImplementedError: BLAKE2 tree hashing parameters are not supported
+ test.test_hashlib.HashLibTestCase('test_case_blake2s_all_parameters'), # NotImplementedError: BLAKE2 tree hashing parameters are not supported
+ ]
+
+ skip_tests = []
+
+ return generate_suite(tests, failing_tests, skip_tests)
+
+ else:
+ return tests
+
+run_test(__name__)