diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-02-08 19:33:39 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-02-08 19:33:39 +0700 |
commit | d5865255abae4b260a7e3191cc0d591c403eb6fa (patch) | |
tree | 59376e9407535bc46a7f0ae9cbee6ad4e9bfe008 | |
parent | Replace _Partial methods with _High/_Low variants (diff) | |
download | BouncyCastle.NET-ed25519-d5865255abae4b260a7e3191cc0d591c403eb6fa.tar.xz |
Refactor AsconEngine
-rw-r--r-- | crypto/src/crypto/engines/AsconEngine.cs | 976 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/AsconTest.cs | 337 |
2 files changed, 721 insertions, 592 deletions
diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs index 281c4b0df..6f70ee43b 100644 --- a/crypto/src/crypto/engines/AsconEngine.cs +++ b/crypto/src/crypto/engines/AsconEngine.cs @@ -1,8 +1,13 @@ using System; using System.IO; +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER +using System.Runtime.CompilerServices; +#endif using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines { @@ -13,8 +18,8 @@ namespace Org.BouncyCastle.Crypto.Engines * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c * </p> */ - public class AsconEngine - : IAeadBlockCipher + public sealed class AsconEngine + : IAeadCipher { public enum AsconParameters { @@ -46,9 +51,7 @@ namespace Org.BouncyCastle.Crypto.Engines private ulong x2; private ulong x3; private ulong x4; - private String algorithmName; - - public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); + private string algorithmName; public string AlgorithmName => algorithmName; @@ -57,136 +60,301 @@ namespace Org.BouncyCastle.Crypto.Engines this.asconParameters = asconParameters; switch (asconParameters) { - case AsconParameters.ascon80pq: - CRYPTO_KEYBYTES = 20; - CRYPTO_ABYTES = 16; - ASCON_AEAD_RATE = 8; - ASCON_IV = 0xa0400c0600000000UL; - algorithmName = "Ascon-80pq AEAD"; - break; - case AsconParameters.ascon128a: - CRYPTO_KEYBYTES = 16; - CRYPTO_ABYTES = 16; - ASCON_AEAD_RATE = 16; - ASCON_IV = 0x80800c0800000000UL; - algorithmName = "Ascon-128a AEAD"; - break; - case AsconParameters.ascon128: - CRYPTO_KEYBYTES = 16; - CRYPTO_ABYTES = 16; - ASCON_AEAD_RATE = 8; - ASCON_IV = 0x80400c0600000000UL; - algorithmName = "Ascon-128 AEAD"; - break; - default: - throw new ArgumentException("invalid parameter setting for ASCON AEAD"); + case AsconParameters.ascon80pq: + CRYPTO_KEYBYTES = 20; + CRYPTO_ABYTES = 16; + ASCON_AEAD_RATE = 8; + ASCON_IV = 0xa0400c0600000000UL; + algorithmName = "Ascon-80pq AEAD"; + break; + case AsconParameters.ascon128a: + CRYPTO_KEYBYTES = 16; + CRYPTO_ABYTES = 16; + ASCON_AEAD_RATE = 16; + ASCON_IV = 0x80800c0800000000UL; + algorithmName = "Ascon-128a AEAD"; + break; + case AsconParameters.ascon128: + CRYPTO_KEYBYTES = 16; + CRYPTO_ABYTES = 16; + ASCON_AEAD_RATE = 8; + ASCON_IV = 0x80400c0600000000UL; + algorithmName = "Ascon-128 AEAD"; + break; + default: + throw new ArgumentException("invalid parameter setting for ASCON AEAD"); } nr = (ASCON_AEAD_RATE == 8) ? 6 : 8; initialised = false; } - private ulong U64BIG(ulong x) + public int GetKeyBytesSize() { - return (((0x00000000000000FFUL & x) << 56) | - ((0x000000000000FF00UL & x) << 40) | - ((0x0000000000FF0000UL & x) << 24) | - ((0x00000000FF000000UL & x) << 8) | - ((0x000000FF00000000UL & x) >> 8) | - ((0x0000FF0000000000UL & x) >> 24) | - ((0x00FF000000000000UL & x) >> 40) | - ((0xFF00000000000000UL & x) >> 56)); + return CRYPTO_KEYBYTES; } - private ulong ROR(ulong x, int n) + public int GetIVBytesSize() { - return x >> n | x << (64 - n); + return CRYPTO_ABYTES; } - private ulong KEYROT(ulong lo2hi, ulong hi2lo) + public void Init(bool forEncryption, ICipherParameters parameters) { - return lo2hi << 32 | hi2lo >> 32; + this.forEncryption = forEncryption; + if (!(parameters is ParametersWithIV withIV)) + throw new ArgumentException("ASCON Init parameters must include an IV"); + + byte[] npub = withIV.GetIV(); + if (npub == null || npub.Length != CRYPTO_ABYTES) + throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV"); + + if (!(withIV.Parameters is KeyParameter key)) + throw new ArgumentException("ASCON Init parameters must include a key"); + + byte[] k = key.GetKey(); + if (k.Length != CRYPTO_KEYBYTES) + throw new ArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long"); + + N0 = Pack.BE_To_UInt64(npub, 0); + N1 = Pack.BE_To_UInt64(npub, 8); + if (CRYPTO_KEYBYTES == 16) + { + K1 = Pack.BE_To_UInt64(k, 0); + K2 = Pack.BE_To_UInt64(k, 8); + } + else if (CRYPTO_KEYBYTES == 20) + { + K0 = Pack.BE_To_UInt32(k, 0); + K1 = Pack.BE_To_UInt64(k, 4); + K2 = Pack.BE_To_UInt64(k, 12); + } + initialised = true; + /*Mask-Gen*/ + Reset(false); } - private ulong PAD(int i) + public void ProcessAadByte(byte input) { - return 0x80UL << (56 - (i << 3)); + if (aadFinished) + { + throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); + } + + aadData.WriteByte(input); } - private ulong MASK(int n) + public void ProcessAadBytes(byte[] inBytes, int inOff, int len) { - /* undefined for n == 0 */ - return ~0UL >> (64 - (n << 3)); + if (aadFinished) + { + throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); + } + + Check.DataLength(inBytes, inOff, len, "input buffer too short"); + + aadData.Write(inBytes, inOff, len); } - private ulong LOAD(byte[] bytes, int inOff, int n) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void ProcessAadBytes(ReadOnlySpan<byte> input) { - ulong x = 0; - int len = System.Math.Min(8, bytes.Length - inOff); - for (int i = 0; i < len; ++i) + if (aadFinished) { - x |= (bytes[i + inOff] & 0xFFUL) << (i << 3); + throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); } - return U64BIG(x & MASK(n)); + + aadData.Write(input); + } +#endif + + public int ProcessByte(byte input, byte[] outBytes, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessByte(input, Spans.FromNullable(outBytes, outOff)); +#else + return ProcessBytes(new byte[]{ input }, 0, 1, outBytes, outOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessByte(byte input, Span<byte> output) + { + Span<byte> singleByte = stackalloc byte[1]{ input }; + + return ProcessBytes(singleByte, output); } +#endif - private void STORE(byte[] bytes, int inOff, ulong w, int n) + public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { - ulong x = 0; - for (int i = 0; i < n; ++i) + Check.DataLength(inBytes, inOff, len, "input buffer too short"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); +#else + if (!initialised) + throw new ArgumentException("Need to call Init function before encryption/decryption"); + + message.Write(inBytes, inOff, len); + int rv = ProcessBytes(outBytes, outOff); + encrypted = true; + return rv; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!initialised) + throw new ArgumentException("Need to call Init function before encryption/decryption"); + + message.Write(input); + int rv = ProcessBytes(output); + encrypted = true; + return rv; + } +#endif + + public int DoFinal(byte[] outBytes, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(outBytes.AsSpan(outOff)); +#else + if (!initialised) + throw new ArgumentException("Need call init function before encryption/decryption"); + + if (!aadFinished) + { + ProcessAad(); + } + if (!encrypted) + { + ProcessBytes(Array.Empty<byte>(), 0, 0, Array.Empty<byte>(), 0); + } + byte[] input = message.GetBuffer(); + int len = Convert.ToInt32(message.Length); + if (forEncryption) { - x |= (bytes[i + inOff] & 0xFFUL) << (i << 3); + Check.OutputLength(outBytes, outOff, len + CRYPTO_ABYTES, "output buffer too short"); } - x &= ~MASK(n); - x |= U64BIG(w); - for (int i = 0; i < n; ++i) + else { - bytes[i + inOff] = (byte)(x >> (i << 3)); + Check.OutputLength(outBytes, outOff, len - CRYPTO_ABYTES, "output buffer too short"); } + if (forEncryption) + { + ascon_final(outBytes, outOff, input, 0, len); + /* set tag */ + mac = new byte[16]; + Pack.UInt64_To_BE(x3, mac, 0); + Pack.UInt64_To_BE(x4, mac, 8); + Array.Copy(mac, 0, outBytes, len + outOff, 16); + Reset(false); + return len + CRYPTO_ABYTES; + } + else + { + len -= CRYPTO_ABYTES; + ascon_final(outBytes, outOff, input, 0, len); + x3 ^= Pack.BE_To_UInt64(input, len); + x4 ^= Pack.BE_To_UInt64(input, len + 8); + ulong result = x3 | x4; + Reset(true); + + if (result != 0UL) + throw new ArgumentException("Mac does not match"); + + return len; + } +#endif } - private ulong LOADBYTES(byte[] bytes, int inOff, int n) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) { - ulong x = 0; - for (int i = 0; i < n; ++i) + if (!initialised) + throw new ArgumentException("Need call init function before encryption/decryption"); + + if (!aadFinished) + { + ProcessAad(); + } + if (!encrypted) + { + ProcessBytes(Array.Empty<byte>(), 0, 0, Array.Empty<byte>(), 0); + } + byte[] input = message.GetBuffer(); + int len = Convert.ToInt32(message.Length); + if (forEncryption) + { + Check.OutputLength(output, len + CRYPTO_ABYTES, "output buffer too short"); + } + else + { + Check.OutputLength(output, len - CRYPTO_ABYTES, "output buffer too short"); + } + if (forEncryption) + { + ascon_final(output, input.AsSpan(0, len)); + /* set tag */ + mac = new byte[CRYPTO_ABYTES]; + Pack.UInt64_To_BE(x3, mac, 0); + Pack.UInt64_To_BE(x4, mac, 8); + mac.AsSpan(0, CRYPTO_ABYTES).CopyTo(output[len..]); + Reset(false); + return len + CRYPTO_ABYTES; + } + else { - x |= (bytes[i + inOff] & 0xFFUL) << ((7 - i) << 3); + len -= CRYPTO_ABYTES; + ascon_final(output, input.AsSpan(0, len)); + x3 ^= Pack.BE_To_UInt64(input, len); + x4 ^= Pack.BE_To_UInt64(input, len + 8); + ulong result = x3 | x4; + Reset(true); + + if (result != 0UL) + throw new ArgumentException("Mac does not match"); + + return len; } - return x; } +#endif - private void STOREBYTES(byte[] bytes, int inOff, ulong w, int n) + public byte[] GetMac() { - for (int i = 0; i < n; ++i) - { - bytes[i + inOff] = (byte)(w >> ((7 - i) << 3)); - } + return mac; + } + + public int GetUpdateOutputSize(int len) + { + return len; } - private void ROUND(ulong C) + public int GetOutputSize(int len) { - 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); + return len + CRYPTO_ABYTES; + } + + public void Reset() + { + Reset(true); } private void P(int nr) { - if (nr == 12) - { - ROUND(0xf0UL); - ROUND(0xe1UL); - ROUND(0xd2UL); - ROUND(0xc3UL); - } if (nr >= 8) { + if (nr == 12) + { + ROUND(0xf0UL); + ROUND(0xe1UL); + ROUND(0xd2UL); + ROUND(0xc3UL); + } ROUND(0xb4UL); ROUND(0xa5UL); } @@ -198,6 +366,39 @@ namespace Org.BouncyCastle.Crypto.Engines ROUND(0x4bUL); } +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + 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 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28); + x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61); + x2 = ~(t2 ^ Longs.RotateRight(t2, 1) ^ Longs.RotateRight(t2, 6)); + x3 = t3 ^ Longs.RotateRight(t3, 10) ^ Longs.RotateRight(t3, 17); + x4 = t4 ^ Longs.RotateRight(t4, 7) ^ Longs.RotateRight(t4, 41); + } + + private void ProcessAad() + { + if (!aadFinished) + { + byte[] ad = aadData.GetBuffer(); + int adlen = Convert.ToInt32(aadData.Length); + /* perform ascon computation */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ascon_adata(ad.AsSpan(0, adlen)); +#else + ascon_adata(ad, 0, adlen); +#endif + aadFinished = true; + } + } + private void ascon_aeadinit() { /* initialize */ @@ -219,40 +420,72 @@ namespace Org.BouncyCastle.Crypto.Engines x4 ^= K2; } - private void ascon_adata(byte[] ad, int adOff, int adlen) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int ProcessBytes(Span<byte> output) { - if (adlen != 0) + int len_orig = Convert.ToInt32(message.Length); + int len = 0; + if (forEncryption) + { + if (len_orig >= ASCON_AEAD_RATE) + { + ProcessAad(); + byte[] input = message.GetBuffer(); + len = (len_orig / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; + Check.OutputLength(output, len, "output buffer is too short"); + ascon_encrypt(output, input.AsSpan(0, len)); + message.SetLength(0); + message.Write(input, len, len_orig - len); + } + } + else + { + if (len_orig - CRYPTO_ABYTES >= ASCON_AEAD_RATE) + { + ProcessAad(); + byte[] input = message.GetBuffer(); + len = ((len_orig - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; + Check.OutputLength(output, len, "output buffer is too short"); + ascon_decrypt(output, input.AsSpan(0, len)); + message.SetLength(0); + message.Write(input, len, len_orig - len); + } + } + return len; + } + + private void ascon_adata(ReadOnlySpan<byte> aad) + { + if (!aad.IsEmpty) { /* full associated data blocks */ - while (adlen >= ASCON_AEAD_RATE) + while (aad.Length >= ASCON_AEAD_RATE) { - x0 ^= LOAD(ad, adOff, 8); + x0 ^= Pack.BE_To_UInt64(aad); if (ASCON_AEAD_RATE == 16) { - x1 ^= LOAD(ad, adOff + 8, 8); + x1 ^= Pack.BE_To_UInt64(aad[8..]); } P(nr); - adOff += ASCON_AEAD_RATE; - adlen -= ASCON_AEAD_RATE; + aad = aad[ASCON_AEAD_RATE..]; } - /* readonly associated data block */ - if (ASCON_AEAD_RATE == 16 && adlen >= 8) + /* final associated data block */ + if (ASCON_AEAD_RATE == 16 && aad.Length >= 8) { - x0 ^= LOAD(ad, adOff, 8); - adOff += 8; - adlen -= 8; - x1 ^= PAD(adlen); - if (adlen != 0) + x0 ^= Pack.BE_To_UInt64(aad); + aad = aad[8..]; + x1 ^= PAD(aad.Length); + if (!aad.IsEmpty) { - x1 ^= LOAD(ad, adOff, adlen); + x1 ^= Pack.BE_To_UInt64_High(aad); } } else { - x0 ^= PAD(adlen); - if (adlen != 0) + x0 ^= PAD(aad.Length); + if (!aad.IsEmpty) { - x0 ^= LOAD(ad, adOff, adlen); + x0 ^= Pack.BE_To_UInt64_High(aad); } } P(nr); @@ -261,115 +494,104 @@ namespace Org.BouncyCastle.Crypto.Engines x4 ^= 1UL; } - private void ascon_encrypt(byte[] c, int cOff, byte[] m, int mOff, int mlen) + private void ascon_encrypt(Span<byte> c, ReadOnlySpan<byte> m) { /* full plaintext blocks */ - while (mlen >= ASCON_AEAD_RATE) + while (m.Length >= ASCON_AEAD_RATE) { - x0 ^= LOAD(m, mOff, 8); - STORE(c, cOff, x0, 8); + x0 ^= Pack.BE_To_UInt64(m); + Pack.UInt64_To_BE(x0, c); if (ASCON_AEAD_RATE == 16) { - x1 ^= LOAD(m, mOff + 8, 8); - STORE(c, cOff + 8, x1, 8); + x1 ^= Pack.BE_To_UInt64(m[8..]); + Pack.UInt64_To_BE(x1, c[8..]); } P(nr); - mOff += ASCON_AEAD_RATE; - cOff += ASCON_AEAD_RATE; - mlen -= ASCON_AEAD_RATE; + m = m[ASCON_AEAD_RATE..]; + c = c[ASCON_AEAD_RATE..]; } } - private void ascon_decrypt(byte[] m, int mOff, byte[] c, int cOff, int clen) + private void ascon_decrypt(Span<byte> m, ReadOnlySpan<byte> c) { /* full ciphertext blocks */ - while (clen >= ASCON_AEAD_RATE) + while (c.Length >= ASCON_AEAD_RATE) { - ulong cx = LOAD(c, cOff, 8); + ulong cx = Pack.BE_To_UInt64(c); x0 ^= cx; - STORE(m, mOff, x0, 8); + Pack.UInt64_To_BE(x0, m); x0 = cx; if (ASCON_AEAD_RATE == 16) { - cx = LOAD(c, cOff + 8, 8); + cx = Pack.BE_To_UInt64(c[8..]); x1 ^= cx; - STORE(m, mOff + 8, x1, 8); + Pack.UInt64_To_BE(x1, m[8..]); x1 = cx; } P(nr); - mOff += ASCON_AEAD_RATE; - cOff += ASCON_AEAD_RATE; - clen -= ASCON_AEAD_RATE; + c = c[ASCON_AEAD_RATE..]; + m = m[ASCON_AEAD_RATE..]; } } - private ulong CLEAR(ulong w, int n) - { - /* undefined for n == 0 */ - ulong mask = 0x00ffffffffffffffUL >> (n * 8 - 8); - return w & mask; - } - - private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen) + private void ascon_final(Span<byte> output, ReadOnlySpan<byte> input) { if (forEncryption) { /* final plaintext block */ - if (ASCON_AEAD_RATE == 16 && mlen >= 8) + if (ASCON_AEAD_RATE == 16 && input.Length >= 8) { - x0 ^= LOAD(m, mOff, 8); - STORE(c, cOff, x0, 8); - mOff += 8; - cOff += 8; - mlen -= 8; - x1 ^= PAD(mlen); - if (mlen != 0) + x0 ^= Pack.BE_To_UInt64(input); + Pack.UInt64_To_BE(x0, output); + input = input[8..]; + output = output[8..]; + x1 ^= PAD(input.Length); + if (!input.IsEmpty) { - x1 ^= LOAD(m, mOff, mlen); - STORE(c, cOff, x1, mlen); + x1 ^= Pack.BE_To_UInt64_High(input); + Pack.UInt64_To_BE_High(x1, output[..input.Length]); } } else { - x0 ^= PAD(mlen); - if (mlen != 0) + x0 ^= PAD(input.Length); + if (!input.IsEmpty) { - x0 ^= LOAD(m, mOff, mlen); - STORE(c, cOff, x0, mlen); + x0 ^= Pack.BE_To_UInt64_High(input); + Pack.UInt64_To_BE_High(x0, output[..input.Length]); } } } else { /* final ciphertext block */ - if (ASCON_AEAD_RATE == 16 && mlen >= 8) + if (ASCON_AEAD_RATE == 16 && input.Length >= 8) { - ulong cx = LOAD(m, mOff, 8); + ulong cx = Pack.BE_To_UInt64(input); x0 ^= cx; - STORE(c, cOff, x0, 8); + Pack.UInt64_To_BE(x0, output); x0 = cx; - mOff += 8; - cOff += 8; - mlen -= 8; - x1 ^= PAD(mlen); - if (mlen != 0) + input = input[8..]; + output = output[8..]; + x1 ^= PAD(input.Length); + if (!input.IsEmpty) { - cx = LOAD(m, mOff, mlen); + cx = Pack.BE_To_UInt64_High(input); x1 ^= cx; - STORE(c, cOff, x1, mlen); - x1 = CLEAR(x1, mlen); + Pack.UInt64_To_BE_High(x1, output[..input.Length]); + x1 &= ulong.MaxValue >> (input.Length << 3); x1 ^= cx; } } else { - x0 ^= PAD(mlen); - if (mlen != 0) + x0 ^= PAD(input.Length); + if (!input.IsEmpty) { - ulong cx = LOAD(m, mOff, mlen); + ulong cx = Pack.BE_To_UInt64_High(input); x0 ^= cx; - STORE(c, cOff, x0, mlen); - x0 = CLEAR(x0, mlen); + Pack.UInt64_To_BE_High(x0, output[..input.Length]); + x0 &= ulong.MaxValue >> (input.Length << 3); x0 ^= cx; } } @@ -377,247 +599,234 @@ namespace Org.BouncyCastle.Crypto.Engines /* finalize */ switch (asconParameters) { - case AsconParameters.ascon128: - x1 ^= K1; - x2 ^= K2; - break; - case AsconParameters.ascon128a: - x2 ^= K1; - x3 ^= K2; - break; - case AsconParameters.ascon80pq: - x1 ^= KEYROT(K0, K1); - x2 ^= KEYROT(K1, K2); - x3 ^= KEYROT(K2, 0UL); - break; + case AsconParameters.ascon128: + x1 ^= K1; + x2 ^= K2; + break; + case AsconParameters.ascon128a: + x2 ^= K1; + x3 ^= K2; + break; + case AsconParameters.ascon80pq: + x1 ^= (K0 << 32 | K1 >> 32); + x2 ^= (K1 << 32 | K2 >> 32); + x3 ^= K2 << 32; + break; } P(12); x3 ^= K1; x4 ^= K2; } - - public void Init(bool forEncryption, ICipherParameters param) +#else + private int ProcessBytes(byte[] output, int outOff) { - this.forEncryption = forEncryption; - if (!(param is ParametersWithIV)) - { - throw new ArgumentException( - "ASCON init parameters must include an IV"); - } - ParametersWithIV ivParams = (ParametersWithIV)param; - byte[] npub = ivParams.GetIV(); - if (npub == null || npub.Length != CRYPTO_ABYTES) - { - throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV"); - } - if (!(ivParams.Parameters is KeyParameter)) - { - throw new ArgumentException( - "ASCON init parameters must include a key"); - } - KeyParameter key = (KeyParameter)ivParams.Parameters; - byte[] k = key.GetKey(); - if (k.Length != CRYPTO_KEYBYTES) - { - throw new ArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long"); - } - N0 = LOAD(npub, 0, 8); - N1 = LOAD(npub, 8, 8); - if (CRYPTO_KEYBYTES == 16) - { - K1 = LOAD(k, 0, 8); - K2 = LOAD(k, 8, 8); - } - else if (CRYPTO_KEYBYTES == 20) + int len_orig = Convert.ToInt32(message.Length); + int len = 0; + if (forEncryption) { - K0 = KEYROT(0, LOADBYTES(k, 0, 4)); - K1 = LOADBYTES(k, 4, 8); - K2 = LOADBYTES(k, 12, 8); + if (len_orig >= ASCON_AEAD_RATE) + { + ProcessAad(); + byte[] input = message.GetBuffer(); + len = (len_orig / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; + Check.OutputLength(output, outOff, len, "output buffer is too short"); + ascon_encrypt(output, outOff, input, 0, len); + message.SetLength(0); + message.Write(input, len, len_orig - len); + } } - initialised = true; - /*Mask-Gen*/ - reset(false); - } - - public void ProcessAadByte(byte input) - { - if (aadFinished) + else { - throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + - " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); + if (len_orig - CRYPTO_ABYTES >= ASCON_AEAD_RATE) + { + ProcessAad(); + byte[] input = message.GetBuffer(); + len = ((len_orig - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; + Check.OutputLength(output, outOff, len, "output buffer is too short"); + ascon_decrypt(output, outOff, input, 0, len); + message.SetLength(0); + message.Write(input, len, len_orig - len); + } } - aadData.Write(new byte[] { input }, 0, 1); + return len; } - - public void ProcessAadBytes(byte[] input, int inOff, int len) + private void ascon_adata(byte[] ad, int adOff, int adlen) { - if (aadFinished) - { - throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + - " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); - } - if ((inOff + len) > input.Length) + if (adlen != 0) { - throw new DataLengthException("input buffer too short"); + /* full associated data blocks */ + while (adlen >= ASCON_AEAD_RATE) + { + x0 ^= Pack.BE_To_UInt64(ad, adOff); + if (ASCON_AEAD_RATE == 16) + { + x1 ^= Pack.BE_To_UInt64(ad, adOff + 8); + } + P(nr); + adOff += ASCON_AEAD_RATE; + adlen -= ASCON_AEAD_RATE; + } + /* final associated data block */ + if (ASCON_AEAD_RATE == 16 && adlen >= 8) + { + x0 ^= Pack.BE_To_UInt64(ad, adOff); + adOff += 8; + adlen -= 8; + x1 ^= PAD(adlen); + if (adlen != 0) + { + x1 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen); + } + } + else + { + x0 ^= PAD(adlen); + if (adlen != 0) + { + x0 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen); + } + } + P(nr); } - aadData.Write(input, inOff, len); - } - - - public int ProcessByte(byte input, byte[] output, int outOff) - { - return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + /* domain separation */ + x4 ^= 1UL; } - - public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + private void ascon_encrypt(byte[] c, int cOff, byte[] m, int mOff, int mlen) { - if (!initialised) - { - throw new ArgumentException("Need call init function before encryption/decryption"); - } - if ((inOff + len) > input.Length) + /* full plaintext blocks */ + while (mlen >= ASCON_AEAD_RATE) { - throw new DataLengthException("input buffer too short"); + x0 ^= Pack.BE_To_UInt64(m, mOff); + Pack.UInt64_To_BE(x0, c, cOff); + if (ASCON_AEAD_RATE == 16) + { + x1 ^= Pack.BE_To_UInt64(m, mOff + 8); + Pack.UInt64_To_BE(x1, c, cOff + 8); + } + P(nr); + mOff += ASCON_AEAD_RATE; + cOff += ASCON_AEAD_RATE; + mlen -= ASCON_AEAD_RATE; } - message.Write(input, inOff, len); - int rv = processBytes(output, outOff); - encrypted = true; - return rv; } - private void processAAD() + private void ascon_decrypt(byte[] m, int mOff, byte[] c, int cOff, int clen) { - if (!aadFinished) + /* full ciphertext blocks */ + while (clen >= ASCON_AEAD_RATE) { - byte[] ad = aadData.GetBuffer(); - int adlen = (int)aadData.Length; - /* perform ascon computation */ - ascon_adata(ad, 0, adlen); - aadFinished = true; + ulong cx = Pack.BE_To_UInt64(c, cOff); + x0 ^= cx; + Pack.UInt64_To_BE(x0, m, mOff); + x0 = cx; + if (ASCON_AEAD_RATE == 16) + { + cx = Pack.BE_To_UInt64(c, cOff + 8); + x1 ^= cx; + Pack.UInt64_To_BE(x1, m, mOff + 8); + x1 = cx; + } + P(nr); + mOff += ASCON_AEAD_RATE; + cOff += ASCON_AEAD_RATE; + clen -= ASCON_AEAD_RATE; } } - private int processBytes(byte[] output, int outOff) + private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen) { - int len = 0; if (forEncryption) { - if ((int)message.Length >= ASCON_AEAD_RATE) + /* final plaintext block */ + if (ASCON_AEAD_RATE == 16 && mlen >= 8) { - processAAD(); - byte[] input = message.GetBuffer(); - len = ((int)message.Length / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; - if (len + outOff > output.Length) + x0 ^= Pack.BE_To_UInt64(m, mOff); + Pack.UInt64_To_BE(x0, c, cOff); + mOff += 8; + cOff += 8; + mlen -= 8; + x1 ^= PAD(mlen); + if (mlen != 0) { - throw new OutputLengthException("output buffer is too short"); + x1 ^= Pack.BE_To_UInt64_High(m, mOff, mlen); + Pack.UInt64_To_BE_High(x1, c, cOff, mlen); } - ascon_encrypt(output, outOff, input, 0, len); - int len_orig = (int)message.Length; - message.SetLength(0); - message.Write(input, len, len_orig - len); } - } - else - { - if ((int)message.Length - CRYPTO_ABYTES >= ASCON_AEAD_RATE) + else { - processAAD(); - byte[] input = message.GetBuffer(); - len = (((int)message.Length - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; - if (len + outOff > output.Length) + x0 ^= PAD(mlen); + if (mlen != 0) { - throw new OutputLengthException("output buffer is too short"); + x0 ^= Pack.BE_To_UInt64_High(m, mOff, mlen); + Pack.UInt64_To_BE_High(x0, c, cOff, mlen); } - ascon_decrypt(output, outOff, input, 0, len); - int len_orig = (int)message.Length; - message.SetLength(0); - message.Write(input, len, len_orig - len); } } - return len; - } - - public int DoFinal(byte[] output, int outOff) - { - if (!initialised) - { - throw new ArgumentException("Need call init function before encryption/decryption"); - } - if (!aadFinished) - { - processAAD(); - } - if (!encrypted) - { - ProcessBytes(new byte[] { }, 0, 0, new byte[] { }, 0); - } - byte[] input = message.GetBuffer(); - int len = (int)message.Length; - if ((forEncryption && outOff + len + CRYPTO_ABYTES > output.Length) || - (!forEncryption && outOff + len - CRYPTO_ABYTES > output.Length)) - { - throw new OutputLengthException("output buffer too short"); - } - if (forEncryption) - { - ascon_final(output, outOff, input, 0, len); - /* set tag */ - mac = new byte[16]; - STOREBYTES(mac, 0, x3, 8); - STOREBYTES(mac, 8, x4, 8); - Array.Copy(mac, 0, output, len + outOff, 16); - reset(false); - return len + CRYPTO_ABYTES; - } else { - len -= CRYPTO_ABYTES; - ascon_final(output, outOff, input, 0, len); - x3 ^= LOADBYTES(input, len, 8); - x4 ^= LOADBYTES(input, len + 8, 8); - ulong result = x3 | x4; - result |= result >> 32; - result |= result >> 16; - result |= result >> 8; - reset(true); - if ((((((int)(result & 0xffUL) - 1) >> 8) & 1) - 1) != 0) + /* final ciphertext block */ + if (ASCON_AEAD_RATE == 16 && mlen >= 8) { - throw new ArgumentException("Mac does not match"); + ulong cx = Pack.BE_To_UInt64(m, mOff); + x0 ^= cx; + Pack.UInt64_To_BE(x0, c, cOff); + x0 = cx; + mOff += 8; + cOff += 8; + mlen -= 8; + x1 ^= PAD(mlen); + if (mlen != 0) + { + cx = Pack.BE_To_UInt64_High(m, mOff, mlen); + x1 ^= cx; + Pack.UInt64_To_BE_High(x1, c, cOff, mlen); + x1 &= ulong.MaxValue >> (mlen << 3); + x1 ^= cx; + } + } + else + { + x0 ^= PAD(mlen); + if (mlen != 0) + { + ulong cx = Pack.BE_To_UInt64_High(m, mOff, mlen); + x0 ^= cx; + Pack.UInt64_To_BE_High(x0, c, cOff, mlen); + x0 &= ulong.MaxValue >> (mlen << 3); + x0 ^= cx; + } } - return len; } + /* finalize */ + switch (asconParameters) + { + case AsconParameters.ascon128: + x1 ^= K1; + x2 ^= K2; + break; + case AsconParameters.ascon128a: + x2 ^= K1; + x3 ^= K2; + break; + case AsconParameters.ascon80pq: + x1 ^= (K0 << 32 | K1 >> 32); + x2 ^= (K1 << 32 | K2 >> 32); + x3 ^= K2 << 32; + break; + } + P(12); + x3 ^= K1; + x4 ^= K2; } +#endif - - public byte[] GetMac() - { - return mac; - } - - - public int GetUpdateOutputSize(int len) - { - return len; - } - - public int GetOutputSize(int len) - { - return len + CRYPTO_ABYTES; - } - - public void Reset() - { - reset(true); - } - - private void reset(bool clearMac) + private void Reset(bool clearMac) { if (!initialised) - { throw new ArgumentException("Need call init function before encryption/decryption"); - } + x0 = x1 = x2 = x3 = x4 = 0; ascon_aeadinit(); aadData.SetLength(0); @@ -630,64 +839,9 @@ namespace Org.BouncyCastle.Crypto.Engines } } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public void ProcessAadBytes(ReadOnlySpan<byte> input) + private static ulong PAD(int i) { - if (aadFinished) - { - throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE + - " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); - } - aadData.Write(input); - } - - public int ProcessByte(byte input, Span<byte> output) - { - byte[] rv = new byte[1]; - int len = ProcessBytes(new byte[] { input }, 0, 1, rv, 0); - rv.AsSpan(0, len).CopyTo(output); - return len; - } - - public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) - { - byte[] rv = new byte[input.Length]; - int len = ProcessBytes(input.ToArray(), 0, rv.Length, rv, 0); - rv.AsSpan(0, len).CopyTo(output); - return len; - } - - public int DoFinal(Span<byte> output) - { - byte[] rv; - if (forEncryption) - { - rv = new byte[message.Length + 16]; - } - else - { - rv = new byte[message.Length]; - } - int len = DoFinal(rv, 0); - rv.AsSpan(0, len).CopyTo(output); - return rv.Length; - } -#endif - public int GetBlockSize() - { - return ASCON_AEAD_RATE; - } - - public int GetKeyBytesSize() - { - return CRYPTO_KEYBYTES; - } - - public int GetIVBytesSize() - { - return CRYPTO_ABYTES; + return 0x8000000000000000UL >> (i << 3); } } } - - diff --git a/crypto/test/src/crypto/test/AsconTest.cs b/crypto/test/src/crypto/test/AsconTest.cs index 837dc413b..eca949471 100644 --- a/crypto/test/src/crypto/test/AsconTest.cs +++ b/crypto/test/src/crypto/test/AsconTest.cs @@ -5,7 +5,6 @@ using System.IO; using NUnit.Framework; using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; @@ -14,87 +13,82 @@ using Org.BouncyCastle.Utilities.Test; namespace Org.BouncyCastle.Crypto.Tests { [TestFixture] - public class AsconTest : SimpleTest + public class AsconTest + : SimpleTest { - public override string Name - { - get { return "ASCON AEAD"; } - } + public override string Name => "ASCON AEAD"; [Test] public override void PerformTest() { - AsconEngine Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon80pq); - testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize()); - testParameters(Ascon, 20, 16, 16, 8); - Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128a); - testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize()); - testParameters(Ascon, 16, 16, 16, 16); - Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128); - testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize()); - testParameters(Ascon, 16, 16, 16, 8); - testVectors(AsconEngine.AsconParameters.ascon80pq, "160_128"); - testVectors(AsconEngine.AsconParameters.ascon128a, "128_128_a"); - testVectors(AsconEngine.AsconParameters.ascon128, "128_128"); + AsconEngine asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon80pq); + ImplTestExceptions(asconEngine); + ImplTestParameters(asconEngine, 20, 16, 16); + + asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon128a); + ImplTestExceptions(asconEngine); + ImplTestParameters(asconEngine, 16, 16, 16); + + asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon128); + ImplTestExceptions(asconEngine); + ImplTestParameters(asconEngine, 16, 16, 16); + + ImplTestVectors(AsconEngine.AsconParameters.ascon80pq, "160_128"); + ImplTestVectors(AsconEngine.AsconParameters.ascon128a, "128_128_a"); + ImplTestVectors(AsconEngine.AsconParameters.ascon128, "128_128"); } - private void testVectors(AsconEngine.AsconParameters asconParameters, string filename) + private void ImplTestVectors(AsconEngine.AsconParameters asconParameters, string filename) { + Random random = new Random(); AsconEngine Ascon = new AsconEngine(asconParameters); - ICipherParameters param; var buf = new Dictionary<string, string>(); //TestSampler sampler = new TestSampler(); using (var src = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.ascon.LWC_AEAD_KAT_" + filename + ".txt"))) { - string line; - string[] data; - byte[] rv; Dictionary<string, string> map = new Dictionary<string, string>(); + string line; while ((line = src.ReadLine()) != null) { - data = line.Split(' '); + var data = line.Split(' '); if (data.Length == 1) { - //if (!map["Count"].Equals("265")) - //{ - // continue; - //} byte[] key = Hex.Decode(map["Key"]); byte[] nonce = Hex.Decode(map["Nonce"]); byte[] ad = Hex.Decode(map["AD"]); byte[] pt = Hex.Decode(map["PT"]); byte[] ct = Hex.Decode(map["CT"]); - param = new ParametersWithIV(new KeyParameter(key), nonce); - Ascon.Init(true, param); - Ascon.ProcessAadBytes(ad, 0, ad.Length); - rv = new byte[Ascon.GetOutputSize(pt.Length)]; - int len = Ascon.ProcessBytes(pt, 0, pt.Length, rv, 0); - //byte[] mac = new byte[16]; - Ascon.DoFinal(rv, len); - //foreach(byte b in Hex.Decode(map["CT"])) - //{ - // Console.Write(b.ToString("X2")); - //} - //Console.WriteLine(); - //foreach (byte b in Arrays.Concatenate(rv, mac)) - //{ - // Console.Write(b.ToString("X2")); - //} - //Console.WriteLine(); - Assert.True(Arrays.AreEqual(rv, ct)); - Ascon.Reset(); - Ascon.Init(false, param); - //Decrypt - Ascon.ProcessAadBytes(ad, 0, ad.Length); - rv = new byte[pt.Length + 16]; - len = Ascon.ProcessBytes(ct, 0, ct.Length, rv, 0); - Ascon.DoFinal(rv, len); - byte[] pt_recovered = new byte[pt.Length]; - Array.Copy(rv, 0, pt_recovered, 0, pt.Length); - Assert.True(Arrays.AreEqual(pt, pt_recovered)); - //Console.WriteLine(map["Count"] + " pass"); map.Clear(); + var param = new ParametersWithIV(new KeyParameter(key), nonce); + + // Encrypt + { + Ascon.Init(true, param); + + var rv = new byte[Ascon.GetOutputSize(pt.Length)]; + random.NextBytes(rv); // should overwrite any existing data + + Ascon.ProcessAadBytes(ad, 0, ad.Length); + int len = Ascon.ProcessBytes(pt, 0, pt.Length, rv, 0); + len += Ascon.DoFinal(rv, len); + + Assert.True(Arrays.AreEqual(rv, 0, len, ct, 0, ct.Length)); + } + + // Decrypt + { + Ascon.Init(false, param); + + var rv = new byte[Ascon.GetOutputSize(ct.Length)]; + random.NextBytes(rv); // should overwrite any existing data + + Ascon.ProcessAadBytes(ad, 0, ad.Length); + int len = Ascon.ProcessBytes(ct, 0, ct.Length, rv, 0); + len += Ascon.DoFinal(rv, len); + + Assert.True(Arrays.AreEqual(rv, 0, len, pt, 0, pt.Length)); + } } else { @@ -106,27 +100,23 @@ namespace Org.BouncyCastle.Crypto.Tests { map[data[0].Trim()] = ""; } - } } } - Console.WriteLine("Ascon AEAD pass"); } - - private void testExceptions(IAeadBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize) - + private void ImplTestExceptions(AsconEngine asconEngine) { - ICipherParameters param; - byte[] k = new byte[keysize]; - byte[] iv = new byte[ivsize]; + int keySize = asconEngine.GetKeyBytesSize(), ivSize = asconEngine.GetIVBytesSize(); + byte[] k = new byte[keySize]; + byte[] iv = new byte[ivSize]; byte[] m = new byte[0]; - byte[] c1 = new byte[aeadBlockCipher.GetOutputSize(m.Length)]; - param = new ParametersWithIV(new KeyParameter(k), iv); + byte[] c1 = new byte[asconEngine.GetOutputSize(m.Length)]; + var param = new ParametersWithIV(new KeyParameter(k), iv); try { - aeadBlockCipher.ProcessBytes(m, 0, m.Length, c1, 0); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessBytes"); + asconEngine.ProcessBytes(m, 0, m.Length, c1, 0); + Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before ProcessBytes"); } catch (ArgumentException) { @@ -135,8 +125,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.ProcessByte((byte)0, c1, 0); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessByte"); + asconEngine.ProcessByte((byte)0, c1, 0); + Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before ProcessByte"); } catch (ArgumentException) { @@ -145,8 +135,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.Reset(); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before reset"); + asconEngine.Reset(); + Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before reset"); } catch (ArgumentException) { @@ -155,8 +145,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.DoFinal(c1, m.Length); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before dofinal"); + asconEngine.DoFinal(c1, m.Length); + Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before dofinal"); } catch (ArgumentException) { @@ -165,25 +155,26 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.GetMac(); - aeadBlockCipher.GetOutputSize(0); - aeadBlockCipher.GetUpdateOutputSize(0); + asconEngine.GetMac(); + asconEngine.GetOutputSize(0); + asconEngine.GetUpdateOutputSize(0); } catch (ArgumentException) { //expected - Assert.Fail(aeadBlockCipher.AlgorithmName + " functions can be called before initialisation"); + Assert.Fail(asconEngine.AlgorithmName + " functions can be called before initialisation"); } + Random rand = new Random(); int randomNum; - while ((randomNum = rand.Next(100)) == keysize) ; + while ((randomNum = rand.Next(100)) == keySize) ; byte[] k1 = new byte[randomNum]; - while ((randomNum = rand.Next(100)) == ivsize) ; + while ((randomNum = rand.Next(100)) == ivSize) ; byte[] iv1 = new byte[randomNum]; try { - aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k1), iv)); - Assert.Fail(aeadBlockCipher.AlgorithmName + " k size does not match"); + asconEngine.Init(true, new ParametersWithIV(new KeyParameter(k1), iv)); + Assert.Fail(asconEngine.AlgorithmName + " k size does not match"); } catch (ArgumentException) { @@ -191,25 +182,24 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k), iv1)); - Assert.Fail(aeadBlockCipher.AlgorithmName + "iv size does not match"); + asconEngine.Init(true, new ParametersWithIV(new KeyParameter(k), iv1)); + Assert.Fail(asconEngine.AlgorithmName + "iv size does not match"); } catch (ArgumentException) { //expected } - - aeadBlockCipher.Init(true, param); + asconEngine.Init(true, param); try { - aeadBlockCipher.DoFinal(c1, m.Length); + asconEngine.DoFinal(c1, m.Length); } catch (Exception) { - Assert.Fail(aeadBlockCipher.AlgorithmName + " allows no input for AAD and plaintext"); + Assert.Fail(asconEngine.AlgorithmName + " allows no input for AAD and plaintext"); } - byte[] mac2 = aeadBlockCipher.GetMac(); + byte[] mac2 = asconEngine.GetMac(); if (mac2 == null) { Assert.Fail("mac should not be empty after dofinal"); @@ -218,18 +208,18 @@ namespace Org.BouncyCastle.Crypto.Tests { Assert.Fail("mac should be equal when calling dofinal and getMac"); } - aeadBlockCipher.ProcessAadByte((byte)0); - byte[] mac1 = new byte[aeadBlockCipher.GetOutputSize(0)]; - aeadBlockCipher.DoFinal(mac1, 0); + asconEngine.ProcessAadByte((byte)0); + byte[] mac1 = new byte[asconEngine.GetOutputSize(0)]; + asconEngine.DoFinal(mac1, 0); if (Arrays.AreEqual(mac1, mac2)) { Assert.Fail("mac should not match"); } - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 0); + asconEngine.Reset(); + asconEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 0); try { - aeadBlockCipher.ProcessAadByte((byte)0); + asconEngine.ProcessAadByte((byte)0); Assert.Fail("ProcessAadByte(s) cannot be called after encryption/decryption"); } catch (ArgumentException) @@ -238,7 +228,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 0, 1); + asconEngine.ProcessAadBytes(new byte[] { 0 }, 0, 1); Assert.Fail("ProcessAadByte(s) cannot be called once only"); } catch (ArgumentException) @@ -246,10 +236,10 @@ namespace Org.BouncyCastle.Crypto.Tests //expected } - aeadBlockCipher.Reset(); + asconEngine.Reset(); try { - aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 1, 1); + asconEngine.ProcessAadBytes(new byte[] { 0 }, 1, 1); Assert.Fail("input for ProcessAadBytes is too short"); } catch (DataLengthException) @@ -258,7 +248,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0); + asconEngine.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0); Assert.Fail("input for ProcessBytes is too short"); } catch (DataLengthException) @@ -267,7 +257,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 8); + asconEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 8); Assert.Fail("output for ProcessBytes is too short"); } catch (OutputLengthException) @@ -276,7 +266,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.DoFinal(new byte[2], 2); + asconEngine.DoFinal(new byte[2], 2); Assert.Fail("output for dofinal is too short"); } catch (DataLengthException) @@ -284,59 +274,58 @@ namespace Org.BouncyCastle.Crypto.Tests //expected } - mac1 = new byte[aeadBlockCipher.GetOutputSize(0)]; - mac2 = new byte[aeadBlockCipher.GetOutputSize(0)]; - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2); - aeadBlockCipher.DoFinal(mac1, 0); - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessAadByte((byte)0); - aeadBlockCipher.ProcessAadByte((byte)0); - aeadBlockCipher.DoFinal(mac2, 0); + mac1 = new byte[asconEngine.GetOutputSize(0)]; + mac2 = new byte[asconEngine.GetOutputSize(0)]; + asconEngine.Reset(); + asconEngine.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2); + asconEngine.DoFinal(mac1, 0); + asconEngine.Reset(); + asconEngine.ProcessAadByte((byte)0); + asconEngine.ProcessAadByte((byte)0); + asconEngine.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { Assert.Fail("mac should match for the same AAD with different ways of inputing"); } - byte[] c2 = new byte[aeadBlockCipher.GetOutputSize(10)]; - byte[] c3 = new byte[aeadBlockCipher.GetOutputSize(10) + 2]; + byte[] c2 = new byte[asconEngine.GetOutputSize(10)]; + byte[] c3 = new byte[asconEngine.GetOutputSize(10) + 2]; byte[] aad2 = { 0, 1, 2, 3, 4 }; byte[] aad3 = { 0, 0, 1, 2, 3, 4, 5 }; byte[] m2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] m3 = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; byte[] m4 = new byte[m2.Length]; - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - int offset = aeadBlockCipher.ProcessBytes(m2, 0, m2.Length, c2, 0); - aeadBlockCipher.DoFinal(c2, offset); - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessAadBytes(aad3, 1, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(m3, 1, m2.Length, c3, 1); - aeadBlockCipher.DoFinal(c3, offset + 1); + asconEngine.Reset(); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + int offset = asconEngine.ProcessBytes(m2, 0, m2.Length, c2, 0); + asconEngine.DoFinal(c2, offset); + asconEngine.Reset(); + asconEngine.ProcessAadBytes(aad3, 1, aad2.Length); + offset = asconEngine.ProcessBytes(m3, 1, m2.Length, c3, 1); + asconEngine.DoFinal(c3, offset + 1); byte[] c3_partial = new byte[c2.Length]; Array.Copy(c3, 1, c3_partial, 0, c2.Length); if (!Arrays.AreEqual(c2, c3_partial)) { Assert.Fail("mac should match for the same AAD and message with different offset for both input and output"); } - aeadBlockCipher.Reset(); - aeadBlockCipher.Init(false, param); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0); - aeadBlockCipher.DoFinal(m4, offset); + asconEngine.Reset(); + asconEngine.Init(false, param); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); + asconEngine.DoFinal(m4, offset); if (!Arrays.AreEqual(m2, m4)) { Assert.Fail("The encryption and decryption does not recover the plaintext"); } - Console.WriteLine(aeadBlockCipher.AlgorithmName + " test Exceptions pass"); c2[c2.Length - 1] ^= 1; - aeadBlockCipher.Reset(); - aeadBlockCipher.Init(false, param); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0); + asconEngine.Reset(); + asconEngine.Init(false, param); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); try { - aeadBlockCipher.DoFinal(m4, offset); + asconEngine.DoFinal(m4, offset); Assert.Fail("The decryption should fail"); } catch (ArgumentException) @@ -345,29 +334,26 @@ namespace Org.BouncyCastle.Crypto.Tests } c2[c2.Length - 1] ^= 1; - byte[] m7 = new byte[blocksize * 2]; - for (int i = 0; i < m7.Length; ++i) - { - m7[i] = (byte)rand.Next(); - } - byte[] c7 = new byte[aeadBlockCipher.GetOutputSize(m7.Length)]; + byte[] m7 = new byte[32 + rand.Next(16)]; + rand.NextBytes(m7); + + byte[] c7 = new byte[asconEngine.GetOutputSize(m7.Length)]; byte[] c8 = new byte[c7.Length]; byte[] c9 = new byte[c7.Length]; - aeadBlockCipher.Init(true, param); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(m7, 0, m7.Length, c7, 0); - aeadBlockCipher.DoFinal(c7, offset); - aeadBlockCipher.Reset(); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(m7, 0, blocksize, c8, 0); - offset += aeadBlockCipher.ProcessBytes(m7, blocksize, m7.Length - blocksize, c8, offset); - aeadBlockCipher.DoFinal(c8, offset); - aeadBlockCipher.Reset(); - int split = rand.Next(blocksize * 2); - aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length); - offset = aeadBlockCipher.ProcessBytes(m7, 0, split, c9, 0); - offset += aeadBlockCipher.ProcessBytes(m7, split, m7.Length - split, c9, offset); - aeadBlockCipher.DoFinal(c9, offset); + asconEngine.Init(true, param); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = asconEngine.ProcessBytes(m7, 0, m7.Length, c7, 0); + asconEngine.DoFinal(c7, offset); + asconEngine.Reset(); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = asconEngine.ProcessBytes(m7, 0, m7.Length, c8, 0); + offset += asconEngine.DoFinal(c8, offset); + asconEngine.Reset(); + int split = rand.Next(1, m7.Length); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = asconEngine.ProcessBytes(m7, 0, split, c9, 0); + offset += asconEngine.ProcessBytes(m7, split, m7.Length - split, c9, offset); + asconEngine.DoFinal(c9, offset); if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9)) { Assert.Fail("Splitting input of plaintext should output the same ciphertext"); @@ -379,10 +365,10 @@ namespace Org.BouncyCastle.Crypto.Tests Span<byte> c4_2 = new byte[c2.Length]; ReadOnlySpan<byte> m5 = new ReadOnlySpan<byte>(m2); ReadOnlySpan<byte> aad4 = new ReadOnlySpan<byte>(aad2); - aeadBlockCipher.Init(true, param); - aeadBlockCipher.ProcessAadBytes(aad4); - offset = aeadBlockCipher.ProcessBytes(m5, c4_1); - aeadBlockCipher.DoFinal(c4_2); + asconEngine.Init(true, param); + asconEngine.ProcessAadBytes(aad4); + offset = asconEngine.ProcessBytes(m5, c4_1); + asconEngine.DoFinal(c4_2); byte[] c5 = new byte[c2.Length]; Array.Copy(c4_1.ToArray(), 0, c5, 0, offset); Array.Copy(c4_2.ToArray(), 0, c5, offset, c5.Length - offset); @@ -390,14 +376,14 @@ namespace Org.BouncyCastle.Crypto.Tests { Assert.Fail("mac should match for the same AAD and message with different offset for both input and output"); } - aeadBlockCipher.Reset(); - aeadBlockCipher.Init(false, param); + asconEngine.Reset(); + asconEngine.Init(false, param); Span<byte> m6_1 = new byte[m2.Length]; Span<byte> m6_2 = new byte[m2.Length]; ReadOnlySpan<byte> c6 = new ReadOnlySpan<byte>(c2); - aeadBlockCipher.ProcessAadBytes(aad4); - offset = aeadBlockCipher.ProcessBytes(c6, m6_1); - aeadBlockCipher.DoFinal(m6_2); + asconEngine.ProcessAadBytes(aad4); + offset = asconEngine.ProcessBytes(c6, m6_1); + asconEngine.DoFinal(m6_2); byte[] m6 = new byte[m2.Length]; Array.Copy(m6_1.ToArray(), 0, m6, 0, offset); Array.Copy(m6_2.ToArray(), 0, m6, offset, m6.Length - offset); @@ -408,25 +394,14 @@ namespace Org.BouncyCastle.Crypto.Tests #endif } - private void testParameters(AsconEngine ascon, int keySize, int ivSize, int macSize, int blockSize) + private void ImplTestParameters(AsconEngine asconEngine, int keySize, int ivSize, int macSize) { - if (ascon.GetKeyBytesSize() != keySize) - { - Assert.Fail("key bytes of " + ascon.AlgorithmName + " is not correct"); - } - if (ascon.GetIVBytesSize() != ivSize) - { - Assert.Fail("iv bytes of " + ascon.AlgorithmName + " is not correct"); - } - if (ascon.GetOutputSize(0) != macSize) - { - Assert.Fail("mac bytes of " + ascon.AlgorithmName + " is not correct"); - } - if (ascon.GetBlockSize() != blockSize) - { - Assert.Fail("block size of " + ascon.AlgorithmName + " is not correct"); - } - Console.WriteLine(ascon.AlgorithmName + " test Parameters pass"); + Assert.AreEqual(keySize, asconEngine.GetKeyBytesSize(), + "key bytes of " + asconEngine.AlgorithmName + " is not correct"); + Assert.AreEqual(ivSize, asconEngine.GetIVBytesSize(), + "iv bytes of " + asconEngine.AlgorithmName + " is not correct"); + Assert.AreEqual(macSize, asconEngine.GetOutputSize(0), + "mac bytes of " + asconEngine.AlgorithmName + " is not correct"); } } -} \ No newline at end of file +} |