From 1a6f7e088051d618a04974542dc64bf9c48c6b8f Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 9 Feb 2023 16:48:32 +0700 Subject: Fix/refactor IsapEngine --- crypto/src/crypto/engines/ISAPEngine.cs | 783 ++++++++++++++++---------------- crypto/test/src/crypto/test/ISAPTest.cs | 347 +++++++------- 2 files changed, 559 insertions(+), 571 deletions(-) diff --git a/crypto/src/crypto/engines/ISAPEngine.cs b/crypto/src/crypto/engines/ISAPEngine.cs index 0280b1ce1..8be5a79b2 100644 --- a/crypto/src/crypto/engines/ISAPEngine.cs +++ b/crypto/src/crypto/engines/ISAPEngine.cs @@ -1,9 +1,14 @@ using System; +using System.Drawing; 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 { @@ -14,8 +19,8 @@ namespace Org.BouncyCastle.Crypto.Engines * ISAP AEAD v2 with reference to C Reference Impl from: https://github.com/isap-lwc/isap-code-package *

*/ - public class ISAPEngine - : IAeadBlockCipher + public sealed class IsapEngine + : IAeadCipher { public enum IsapType { @@ -25,42 +30,13 @@ namespace Org.BouncyCastle.Crypto.Engines ISAP_K_128 } - public ISAPEngine(IsapType isapType) - { - switch (isapType) - { - case IsapType.ISAP_A_128A: - ISAPAEAD = new ISAPAEAD_A_128A(); - ISAP_rH = 64; - algorithmName = "ISAP-A-128A AEAD"; - break; - case IsapType.ISAP_K_128A: - ISAPAEAD = new ISAPAEAD_K_128A(); - ISAP_rH = 144; - algorithmName = "ISAP-K-128A AEAD"; - break; - case IsapType.ISAP_A_128: - ISAPAEAD = new ISAPAEAD_A_128(); - ISAP_rH = 64; - algorithmName = "ISAP-A-128 AEAD"; - break; - case IsapType.ISAP_K_128: - ISAPAEAD = new ISAPAEAD_K_128(); - ISAP_rH = 144; - algorithmName = "ISAP-K-128 AEAD"; - break; - } - ISAP_rH_SZ = (ISAP_rH + 7) >> 3; - } + private const int CRYPTO_KEYBYTES = 16; + private const int CRYPTO_NPUBBYTES = 16; + private const int ISAP_STATE_SZ = 40; private string algorithmName; private bool forEncryption; private bool initialised; - const int CRYPTO_KEYBYTES = 16; - const int CRYPTO_NPUBBYTES = 16; - const int ISAP_STATE_SZ = 40; - private byte[] c; - private byte[] ad; private byte[] mac; private MemoryStream aadData = new MemoryStream(); private MemoryStream message = new MemoryStream(); @@ -69,18 +45,58 @@ namespace Org.BouncyCastle.Crypto.Engines private int ISAP_rH; private int ISAP_rH_SZ; - public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); + public IsapEngine(IsapType isapType) + { + switch (isapType) + { + case IsapType.ISAP_A_128A: + ISAPAEAD = new ISAPAEAD_A_128A(); + ISAP_rH = 64; + algorithmName = "ISAP-A-128A AEAD"; + break; + case IsapType.ISAP_K_128A: + ISAPAEAD = new ISAPAEAD_K_128A(); + ISAP_rH = 144; + algorithmName = "ISAP-K-128A AEAD"; + break; + case IsapType.ISAP_A_128: + ISAPAEAD = new ISAPAEAD_A_128(); + ISAP_rH = 64; + algorithmName = "ISAP-A-128 AEAD"; + break; + case IsapType.ISAP_K_128: + ISAPAEAD = new ISAPAEAD_K_128(); + ISAP_rH = 144; + algorithmName = "ISAP-K-128 AEAD"; + break; + } + ISAP_rH_SZ = (ISAP_rH + 7) >> 3; + } + + public int GetKeyBytesSize() + { + return CRYPTO_KEYBYTES; + } + + public int GetIVBytesSize() + { + return CRYPTO_NPUBBYTES; + } public string AlgorithmName => algorithmName; - protected abstract class ISAP_AEAD + private abstract class ISAP_AEAD { protected byte[] k; protected byte[] npub; protected int ISAP_rH; protected int ISAP_rH_SZ; - public abstract void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void isap_enc(ReadOnlySpan m, Span c); +#else + public abstract void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff); +#endif public abstract void init(byte[] k, byte[] npub, int ISAP_rH, int ISAP_rH_SZ); @@ -89,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Engines public abstract void reset(); } - protected abstract class ISAPAEAD_A : ISAP_AEAD + private abstract class ISAPAEAD_A : ISAP_AEAD { protected ulong[] k64; protected ulong[] npub64; @@ -104,14 +120,10 @@ namespace Org.BouncyCastle.Crypto.Engines this.npub = npub; this.ISAP_rH = ISAP_rH; this.ISAP_rH_SZ = ISAP_rH_SZ; - npub64 = new ulong[getulongSize(npub.Length)]; - Pack.LE_To_UInt64(npub, 0, npub64, 0, npub64.Length); - npub64[0] = U64BIG(npub64[0]); - npub64[1] = U64BIG(npub64[1]); - k64 = new ulong[getulongSize(k.Length)]; - Pack.LE_To_UInt64(k, 0, k64, 0, k64.Length); - k64[0] = U64BIG(k64[0]); - k64[1] = U64BIG(k64[1]); + npub64 = new ulong[(npub.Length + 7) / 8]; + Pack.BE_To_UInt64(npub, 0, npub64, 0, npub64.Length); + k64 = new ulong[(k.Length + 7) / 8]; + Pack.BE_To_UInt64(k, 0, k64, 0, k64.Length); reset(); } @@ -121,21 +133,19 @@ namespace Org.BouncyCastle.Crypto.Engines protected void ABSORB_MAC(byte[] src, int len) { - ulong[] src64 = new ulong[src.Length >> 3]; - Pack.LE_To_UInt64(src, 0, src64, 0, src64.Length); - int idx = 0; - while (len >= ISAP_rH_SZ) + int off = 0; + while (len >= 8) { - x0 ^= U64BIG(src64[idx++]); + x0 ^= Pack.BE_To_UInt64(src, off); + off += 8; + len -= 8; P12(); - len -= ISAP_rH_SZ; } - /* Absorb const ad block */ - for (int i = 0; i < len; ++i) + if (len > 0) { - x0 ^= (src[(idx << 3) + i] & 0xFFUL) << ((7 - i) << 3); + x0 ^= Pack.BE_To_UInt64_High(src, off, len); } - x0 ^= 0x80UL << ((7 - len) << 3); + x0 ^= 0x8000000000000000UL >> (len << 3); P12(); } @@ -152,8 +162,8 @@ namespace Org.BouncyCastle.Crypto.Engines x4 ^= 1L; ABSORB_MAC(c, clen); // Derive K* - Pack.UInt64_To_LE(U64BIG(x0), tag, 0); - Pack.UInt64_To_LE(U64BIG(x1), tag, 8); + Pack.UInt64_To_BE(x0, tag, 0); + Pack.UInt64_To_BE(x1, tag, 8); ulong tmp_x2 = x2, tmp_x3 = x3, tmp_x4 = x4; isap_rk(ISAP_IV2_64, tag, CRYPTO_KEYBYTES); x2 = tmp_x2; @@ -161,8 +171,8 @@ namespace Org.BouncyCastle.Crypto.Engines x4 = tmp_x4; // Squeeze tag P12(); - Pack.UInt64_To_LE(U64BIG(x0), tag, tagOff); - Pack.UInt64_To_LE(U64BIG(x1), tag, tagOff + 8); + Pack.UInt64_To_BE(x0, tag, tagOff); + Pack.UInt64_To_BE(x1, tag, tagOff + 8); } public void isap_rk(ulong iv64, byte[] y, int ylen) @@ -183,28 +193,46 @@ namespace Org.BouncyCastle.Crypto.Engines P12(); } - public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void isap_enc(ReadOnlySpan m, Span c) { - /* Encrypt m */ - ulong[] m64 = new ulong[mlen >> 3]; - Pack.LE_To_UInt64(m, mOff, m64, 0, m64.Length); - ulong[] c64 = new ulong[m64.Length]; - int idx = 0; - while (mlen >= ISAP_rH_SZ) + while (m.Length >= 8) { - c64[idx] = U64BIG(x0) ^ m64[idx]; + ulong t = Pack.BE_To_UInt64(m); + t ^= x0; + Pack.UInt64_To_BE(t, c); + m = m[8..]; + c = c[8..]; + PX1(); + } + if (!m.IsEmpty) + { + ulong t = Pack.BE_To_UInt64_High(m); + t ^= x0; + Pack.UInt64_To_BE_High(t, c[..m.Length]); + } + } +#else + public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff) + { + while (mlen >= 8) + { + ulong t = Pack.BE_To_UInt64(m, mOff); + t ^= x0; + Pack.UInt64_To_BE(t, c, cOff); + mOff += 8; + mlen -= 8; + cOff += 8; PX1(); - idx++; - mlen -= ISAP_rH_SZ; } - Pack.UInt64_To_LE(c64, 0, c64.Length, c, cOff); - /* Encrypt const m block */ - byte[] xo = Pack.UInt64_To_LE(x0); - while (mlen > 0) + if (mlen > 0) { - c[(idx << 3) + cOff + mlen - 1] = (byte)(xo[ISAP_rH_SZ - mlen] ^ m[(idx << 3) + mOff + --mlen]); + ulong t = Pack.BE_To_UInt64_High(m, mOff, mlen); + t ^= x0; + Pack.UInt64_To_BE_High(t, c, cOff, mlen); } } +#endif public override void reset() { @@ -215,22 +243,9 @@ namespace Org.BouncyCastle.Crypto.Engines PX1(); } - private int getulongSize(int x) - { - return (x >> 3) + ((x & 7) != 0 ? 1 : 0); - } - - private ulong ROTR(ulong x, int n) - { - return (x >> n) | (x << (64 - n)); - } - - protected ulong U64BIG(ulong x) - { - return ((ROTR(x, 8) & (0xFF000000FF000000UL)) | (ROTR(x, 24) & (0x00FF000000FF0000UL)) | - (ROTR(x, 40) & (0x0000FF000000FF00UL)) | (ROTR(x, 56) & (0x000000FF000000FFUL))); - } - +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif protected void ROUND(ulong C) { t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); @@ -238,11 +253,11 @@ namespace Org.BouncyCastle.Crypto.Engines t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); - x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28); - x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61); - x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6)); - x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17); - x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41); + 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); } public void P12() @@ -315,8 +330,9 @@ namespace Org.BouncyCastle.Crypto.Engines protected ushort[] ISAP_IV3_16; protected ushort[] k16; protected ushort[] iv16; - private readonly int[] KeccakF400RoundConstants = {0x0001, 0x8082, 0x808a, 0x8000, 0x808b, 0x0001, 0x8081, 0x8009, - 0x008a, 0x0088, 0x8009, 0x000a, 0x808b, 0x008b, 0x8089, 0x8003, 0x8002, 0x0080, 0x800a, 0x000a}; + private readonly int[] KeccakF400RoundConstants = { + 0x0001, 0x8082, 0x808a, 0x8000, 0x808b, 0x0001, 0x8081, 0x8009, 0x008a, 0x0088, 0x8009, 0x000a, 0x808b, + 0x008b, 0x8089, 0x8003, 0x8002, 0x0080, 0x800a, 0x000a }; protected ushort[] SX = new ushort[25]; protected ushort[] E = new ushort[25]; protected ushort[] C = new ushort[5]; @@ -328,9 +344,9 @@ namespace Org.BouncyCastle.Crypto.Engines this.ISAP_rH = ISAP_rH; this.ISAP_rH_SZ = ISAP_rH_SZ; k16 = new ushort[k.Length >> 1]; - byteToushort(k, k16, k16.Length); + Pack.LE_To_UInt16(k, 0, k16); iv16 = new ushort[npub.Length >> 1]; - byteToushort(npub, iv16, iv16.Length); + Pack.LE_To_UInt16(npub, 0, iv16); reset(); } @@ -355,33 +371,28 @@ namespace Org.BouncyCastle.Crypto.Engines { int rem_bytes = len; int idx = 0; - while (true) + while (rem_bytes > ISAP_rH_SZ) { - if (rem_bytes > ISAP_rH_SZ) - { - byteToushortXor(src, SX, ISAP_rH_SZ >> 1); - idx += ISAP_rH_SZ; - rem_bytes -= ISAP_rH_SZ; - PermuteRoundsHX(SX, E, C); - } - else if (rem_bytes == ISAP_rH_SZ) - { - byteToushortXor(src, SX, ISAP_rH_SZ >> 1); - PermuteRoundsHX(SX, E, C); - SX[0] ^= 0x80; - PermuteRoundsHX(SX, E, C); - break; - } - else + byteToushortXor(src, SX, ISAP_rH_SZ >> 1); + idx += ISAP_rH_SZ; + rem_bytes -= ISAP_rH_SZ; + PermuteRoundsHX(SX, E, C); + } + if (rem_bytes == ISAP_rH_SZ) + { + byteToushortXor(src, SX, ISAP_rH_SZ >> 1); + PermuteRoundsHX(SX, E, C); + SX[0] ^= 0x80; + PermuteRoundsHX(SX, E, C); + } + else + { + for (int i = 0; i < rem_bytes; i++) { - for (int i = 0; i < rem_bytes; i++) - { - SX[i >> 1] ^= (ushort)((src[idx++] & 0xFFU) << ((i & 1) << 3)); - } - SX[rem_bytes >> 1] ^= (ushort)(0x80U << ((rem_bytes & 1) << 3)); - PermuteRoundsHX(SX, E, C); - break; + SX[i >> 1] ^= (ushort)((src[idx++] & 0xFFU) << ((i & 1) << 3)); } + SX[rem_bytes >> 1] ^= (ushort)(0x80U << ((rem_bytes & 1) << 3)); + PermuteRoundsHX(SX, E, C); } } @@ -419,61 +430,64 @@ namespace Org.BouncyCastle.Crypto.Engines // Absorb C ABSORB_MAC(SX, c, clen, E, C); // Derive K* - ushortToByte(SX, tag, tagOff); + Pack.UInt16_To_LE(SX, 0, 8, tag, tagOff); isap_rk(ISAP_IV2_16, tag, CRYPTO_KEYBYTES, SX, CRYPTO_KEYBYTES, C); // Squeeze tag PermuteRoundsHX(SX, E, C); - ushortToByte(SX, tag, tagOff); + Pack.UInt16_To_LE(SX, 0, 8, tag, tagOff); } - public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void isap_enc(ReadOnlySpan m, Span c) { + int off = 0, len = m.Length; + // Squeeze key stream - while (true) + while (len >= ISAP_rH_SZ) { - if (mlen >= ISAP_rH_SZ) - { - // Squeeze full lane and continue - for (int i = 0; i < ISAP_rH_SZ; ++i) - { - c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]); - } - mlen -= ISAP_rH_SZ; - PermuteRoundsKX(SX, E, C); - } - else + // Squeeze full lane and continue + for (int i = 0; i < ISAP_rH_SZ; ++i) { - // Squeeze full or partial lane and stop - for (int i = 0; i < mlen; ++i) - { - c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]); - } - break; + c[off] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[off]); + ++off; } + len -= ISAP_rH_SZ; + PermuteRoundsKX(SX, E, C); } - } - - private void byteToushortXor(byte[] input, ushort[] output, int outLen) - { - for (int i = 0; i < outLen; ++i) + // Squeeze partial lane and stop + for (int i = 0; i < len; ++i) { - output[i] ^= Pack.LE_To_UInt16(input, (i << 1)); + c[off] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[off]); + ++off; } } - - private void byteToushort(byte[] input, ushort[] output, int outLen) +#else + public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff) { - for (int i = 0; i < outLen; ++i) + // Squeeze key stream + while (mlen >= ISAP_rH_SZ) + { + // Squeeze full lane and continue + for (int i = 0; i < ISAP_rH_SZ; ++i) + { + c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]); + } + mlen -= ISAP_rH_SZ; + PermuteRoundsKX(SX, E, C); + } + // Squeeze partial lane and stop + for (int i = 0; i < mlen; ++i) { - output[i] = Pack.LE_To_UInt16(input, (i << 1)); + c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]); } } +#endif - private void ushortToByte(ushort[] input, byte[] output, int outOff) + private void byteToushortXor(byte[] input, ushort[] output, int outLen) { - for (int i = 0; i < 8; ++i) + for (int i = 0; i < outLen; ++i) { - shortToLittleEndian(input[i], output, outOff + (i << 1)); + output[i] ^= Pack.LE_To_UInt16(input, (i << 1)); } } @@ -522,28 +536,23 @@ namespace Org.BouncyCastle.Crypto.Engines C[4] = (ushort)(SX[4] ^ SX[9] ^ SX[14] ^ SX[19] ^ SX[24]); } - private ushort ROL16(ushort a, int offset) - { - return (ushort)(((a & 0xFFFF) << offset) ^ ((a & 0xFFFF) >> (16 - offset))); - } - protected void thetaRhoPiChiIotaPrepareTheta(int i, ushort[] A, ushort[] E, ushort[] C) { - ushort Da = (ushort)(C[4] ^ ROL16(C[1], 1)); - ushort De = (ushort)(C[0] ^ ROL16(C[2], 1)); - ushort Di = (ushort)(C[1] ^ ROL16(C[3], 1)); - ushort Do = (ushort)(C[2] ^ ROL16(C[4], 1)); - ushort Du = (ushort)(C[3] ^ ROL16(C[0], 1)); + ushort Da = (ushort)(C[4] ^ Shorts.RotateLeft(C[1], 1)); + ushort De = (ushort)(C[0] ^ Shorts.RotateLeft(C[2], 1)); + ushort Di = (ushort)(C[1] ^ Shorts.RotateLeft(C[3], 1)); + ushort Do = (ushort)(C[2] ^ Shorts.RotateLeft(C[4], 1)); + ushort Du = (ushort)(C[3] ^ Shorts.RotateLeft(C[0], 1)); ushort Ba = A[0] ^= Da; A[6] ^= De; - ushort Be = ROL16(A[6], 12); + ushort Be = Shorts.RotateLeft(A[6], 12); A[12] ^= Di; - ushort Bi = ROL16(A[12], 11); + ushort Bi = Shorts.RotateLeft(A[12], 11); A[18] ^= Do; - ushort Bo = ROL16(A[18], 5); + ushort Bo = Shorts.RotateLeft(A[18], 5); A[24] ^= Du; - ushort Bu = ROL16(A[24], 14); + ushort Bu = Shorts.RotateLeft(A[24], 14); C[0] = E[0] = (ushort)(Ba ^ ((~Be) & Bi) ^ KeccakF400RoundConstants[i]); C[1] = E[1] = (ushort)(Be ^ ((~Bi) & Bo)); C[2] = E[2] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -551,15 +560,15 @@ namespace Org.BouncyCastle.Crypto.Engines C[4] = E[4] = (ushort)(Bu ^ ((~Ba) & Be)); A[3] ^= Do; - Ba = ROL16(A[3], 12); + Ba = Shorts.RotateLeft(A[3], 12); A[9] ^= Du; - Be = ROL16(A[9], 4); + Be = Shorts.RotateLeft(A[9], 4); A[10] ^= Da; - Bi = ROL16(A[10], 3); + Bi = Shorts.RotateLeft(A[10], 3); A[16] ^= De; - Bo = ROL16(A[16], 13); + Bo = Shorts.RotateLeft(A[16], 13); A[22] ^= Di; - Bu = ROL16(A[22], 13); + Bu = Shorts.RotateLeft(A[22], 13); E[5] = (ushort)(Ba ^ ((~Be) & Bi)); C[0] ^= E[5]; E[6] = (ushort)(Be ^ ((~Bi) & Bo)); @@ -572,15 +581,15 @@ namespace Org.BouncyCastle.Crypto.Engines C[4] ^= E[9]; A[1] ^= De; - Ba = ROL16(A[1], 1); + Ba = Shorts.RotateLeft(A[1], 1); A[7] ^= Di; - Be = ROL16(A[7], 6); + Be = Shorts.RotateLeft(A[7], 6); A[13] ^= Do; - Bi = ROL16(A[13], 9); + Bi = Shorts.RotateLeft(A[13], 9); A[19] ^= Du; - Bo = ROL16(A[19], 8); + Bo = Shorts.RotateLeft(A[19], 8); A[20] ^= Da; - Bu = ROL16(A[20], 2); + Bu = Shorts.RotateLeft(A[20], 2); E[10] = (ushort)(Ba ^ ((~Be) & Bi)); C[0] ^= E[10]; E[11] = (ushort)(Be ^ ((~Bi) & Bo)); @@ -593,15 +602,15 @@ namespace Org.BouncyCastle.Crypto.Engines C[4] ^= E[14]; A[4] ^= Du; - Ba = ROL16(A[4], 11); + Ba = Shorts.RotateLeft(A[4], 11); A[5] ^= Da; - Be = ROL16(A[5], 4); + Be = Shorts.RotateLeft(A[5], 4); A[11] ^= De; - Bi = ROL16(A[11], 10); + Bi = Shorts.RotateLeft(A[11], 10); A[17] ^= Di; - Bo = ROL16(A[17], 15); + Bo = Shorts.RotateLeft(A[17], 15); A[23] ^= Do; - Bu = ROL16(A[23], 8); + Bu = Shorts.RotateLeft(A[23], 8); E[15] = (ushort)(Ba ^ ((~Be) & Bi)); C[0] ^= E[15]; E[16] = (ushort)(Be ^ ((~Bi) & Bo)); @@ -614,15 +623,15 @@ namespace Org.BouncyCastle.Crypto.Engines C[4] ^= E[19]; A[2] ^= Di; - Ba = ROL16(A[2], 14); + Ba = Shorts.RotateLeft(A[2], 14); A[8] ^= Do; - Be = ROL16(A[8], 7); + Be = Shorts.RotateLeft(A[8], 7); A[14] ^= Du; - Bi = ROL16(A[14], 7); + Bi = Shorts.RotateLeft(A[14], 7); A[15] ^= Da; - Bo = ROL16(A[15], 9); + Bo = Shorts.RotateLeft(A[15], 9); A[21] ^= De; - Bu = ROL16(A[21], 2); + Bu = Shorts.RotateLeft(A[21], 2); E[20] = (ushort)(Ba ^ ((~Be) & Bi)); C[0] ^= E[20]; E[21] = (ushort)(Be ^ ((~Bi) & Bo)); @@ -637,21 +646,21 @@ namespace Org.BouncyCastle.Crypto.Engines protected void thetaRhoPiChiIota(ushort[] A, ushort[] E, ushort[] C) { - ushort Da = (ushort)(C[4] ^ ROL16(C[1], 1)); - ushort De = (ushort)(C[0] ^ ROL16(C[2], 1)); - ushort Di = (ushort)(C[1] ^ ROL16(C[3], 1)); - ushort Do = (ushort)(C[2] ^ ROL16(C[4], 1)); - ushort Du = (ushort)(C[3] ^ ROL16(C[0], 1)); + ushort Da = (ushort)(C[4] ^ Shorts.RotateLeft(C[1], 1)); + ushort De = (ushort)(C[0] ^ Shorts.RotateLeft(C[2], 1)); + ushort Di = (ushort)(C[1] ^ Shorts.RotateLeft(C[3], 1)); + ushort Do = (ushort)(C[2] ^ Shorts.RotateLeft(C[4], 1)); + ushort Du = (ushort)(C[3] ^ Shorts.RotateLeft(C[0], 1)); ushort Ba = A[0] ^= Da; A[6] ^= De; - ushort Be = ROL16(A[6], 12); + ushort Be = Shorts.RotateLeft(A[6], 12); A[12] ^= Di; - ushort Bi = ROL16(A[12], 11); + ushort Bi = Shorts.RotateLeft(A[12], 11); A[18] ^= Do; - ushort Bo = ROL16(A[18], 5); + ushort Bo = Shorts.RotateLeft(A[18], 5); A[24] ^= Du; - ushort Bu = ROL16(A[24], 14); + ushort Bu = Shorts.RotateLeft(A[24], 14); E[0] = (ushort)(Ba ^ ((~Be) & Bi) ^ KeccakF400RoundConstants[19]); E[1] = (ushort)(Be ^ ((~Bi) & Bo)); E[2] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -659,15 +668,15 @@ namespace Org.BouncyCastle.Crypto.Engines E[4] = (ushort)(Bu ^ ((~Ba) & Be)); A[3] ^= Do; - Ba = ROL16(A[3], 12); + Ba = Shorts.RotateLeft(A[3], 12); A[9] ^= Du; - Be = ROL16(A[9], 4); + Be = Shorts.RotateLeft(A[9], 4); A[10] ^= Da; - Bi = ROL16(A[10], 3); + Bi = Shorts.RotateLeft(A[10], 3); A[16] ^= De; - Bo = ROL16(A[16], 13); + Bo = Shorts.RotateLeft(A[16], 13); A[22] ^= Di; - Bu = ROL16(A[22], 13); + Bu = Shorts.RotateLeft(A[22], 13); E[5] = (ushort)(Ba ^ ((~Be) & Bi)); E[6] = (ushort)(Be ^ ((~Bi) & Bo)); E[7] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -675,15 +684,15 @@ namespace Org.BouncyCastle.Crypto.Engines E[9] = (ushort)(Bu ^ ((~Ba) & Be)); A[1] ^= De; - Ba = ROL16(A[1], 1); + Ba = Shorts.RotateLeft(A[1], 1); A[7] ^= Di; - Be = ROL16(A[7], 6); + Be = Shorts.RotateLeft(A[7], 6); A[13] ^= Do; - Bi = ROL16(A[13], 9); + Bi = Shorts.RotateLeft(A[13], 9); A[19] ^= Du; - Bo = ROL16(A[19], 8); + Bo = Shorts.RotateLeft(A[19], 8); A[20] ^= Da; - Bu = ROL16(A[20], 2); + Bu = Shorts.RotateLeft(A[20], 2); E[10] = (ushort)(Ba ^ ((~Be) & Bi)); E[11] = (ushort)(Be ^ ((~Bi) & Bo)); E[12] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -691,15 +700,15 @@ namespace Org.BouncyCastle.Crypto.Engines E[14] = (ushort)(Bu ^ ((~Ba) & Be)); A[4] ^= Du; - Ba = ROL16(A[4], 11); + Ba = Shorts.RotateLeft(A[4], 11); A[5] ^= Da; - Be = ROL16(A[5], 4); + Be = Shorts.RotateLeft(A[5], 4); A[11] ^= De; - Bi = ROL16(A[11], 10); + Bi = Shorts.RotateLeft(A[11], 10); A[17] ^= Di; - Bo = ROL16(A[17], 15); + Bo = Shorts.RotateLeft(A[17], 15); A[23] ^= Do; - Bu = ROL16(A[23], 8); + Bu = Shorts.RotateLeft(A[23], 8); E[15] = (ushort)(Ba ^ ((~Be) & Bi)); E[16] = (ushort)(Be ^ ((~Bi) & Bo)); E[17] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -707,15 +716,15 @@ namespace Org.BouncyCastle.Crypto.Engines E[19] = (ushort)(Bu ^ ((~Ba) & Be)); A[2] ^= Di; - Ba = ROL16(A[2], 14); + Ba = Shorts.RotateLeft(A[2], 14); A[8] ^= Do; - Be = ROL16(A[8], 7); + Be = Shorts.RotateLeft(A[8], 7); A[14] ^= Du; - Bi = ROL16(A[14], 7); + Bi = Shorts.RotateLeft(A[14], 7); A[15] ^= Da; - Bo = ROL16(A[15], 9); + Bo = Shorts.RotateLeft(A[15], 9); A[21] ^= De; - Bu = ROL16(A[21], 2); + Bu = Shorts.RotateLeft(A[21], 2); E[20] = (ushort)(Ba ^ ((~Be) & Bi)); E[21] = (ushort)(Be ^ ((~Bi) & Bo)); E[22] = (ushort)(Bi ^ ((~Bo) & Bu)); @@ -728,9 +737,9 @@ namespace Org.BouncyCastle.Crypto.Engines { public ISAPAEAD_K_128A() { - ISAP_IV1_16 = new ushort[] { 32769, 400, 272, 2056 }; - ISAP_IV2_16 = new ushort[] { 32770, 400, 272, 2056 }; - ISAP_IV3_16 = new ushort[] { 32771, 400, 272, 2056 }; + ISAP_IV1_16 = new ushort[]{ 32769, 400, 272, 2056 }; + ISAP_IV2_16 = new ushort[]{ 32770, 400, 272, 2056 }; + ISAP_IV3_16 = new ushort[]{ 32771, 400, 272, 2056 }; } protected override void PermuteRoundsHX(ushort[] SX, ushort[] E, ushort[] C) @@ -758,9 +767,9 @@ namespace Org.BouncyCastle.Crypto.Engines { public ISAPAEAD_K_128() { - ISAP_IV1_16 = new ushort[] { 32769, 400, 3092, 3084 }; - ISAP_IV2_16 = new ushort[] { 32770, 400, 3092, 3084 }; - ISAP_IV3_16 = new ushort[] { 32771, 400, 3092, 3084 }; + ISAP_IV1_16 = new ushort[]{ 32769, 400, 3092, 3084 }; + ISAP_IV2_16 = new ushort[]{ 32770, 400, 3092, 3084 }; + ISAP_IV3_16 = new ushort[]{ 32771, 400, 3092, 3084 }; } protected override void PermuteRoundsHX(ushort[] SX, ushort[] E, ushort[] C) @@ -784,251 +793,241 @@ namespace Org.BouncyCastle.Crypto.Engines } } - - public void Init(bool forEncryption, ICipherParameters param) { this.forEncryption = forEncryption; - if (!(param is ParametersWithIV)) - { - throw new ArgumentException( - "ISAP AEAD init parameters must include an IV"); - } - - ParametersWithIV ivParams = (ParametersWithIV)param; - - byte[] iv = ivParams.GetIV(); + if (!(param is ParametersWithIV withIV)) + throw new ArgumentException("ISAP AEAD init parameters must include an IV"); + byte[] iv = withIV.GetIV(); if (iv == null || iv.Length != 16) - { - throw new ArgumentException( - "ISAP AEAD requires exactly 12 bytes of IV"); - } + throw new ArgumentException("ISAP AEAD requires exactly 12 bytes of IV"); - if (!(ivParams.Parameters is KeyParameter)) - { - throw new ArgumentException( - "ISAP AEAD init parameters must include a key"); - } + if (!(withIV.Parameters is KeyParameter key)) + throw new ArgumentException("ISAP AEAD init parameters must include a key"); - KeyParameter key = (KeyParameter)ivParams.Parameters; byte[] keyBytes = key.GetKey(); if (keyBytes.Length != 16) - { - throw new ArgumentException( - "ISAP AEAD key must be 128 bits ulong"); - } + throw new ArgumentException("ISAP AEAD key must be 128 bits ulong"); /* * Initialize variables. */ - byte[] npub = new byte[iv.Length]; - byte[] k = new byte[keyBytes.Length]; - Array.Copy(iv, 0, npub, 0, iv.Length); - Array.Copy(keyBytes, 0, k, 0, keyBytes.Length); initialised = true; - ISAPAEAD.init(k, npub, ISAP_rH, ISAP_rH_SZ); + ISAPAEAD.init(keyBytes, iv, ISAP_rH, ISAP_rH_SZ); Reset(); } public void ProcessAadByte(byte input) { - aadData.Write(new byte[] { input }, 0, 1); + aadData.WriteByte(input); } + public void ProcessAadBytes(byte[] inBytes, int inOff, int len) + { + Check.DataLength(inBytes, inOff, len, "input buffer too short"); + + aadData.Write(inBytes, inOff, len); + } - public void ProcessAadBytes(byte[] input, int inOff, int len) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void ProcessAadBytes(ReadOnlySpan input) { - if ((inOff + len) > input.Length) - { - throw new DataLengthException("input buffer too short" + (forEncryption ? "encryption" : "decryption")); - } - aadData.Write(input, inOff, len); + 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 + } - public int ProcessByte(byte input, byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessByte(byte input, Span output) { + Span singleByte = stackalloc byte[1]{ input }; - return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + return ProcessBytes(singleByte, output); } +#endif - - public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { + 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); + + if (forEncryption) { - throw new ArgumentException("Need call init function before encryption/decryption"); - } - if ((inOff + len) > input.Length) - { - throw new DataLengthException("input buffer too short"); + int msgLen = Convert.ToInt32(message.Length); + if (msgLen >= ISAP_rH_SZ) + { + int outLen = msgLen / ISAP_rH_SZ * ISAP_rH_SZ; + Check.OutputLength(outBytes, outOff, outLen, "output buffer is too short"); + byte[] enc_input = message.GetBuffer(); + ISAPAEAD.isap_enc(enc_input, 0, outLen, outBytes, outOff); + outputStream.Write(outBytes, outOff, outLen); + int enc_input_len = msgLen; + message.SetLength(0); + message.Write(enc_input, outLen, enc_input_len - outLen); + return outLen; + } } - message.Write(input, inOff, len); + return 0; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBytes(ReadOnlySpan input, Span output) + { + if (!initialised) + throw new ArgumentException("Need to call Init function before encryption/decryption"); + + message.Write(input); + if (forEncryption) { - if (message.Length >= ISAP_rH_SZ) + int msgLen = Convert.ToInt32(message.Length); + if (msgLen >= ISAP_rH_SZ) { - len = (int)message.Length / ISAP_rH_SZ * ISAP_rH_SZ; - if (outOff + len > output.Length) - { - throw new OutputLengthException("output buffer is too short"); - } + int outLen = msgLen / ISAP_rH_SZ * ISAP_rH_SZ; + Check.OutputLength(output, outLen, "output buffer is too short"); byte[] enc_input = message.GetBuffer(); - ISAPAEAD.isap_enc(enc_input, 0, len, output, outOff, output.Length); - outputStream.Write(output, outOff, len); - int enc_input_len = (int)message.Length; + ISAPAEAD.isap_enc(enc_input.AsSpan(0, outLen), output); + outputStream.Write(output[..outLen]); + int enc_input_len = msgLen; message.SetLength(0); - message.Write(enc_input, len, enc_input_len - len); - return len; + message.Write(enc_input, outLen, enc_input_len - outLen); + return outLen; } } return 0; } +#endif - - public int DoFinal(byte[] output, int outOff) + 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"); - } - int len; + + byte[] aad = aadData.GetBuffer(); + byte[] msg = message.GetBuffer(); + + int aadLen = Convert.ToInt32(aadData.Length); + int msgLen = Convert.ToInt32(message.Length); + int outLen; if (forEncryption) { - byte[] enc_input = message.GetBuffer(); - len = (int)message.Length; - if (outOff + len + 16 > output.Length) - { - throw new OutputLengthException("output buffer is too short"); - } - ISAPAEAD.isap_enc(enc_input, 0, len, output, outOff, output.Length); - outputStream.Write(output, outOff, len); - outOff += len; - ad = aadData.GetBuffer(); - c = outputStream.GetBuffer(); + outLen = msgLen + 16; + Check.OutputLength(outBytes, outOff, outLen, "output buffer is too short"); + ISAPAEAD.isap_enc(msg, 0, msgLen, outBytes, outOff); + outputStream.Write(outBytes, outOff, msgLen); + outOff += msgLen; + byte[] c = outputStream.GetBuffer(); mac = new byte[16]; - ISAPAEAD.isap_mac(ad, (int)aadData.Length, c, (int)outputStream.Length, mac, 0); - Array.Copy(mac, 0, output, outOff, 16); - len += 16; + ISAPAEAD.isap_mac(aad, aadLen, c, Convert.ToInt32(outputStream.Length), mac, 0); + Array.Copy(mac, 0, outBytes, outOff, 16); } else { - ad = aadData.GetBuffer(); - int adlen = (int)aadData.Length; - c = message.GetBuffer(); - int clen = (int)message.Length; + outLen = msgLen - 16; + Check.OutputLength(outBytes, outOff, outLen, "output buffer is too short"); mac = new byte[16]; - len = clen - mac.Length; - if (len + outOff > output.Length) - { - throw new OutputLengthException("output buffer is too short"); - } - ISAPAEAD.isap_mac(ad, adlen, c, len, mac, 0); + ISAPAEAD.isap_mac(aad, aadLen, msg, outLen, mac, 0); ISAPAEAD.reset(); - for (int i = 0; i < 16; ++i) - { - if (mac[i] != c[len + i]) - { - throw new ArgumentException("Mac does not match"); - } - } - ISAPAEAD.isap_enc(c, 0, len, output, outOff, output.Length); + if (!Arrays.FixedTimeEquals(16, mac, 0, msg, outLen)) + throw new ArgumentException("Mac does not match"); + ISAPAEAD.isap_enc(msg, 0, outLen, outBytes, outOff); } - return len; + return outLen; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span output) + { + if (!initialised) + throw new ArgumentException("Need call init function before encryption/decryption"); + + byte[] aad = aadData.GetBuffer(); + byte[] msg = message.GetBuffer(); + + int aadLen = Convert.ToInt32(aadData.Length); + int msgLen = Convert.ToInt32(message.Length); + int outLen; + if (forEncryption) + { + outLen = msgLen + 16; + Check.OutputLength(output, outLen, "output buffer is too short"); + ISAPAEAD.isap_enc(msg.AsSpan(0, msgLen), output); + outputStream.Write(output[..msgLen]); + output = output[msgLen..]; + byte[] c = outputStream.GetBuffer(); + mac = new byte[16]; + ISAPAEAD.isap_mac(aad, aadLen, c, Convert.ToInt32(outputStream.Length), mac, 0); + mac.CopyTo(output); + } + else + { + outLen = msgLen - 16; + Check.OutputLength(output, outLen, "output buffer is too short"); + mac = new byte[16]; + ISAPAEAD.isap_mac(aad, aadLen, msg, outLen, mac, 0); + ISAPAEAD.reset(); + if (!Arrays.FixedTimeEquals(16, mac, 0, msg, outLen)) + throw new ArgumentException("Mac does not match"); + ISAPAEAD.isap_enc(msg.AsSpan(0, outLen), output); + } + return outLen; + } +#endif public byte[] GetMac() { return mac; } - public int GetUpdateOutputSize(int len) { - return len; - } + if (!forEncryption) + return 0; + int totalData = Convert.ToInt32(message.Length + len); + return totalData - totalData % ISAP_rH_SZ; + } public int GetOutputSize(int len) { - return len + 16; - } + int totalData = Convert.ToInt32(message.Length + len); + if (forEncryption) + return totalData + 16; + + return System.Math.Max(0, totalData - 16); + } public void Reset() { if (!initialised) - { throw new ArgumentException("Need call init function before encryption/decryption"); - } + aadData.SetLength(0); ISAPAEAD.reset(); message.SetLength(0); outputStream.SetLength(0); } - - private static void shortToLittleEndian(ushort n, byte[] bs, int off) - { - bs[off] = (byte)(n); - bs[++off] = (byte)(n >> 8); - } - - public int GetBlockSize() - { - return ISAP_rH_SZ; - } - -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public void ProcessAadBytes(ReadOnlySpan input) - { - aadData.Write(input); - } - - public int ProcessByte(byte input, Span 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 input, Span 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 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 GetKeyBytesSize() - { - return CRYPTO_KEYBYTES; - } - - public int GetIVBytesSize() - { - return CRYPTO_NPUBBYTES; - } } } - - diff --git a/crypto/test/src/crypto/test/ISAPTest.cs b/crypto/test/src/crypto/test/ISAPTest.cs index f38722e4a..ce19b38d5 100644 --- a/crypto/test/src/crypto/test/ISAPTest.cs +++ b/crypto/test/src/crypto/test/ISAPTest.cs @@ -6,63 +6,55 @@ using NUnit.Framework; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; -using static Org.BouncyCastle.Crypto.Engines.ISAPEngine; - namespace Org.BouncyCastle.Crypto.Tests { [TestFixture] - public class ISAPTest : SimpleTest + public class IsapTest + : SimpleTest { - public override string Name - { - get { return "ISAP"; } - } + public override string Name => "ISAP"; [Test] public override void PerformTest() { - ISAPEngine ISAP = new ISAPEngine(IsapType.ISAP_K_128A); - testExceptions(ISAP, ISAP.GetKeyBytesSize(), ISAP.GetIVBytesSize(), ISAP.GetBlockSize()); - testParameters(ISAP, 16, 16, 16, 18); - ISAP = new ISAPEngine(IsapType.ISAP_K_128); - testExceptions(ISAP, ISAP.GetKeyBytesSize(), ISAP.GetIVBytesSize(), ISAP.GetBlockSize()); - testParameters(ISAP, 16, 16, 16, 18); - ISAP = new ISAPEngine(IsapType.ISAP_A_128A); - testExceptions(ISAP, ISAP.GetKeyBytesSize(), ISAP.GetIVBytesSize(), ISAP.GetBlockSize()); - testParameters(ISAP, 16, 16, 16, 8); - ISAP = new ISAPEngine(IsapType.ISAP_A_128); - testExceptions(ISAP, ISAP.GetKeyBytesSize(), ISAP.GetIVBytesSize(), ISAP.GetBlockSize()); - testParameters(ISAP, 16, 16, 16, 8); - testExceptions(new ISAPDigest(), 32); - testVectors("isapa128av20", IsapType.ISAP_A_128A); - testVectors("isapa128v20", IsapType.ISAP_A_128); - testVectors("isapk128av20", IsapType.ISAP_K_128A); - testVectors("isapk128v20", IsapType.ISAP_K_128); - testVectors(); + IsapEngine isapEngine = new IsapEngine(IsapEngine.IsapType.ISAP_K_128A); + ImplTestExceptions(isapEngine); + ImplTestParameters(isapEngine, 16, 16, 16); + isapEngine = new IsapEngine(IsapEngine.IsapType.ISAP_K_128); + ImplTestExceptions(isapEngine); + ImplTestParameters(isapEngine, 16, 16, 16); + isapEngine = new IsapEngine(IsapEngine.IsapType.ISAP_A_128A); + ImplTestExceptions(isapEngine); + ImplTestParameters(isapEngine, 16, 16, 16); + isapEngine = new IsapEngine(IsapEngine.IsapType.ISAP_A_128); + ImplTestExceptions(isapEngine); + ImplTestParameters(isapEngine, 16, 16, 16); + ImplTestExceptions(new ISAPDigest(), 32); + ImplTestVectors("isapa128av20", IsapEngine.IsapType.ISAP_A_128A); + ImplTestVectors("isapa128v20", IsapEngine.IsapType.ISAP_A_128); + ImplTestVectors("isapk128av20", IsapEngine.IsapType.ISAP_K_128A); + ImplTestVectors("isapk128v20", IsapEngine.IsapType.ISAP_K_128); + ImplTestVectors(); } - - private void testVectors(string filename, IsapType isapType) + private void ImplTestVectors(string filename, IsapEngine.IsapType isapType) { - ISAPEngine isap = new ISAPEngine(isapType); - ICipherParameters param; + Random random = new Random(); + IsapEngine isapEngine = new IsapEngine(isapType); var buf = new Dictionary(); //TestSampler sampler = new TestSampler(); using (var src = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.isap." + filename + "_LWC_AEAD_KAT_128_128.txt"))) { - string line; - string[] data; - byte[] rv; Dictionary map = new Dictionary(); + string line; while ((line = src.ReadLine()) != null) { - data = line.Split(' '); + var data = line.Split(' '); if (data.Length == 1) { byte[] key = Hex.Decode(map["Key"]); @@ -70,37 +62,37 @@ namespace Org.BouncyCastle.Crypto.Tests 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); - isap.Init(true, param); - isap.ProcessAadBytes(ad, 0, ad.Length); - rv = new byte[isap.GetOutputSize(pt.Length)]; - int len = isap.ProcessBytes(pt, 0, pt.Length, rv, 0); - //byte[] mac = new byte[16]; - isap.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)); - isap.Reset(); - isap.Init(false, param); - //Decrypt - isap.ProcessAadBytes(ad, 0, ad.Length); - rv = new byte[pt.Length + 16]; - len = isap.ProcessBytes(ct, 0, ct.Length, rv, 0); - isap.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(); - isap.Reset(); + + var parameters = new ParametersWithIV(new KeyParameter(key), nonce); + + // Encrypt + { + isapEngine.Init(true, parameters); + + var rv = new byte[isapEngine.GetOutputSize(pt.Length)]; + random.NextBytes(rv); // should overwrite any existing data + + isapEngine.ProcessAadBytes(ad, 0, ad.Length); + int len = isapEngine.ProcessBytes(pt, 0, pt.Length, rv, 0); + len += isapEngine.DoFinal(rv, len); + + Assert.True(Arrays.AreEqual(rv, 0, len, ct, 0, ct.Length)); + } + + // Decrypt + { + isapEngine.Init(false, parameters); + + var rv = new byte[isapEngine.GetOutputSize(ct.Length)]; + random.NextBytes(rv); // should overwrite any existing data + + isapEngine.ProcessAadBytes(ad, 0, ad.Length); + int len = isapEngine.ProcessBytes(ct, 0, ct.Length, rv, 0); + len += isapEngine.DoFinal(rv, len); + + Assert.True(Arrays.AreEqual(rv, 0, len, pt, 0, pt.Length)); + } } else { @@ -112,12 +104,12 @@ namespace Org.BouncyCastle.Crypto.Tests { map[data[0].Trim()] = ""; } - } } } } - private void testVectors() + + private void ImplTestVectors() { ISAPDigest isap = new ISAPDigest(); var buf = new Dictionary(); @@ -138,7 +130,6 @@ namespace Org.BouncyCastle.Crypto.Tests byte[] hash = new byte[32]; isap.DoFinal(hash, 0); Assert.True(Arrays.AreEqual(hash, Hex.Decode(map["MD"]))); - //Console.WriteLine(map["Count"] + " pass"); map.Clear(); isap.Reset(); } @@ -158,18 +149,18 @@ namespace Org.BouncyCastle.Crypto.Tests } } - private void testExceptions(IAeadBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize) + private void ImplTestExceptions(IsapEngine isapEngine) { - ICipherParameters param; + int keysize = isapEngine.GetKeyBytesSize(), ivsize = isapEngine.GetIVBytesSize(); + int offset; 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[] m = Array.Empty(); + ICipherParameters param = new ParametersWithIV(new KeyParameter(k), iv); try { - aeadBlockCipher.ProcessBytes(m, 0, m.Length, c1, 0); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialized before ProcessBytes"); + isapEngine.ProcessBytes(m, 0, m.Length, null, 0); + Assert.Fail(isapEngine.AlgorithmName + " need to be initialized before ProcessBytes"); } catch (ArgumentException) { @@ -178,8 +169,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.ProcessByte((byte)0, c1, 0); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialized before ProcessByte"); + isapEngine.ProcessByte((byte)0, null, 0); + Assert.Fail(isapEngine.AlgorithmName + " need to be initialized before ProcessByte"); } catch (ArgumentException) { @@ -188,8 +179,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.Reset(); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialized before Reset"); + isapEngine.Reset(); + Assert.Fail(isapEngine.AlgorithmName + " need to be initialized before Reset"); } catch (ArgumentException) { @@ -198,8 +189,8 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.DoFinal(c1, m.Length); - Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialized before Dofinal"); + isapEngine.DoFinal(null, m.Length); + Assert.Fail(isapEngine.AlgorithmName + " need to be initialized before Dofinal"); } catch (ArgumentException) { @@ -208,13 +199,13 @@ namespace Org.BouncyCastle.Crypto.Tests try { - aeadBlockCipher.GetMac(); - aeadBlockCipher.GetOutputSize(0); - aeadBlockCipher.GetUpdateOutputSize(0); + isapEngine.GetMac(); + isapEngine.GetOutputSize(0); + isapEngine.GetUpdateOutputSize(0); } catch (ArgumentException) { - Assert.Fail(aeadBlockCipher.AlgorithmName + " functions can be called before initialization"); + Assert.Fail(isapEngine.AlgorithmName + " functions can be called before initialization"); } Random rand = new Random(); int randomNum; @@ -224,8 +215,8 @@ namespace Org.BouncyCastle.Crypto.Tests byte[] iv1 = new byte[randomNum]; try { - aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k1), iv)); - Assert.Fail(aeadBlockCipher.AlgorithmName + " k size does not match"); + isapEngine.Init(true, new ParametersWithIV(new KeyParameter(k1), iv)); + Assert.Fail(isapEngine.AlgorithmName + " k size does not match"); } catch (ArgumentException) { @@ -233,25 +224,25 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k), iv1)); - Assert.Fail(aeadBlockCipher.AlgorithmName + "iv size does not match"); + isapEngine.Init(true, new ParametersWithIV(new KeyParameter(k), iv1)); + Assert.Fail(isapEngine.AlgorithmName + "iv size does not match"); } catch (ArgumentException) { //expected } - - aeadBlockCipher.Init(true, param); + isapEngine.Init(true, param); + byte[] c1 = new byte[isapEngine.GetOutputSize(m.Length)]; try { - aeadBlockCipher.DoFinal(c1, m.Length); + isapEngine.DoFinal(c1, m.Length); } catch (Exception) { - Assert.Fail(aeadBlockCipher.AlgorithmName + " allows no input for AAD and plaintext"); + Assert.Fail(isapEngine.AlgorithmName + " allows no input for AAD and plaintext"); } - byte[] mac2 = aeadBlockCipher.GetMac(); + byte[] mac2 = isapEngine.GetMac(); if (mac2 == null) { Assert.Fail("mac should not be empty after Dofinal"); @@ -260,15 +251,15 @@ 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); + isapEngine.ProcessAadByte(0x00); + byte[] mac1 = new byte[isapEngine.GetOutputSize(0)]; + isapEngine.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); + isapEngine.Reset(); + isapEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 0); //try //{ // aeadBlockCipher.ProcessAadByte((byte)0); @@ -288,10 +279,10 @@ namespace Org.BouncyCastle.Crypto.Tests // //expected //} - aeadBlockCipher.Reset(); + isapEngine.Reset(); try { - aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 1, 1); + isapEngine.ProcessAadBytes(new byte[] { 0 }, 1, 1); Assert.Fail("input for ProcessAadBytes is too short"); } catch (DataLengthException) @@ -300,7 +291,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0); + isapEngine.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0); Assert.Fail("input for ProcessBytes is too short"); } catch (DataLengthException) @@ -309,7 +300,9 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.ProcessBytes(new byte[blocksize], 0, blocksize, new byte[blocksize], blocksize >> 1); + int inputSize = rand.Next(32, 64); + int outputSize = isapEngine.GetUpdateOutputSize(inputSize); + isapEngine.ProcessBytes(new byte[inputSize], 0, inputSize, new byte[outputSize], 1); Assert.Fail("output for ProcessBytes is too short"); } catch (OutputLengthException) @@ -318,7 +311,7 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - aeadBlockCipher.DoFinal(new byte[2], 2); + isapEngine.DoFinal(new byte[2], 2); Assert.Fail("output for dofinal is too short"); } catch (DataLengthException) @@ -326,59 +319,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[isapEngine.GetOutputSize(0)]; + mac2 = new byte[isapEngine.GetOutputSize(0)]; + isapEngine.Reset(); + isapEngine.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2); + isapEngine.DoFinal(mac1, 0); + isapEngine.Reset(); + isapEngine.ProcessAadByte((byte)0); + isapEngine.ProcessAadByte((byte)0); + isapEngine.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[isapEngine.GetOutputSize(10)]; + byte[] c3 = new byte[isapEngine.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); + isapEngine.Reset(); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(m2, 0, m2.Length, c2, 0); + isapEngine.DoFinal(c2, offset); + isapEngine.Reset(); + isapEngine.ProcessAadBytes(aad3, 1, aad2.Length); + offset = isapEngine.ProcessBytes(m3, 1, m2.Length, c3, 1); + isapEngine.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); + isapEngine.Reset(); + isapEngine.Init(false, param); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); + offset += isapEngine.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); + isapEngine.Reset(); + isapEngine.Init(false, param); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); try { - aeadBlockCipher.DoFinal(m4, offset); + offset += isapEngine.DoFinal(m4, offset); Assert.Fail("The decryption should fail"); } catch (ArgumentException) @@ -387,26 +379,27 @@ namespace Org.BouncyCastle.Crypto.Tests } c2[c2.Length - 1] ^= 1; - byte[] m7 = new byte[blocksize * 2]; + byte[] m7 = new byte[32 + rand.Next(32)]; rand.NextBytes(m7); - byte[] c7 = new byte[aeadBlockCipher.GetOutputSize(m7.Length)]; + + isapEngine.Init(true, param); + byte[] c7 = new byte[isapEngine.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); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(m7, 0, m7.Length, c7, 0); + offset += isapEngine.DoFinal(c7, offset); + isapEngine.Reset(); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(m7, 0, m7.Length / 2, c8, 0); + offset += isapEngine.ProcessBytes(m7, m7.Length / 2, m7.Length - m7.Length / 2, c8, offset); + offset += isapEngine.DoFinal(c8, offset); + isapEngine.Reset(); + int split = rand.Next(1, m7.Length); + isapEngine.ProcessAadBytes(aad2, 0, aad2.Length); + offset = isapEngine.ProcessBytes(m7, 0, split, c9, 0); + offset += isapEngine.ProcessBytes(m7, split, m7.Length - split, c9, offset); + isapEngine.DoFinal(c9, offset); if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9)) { Assert.Fail("Splitting input of plaintext should output the same ciphertext"); @@ -418,10 +411,10 @@ namespace Org.BouncyCastle.Crypto.Tests Span c4_2 = new byte[c2.Length]; ReadOnlySpan m5 = new ReadOnlySpan(m2); ReadOnlySpan aad4 = new ReadOnlySpan(aad2); - aeadBlockCipher.Init(true, param); - aeadBlockCipher.ProcessAadBytes(aad4); - offset = aeadBlockCipher.ProcessBytes(m5, c4_1); - aeadBlockCipher.DoFinal(c4_2); + isapEngine.Init(true, param); + isapEngine.ProcessAadBytes(aad4); + offset = isapEngine.ProcessBytes(m5, c4_1); + isapEngine.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); @@ -429,14 +422,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); + isapEngine.Reset(); + isapEngine.Init(false, param); Span m6_1 = new byte[m2.Length]; Span m6_2 = new byte[m2.Length]; ReadOnlySpan c6 = new ReadOnlySpan(c2); - aeadBlockCipher.ProcessAadBytes(aad4); - offset = aeadBlockCipher.ProcessBytes(c6, m6_1); - aeadBlockCipher.DoFinal(m6_2); + isapEngine.ProcessAadBytes(aad4); + offset = isapEngine.ProcessBytes(c6, m6_1); + isapEngine.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); @@ -447,28 +440,25 @@ namespace Org.BouncyCastle.Crypto.Tests #endif } - private void testParameters(ISAPEngine ascon, int keySize, int ivSize, int macSize, int blockSize) + private void ImplTestParameters(IsapEngine isapEngine, 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, isapEngine.GetKeyBytesSize(), + "key bytes of " + isapEngine.AlgorithmName + " is not correct"); + Assert.AreEqual(ivSize, isapEngine.GetIVBytesSize(), + "iv bytes of " + isapEngine.AlgorithmName + " is not correct"); + + var parameters = new ParametersWithIV(new KeyParameter(new byte[keySize]), new byte[ivSize]); + + isapEngine.Init(true, parameters); + Assert.AreEqual(macSize, isapEngine.GetOutputSize(0), + "GetOutputSize of " + isapEngine.AlgorithmName + " is incorrect for encryption"); + + isapEngine.Init(false, parameters); + Assert.AreEqual(0, isapEngine.GetOutputSize(macSize), + "GetOutputSize of " + isapEngine.AlgorithmName + " is incorrect for decryption"); } - private void testExceptions(IDigest digest, int digestsize) + private void ImplTestExceptions(IDigest digest, int digestsize) { if (digest.GetDigestSize() != digestsize) { @@ -493,7 +483,6 @@ namespace Org.BouncyCastle.Crypto.Tests { //expected } - //Console.WriteLine(digest.AlgorithmName + " test Exceptions pass"); } } } \ No newline at end of file -- cgit 1.4.1