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 /crypto/src | |
parent | Replace _Partial methods with _High/_Low variants (diff) | |
download | BouncyCastle.NET-ed25519-d5865255abae4b260a7e3191cc0d591c403eb6fa.tar.xz |
Refactor AsconEngine
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/engines/AsconEngine.cs | 976 |
1 files changed, 565 insertions, 411 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); } } } - - |