summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/src/crypto/digests/KeccakDigest.cs4
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs142
2 files changed, 146 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs
index 60bdcdc0a..b7f7a355d 100644
--- a/crypto/src/crypto/digests/KeccakDigest.cs
+++ b/crypto/src/crypto/digests/KeccakDigest.cs
@@ -404,7 +404,11 @@ namespace Org.BouncyCastle.Crypto.Digests
             this.bitsInQueue = rate;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void KeccakPermutation(Span<ulong> A)
+#else
         internal static void KeccakPermutation(ulong[] A)
+#endif
         {
             var bounds = A[24];
 
diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs
index 778c453d8..60ef8768a 100644
--- a/crypto/src/crypto/digests/SHA3Digest.cs
+++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Diagnostics;
 
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
@@ -14,6 +16,146 @@ namespace Org.BouncyCastle.Crypto.Digests
     public 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<ulong> input, int inputLengthBits, Span<byte> 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<ulong> 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)