diff options
author | gefeili <gli@keyfactor.com> | 2023-02-09 11:31:33 +1030 |
---|---|---|
committer | gefeili <gli@keyfactor.com> | 2023-02-09 11:31:33 +1030 |
commit | 242b0985e06f83347889c18ead32b41ef3d73e9f (patch) | |
tree | b5f82738d3a1a9c260a1efd93bf857b1436f7003 /crypto/src | |
parent | Refactor AsconEngine (diff) | |
download | BouncyCastle.NET-ed25519-242b0985e06f83347889c18ead32b41ef3d73e9f.tar.xz |
Add Ascon Hash to the master branch
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/digests/AsconDigest.cs | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/AsconDigest.cs b/crypto/src/crypto/digests/AsconDigest.cs new file mode 100644 index 000000000..9c77024b8 --- /dev/null +++ b/crypto/src/crypto/digests/AsconDigest.cs @@ -0,0 +1,228 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + public class AsconDigest : IDigest + { + public enum AsconParameters + { + AsconHash, + AsconHashA, + AsconXof, + AsconXofA, + } + + public AsconDigest(AsconParameters parameters) + { + switch (parameters) + { + case AsconParameters.AsconHash: + ASCON_PB_ROUNDS = 12; + ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) | + ((ulong)(ASCON_PA_ROUNDS) << 40) | + ((ulong)(ASCON_HASH_BYTES * 8))); + algorithmName = "Ascon-Hash"; + break; + case AsconParameters.AsconHashA: + ASCON_PB_ROUNDS = 8; + ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) | + ((ulong)(ASCON_PA_ROUNDS) << 40) | + ((ulong)(ASCON_PA_ROUNDS - ASCON_PB_ROUNDS) << 32) | + ((ulong)(ASCON_HASH_BYTES * 8))); + algorithmName = "Ascon-HashA"; + break; + case AsconParameters.AsconXof: + ASCON_PB_ROUNDS = 12; + ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) | + ((ulong)(ASCON_PA_ROUNDS) << 40)); + algorithmName = "Ascon-Xof"; + break; + case AsconParameters.AsconXofA: + ASCON_PB_ROUNDS = 8; + ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) | + ((ulong)(ASCON_PA_ROUNDS) << 40) | + ((ulong)(ASCON_PA_ROUNDS - ASCON_PB_ROUNDS) << 32)); + algorithmName = "Ascon-XofA"; + break; + default: + throw new ArgumentException("Invalid parameter settings for Ascon Hash"); + } + } + + private string algorithmName; + + private readonly MemoryStream buffer = new MemoryStream(); + private ulong x0; + private ulong x1; + private ulong x2; + private ulong x3; + private ulong x4; + private readonly int CRYPTO_BYTES = 32; + private readonly ulong ASCON_IV; + private readonly int ASCON_HASH_RATE = 8; + private readonly int ASCON_PA_ROUNDS = 12; + private int ASCON_PB_ROUNDS; + + + private uint ASCON_HASH_BYTES = 32; + + public string AlgorithmName => algorithmName; + + private ulong ROR(ulong x, int n) + { + return x >> n | x << (64 - n); + } + + private void ROUND(ulong C) + { + ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); + ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); + ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); + ulong t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); + ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28); + x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61); + x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6)); + x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17); + x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41); + } + + private void P(int nr) + { + if (nr == 12) + { + ROUND(0xf0UL); + ROUND(0xe1UL); + ROUND(0xd2UL); + ROUND(0xc3UL); + } + if (nr >= 8) + { + ROUND(0xb4UL); + ROUND(0xa5UL); + } + ROUND(0x96UL); + ROUND(0x87UL); + ROUND(0x78UL); + ROUND(0x69UL); + ROUND(0x5aUL); + ROUND(0x4bUL); + } + + private ulong PAD(int i) + { + return 0x80UL << (56 - (i << 3)); + } + + private ulong LOADBYTES(byte[] bytes, int inOff, int n) + { + ulong x = 0; + for (int i = 0; i < n; ++i) + { + x |= (bytes[i + inOff] & 0xFFUL) << ((7 - i) << 3); + } + return x; + } + + private void STOREBYTES(byte[] bytes, int inOff, ulong w, int n) + { + for (int i = 0; i < n; ++i) + { + bytes[i + inOff] = (byte)(w >> ((7 - i) << 3)); + } + } + + public int GetDigestSize() + { + return CRYPTO_BYTES; + } + + + public void Update(byte input) + { + buffer.Write(new byte[] { input }, 0, 1); + } + + + public void BlockUpdate(byte[] input, int inOff, int len) + { + if ((inOff + len) > input.Length) + { + throw new DataLengthException("input buffer too ushort"); + } + buffer.Write(input, inOff, len); + } + + + public int DoFinal(byte[] output, int outOff) + { + if (CRYPTO_BYTES + outOff > output.Length) + { + throw new OutputLengthException("output buffer is too ushort"); + } + byte[] input = buffer.GetBuffer(); + int len = (int)buffer.Length; + int inOff = 0; + /* initialize */ + x0 = ASCON_IV; + x1 = 0; + x2 = 0; + x3 = 0; + x4 = 0; + P(ASCON_PA_ROUNDS); + /* absorb full plaintext blocks */ + while (len >= ASCON_HASH_RATE) + { + x0 ^= LOADBYTES(input, inOff, 8); + P(ASCON_PB_ROUNDS); + inOff += ASCON_HASH_RATE; + len -= ASCON_HASH_RATE; + } + /* absorb readonly plaintext block */ + x0 ^= LOADBYTES(input, inOff, len); + x0 ^= PAD(len); + P(ASCON_PA_ROUNDS); + /* squeeze full output blocks */ + len = CRYPTO_BYTES; + while (len > ASCON_HASH_RATE) + { + STOREBYTES(output, outOff, x0, 8); + P(ASCON_PB_ROUNDS); + outOff += ASCON_HASH_RATE; + len -= ASCON_HASH_RATE; + } + /* squeeze readonly output block */ + STOREBYTES(output, outOff, x0, len); + return CRYPTO_BYTES; + } + + + public void Reset() + { + buffer.SetLength(0); + } + + public int GetByteLength() + { + throw new NotImplementedException(); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + byte[] rv = new byte[32]; + int rlt = DoFinal(rv, 0); + rv.AsSpan(0, 32).CopyTo(output); + return rlt; + } + + public void BlockUpdate(ReadOnlySpan<byte> input) + { + buffer.Write(input.ToArray(), 0, input.Length); + } +#endif + } +} + |