diff options
-rw-r--r-- | crypto/src/crypto/digests/KeccakDigest.cs | 4 | ||||
-rw-r--r-- | crypto/src/crypto/digests/SHA3Digest.cs | 142 |
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) |