3
\$\begingroup\$

In a part of the project, I had to implement a solution for encapsulation of byte arrays. We do use ssh and secure ports for socket connection but I was in need of an extra layer of protection against man-in-the-middle attacks or a mechanism to drop packages rather fast before reading all the package content. (In case somebody sends an array with 10k elements, continuously to make denial of service). So I created this library which might help me in my case.

Project repo for further details.

CapsuleConstant Class:

using System;
using System.Collections.Generic;
using System.Security.Cryptography;


namespace LiteByte{

     /// <summary>
     /// Class to generate instances for capsulation constants.
     /// </summary>
    public class CapsuleConstant : IComparable<CapsuleConstant>
    {
        private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
        private byte val;
        private int position;//0 is first 
        private bool head;
        /// <summary>
        /// Value property of an CapsuleConstant instance. Byte value for constant.
        /// </summary>
        public byte Val { get => val; set => val = value; }

        /// <summary>
        /// Position of the constant related to the start position parameter(head). Ex: if position is:0 and head=true, this constant will be the first element in the capsule.
        /// </summary>
        public int Position { get => position; set => position = value; }

        /// <summary>
        /// Property to indicate counting for position from start(head) or counting from the end(tail) of the capsule. Ex:Head:false, position:0 will be the last element of the capsule.
        /// </summary>
        public bool Head { get => head; set => head = value; }

        /// <summary>
        /// Base constructor of CapsuleConstant class to initiate instances.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="position"></param>
        /// <param name="head"></param>
        public CapsuleConstant(byte value, int position, bool head)
        {
            this.val = value;
            if (position < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            this.position = position;
            this.head = head;
        }

        private CapsuleConstant(byte value,int headCounter, int tailCounter,bool head)
        {
            this.val = value;
            if (head)
            {
                this.position = headCounter;
            }
            else
            {
                this.position = tailCounter;
            }
            this.head = head;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="amount">Values which vary from 1 to int.max()</param>
        /// <returns></returns>
        public static Stack<CapsuleConstant> GenerateCapsulationConstants(int amount)
        {

            try
            {
                uint unsignedAmount = Convert.ToUInt32(amount);
            }
            catch (Exception)
            {
                throw new ArgumentOutOfRangeException();
            }
            Stack<CapsuleConstant> capsuleConstants = new Stack<CapsuleConstant>();

            byte[] randomHolder = new byte[1];



            Random rnd = new Random();
            int headCounter = -1;
            int tailCounter = -1;
            bool head;

            for (int i = 0; i < amount; i++)
            {//TODO stack to string conversion for console writeline, string
                rngCsp.GetBytes(randomHolder);
                if (rnd.NextDouble() >= 0.5)
                {
                    head = true;
                    headCounter++;

                }
                else
                {
                    head = false;
                    tailCounter++;
                    //TODO method for searching capsulation stack for certain stuff
                }
                capsuleConstants.Push(new CapsuleConstant(randomHolder[0], headCounter, tailCounter, head));

            }

            return capsuleConstants;
        }

        /// <summary>
        /// Implementation of IEnumarable 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(CapsuleConstant other)
        {
            return this.position.CompareTo(other.position);

        }
    }
}

Encapsulation method:

public byte[] ConvertToSyntax(byte[] infactData)
{
    if (infactData == null)
    {
        throw new ArgumentNullException("infactData", "Byte array parameter infactData cannot be null");
    }
    Stack<CapsuleConstant> capsuleConstantsClone = StackClone<CapsuleConstant>(capsulationConstants);
    int capsuleSize = infactData.Length + capsuleConstantsClone.Count;
    byte[] capsule = new byte[capsuleSize];



    while (capsuleConstantsClone.Count != 0)
    {
        CapsuleConstant constant = capsuleConstantsClone.Pop();
        if (constant.Head)
        {
            capsule[constant.Position] = constant.Val;
        }
        else
        {
            capsule[(capsule.Length - 1) - constant.Position] = constant.Val;
        }
    }


    CapsuleConstant maxHead = (from x in capsulationConstants where x.Head == true select x).Max();
    Array.Copy(infactData, 0, capsule, maxHead.Position + 1, infactData.Length);

    return capsule;
}

Example usage as follows:

Stack<CapsuleConstant> constants=CapsuleConstant.GenerateCapsulationConstants(100);
LiteByteCapsule lite=new LiteByteCapsule(constants);
byte[] innerPackage={99,255,0,35,42};
byte[] capsule=lite.ConvertToSyntax(innerPackage);
byte[] decapsulated=lite.CheckSyntax(capsule);

Unit Test of CheckSyntax (Using XUnit and no mock preset). It takes the most time among other tests. (~80 ms, other tests take about ~1ms. included in repository)

[Fact]
public void CheckSyntax_Base()
{
    int amount = 100;
    LiteByteCapsule lite = new LiteByteCapsule(CapsuleConstant.GenerateCapsulationConstants(amount));
    byte[] package = { 99, 0, 255, 12, 33, 54, 123 };
    byte[] capsule=lite.ConvertToSyntax(package);
    byte[] inner=lite.CheckSyntax(capsule);
    Assert.NotNull(inner);
    Assert.NotEmpty(inner);
    Assert.Equal(inner.Length, package.Length);
    Assert.Equal(package, inner);

}

Current unit tests report no error. But i want to make it better. Because in case of encapsulation and de-encapsulation of a byte array for 1000 times it takes ~80ms. What can i improve, or should i change the whole structure and approach to the problem ?

Any idea and feedback helps. Thanks in advance.

\$\endgroup\$
  • 2
    \$\begingroup\$ Code behind a link can be useful as additional context. But you should at least include the code you want get reviewed in the question. \$\endgroup\$ – dfhwze Jul 24 at 7:24
  • 1
    \$\begingroup\$ Understood, thanks for the info. Editing now. \$\endgroup\$ – Skywarth Jul 24 at 7:24
  • 1
    \$\begingroup\$ Thanks for the update. You also mention you have unit tests. It would be most helpful if you would include them, so reviewers get more context to help you out. And try also to include source code of CapsuleConstant :) \$\endgroup\$ – dfhwze Jul 24 at 7:31
  • 2
    \$\begingroup\$ My bad, thought that it might be bloating to include all that code in snippet. Updated. \$\endgroup\$ – Skywarth Jul 24 at 7:50
  • 2
    \$\begingroup\$ I don't think your post and github readme really explain what your code actually does, but apparently it's prepending and appending some random bytes to a message. How does the receiving end know which bytes to expect? And how does this protect against a MitM attack? \$\endgroup\$ – Pieter Witvoet Jul 24 at 11:16
2
\$\begingroup\$

Specification

You have 2 requirements stated below. I see no reason why to re-invent-the-wheel here. Both requirements can be met with using common practices.

..or a mechanism to drop packages rather fast before reading all the package content. (In case somebody sends an array with 10k elements, continuously to make denial of service).

This should be the top layer that uses a fast algorithm to detect message tampering. I would use a CRC or any other well-known checksum. This layer acts fast and would already filter out a lot of flooding.

[message] = [content][checksum]

I was in need of an extra layer of protection against man-in-the-middle attacks..

An additional layer is needed that does a thorough security check. Here you could verify confidentiality, authentication and addition message tampering. Multiple strategies and algorithms are available; This post talks about strategies (sign vs encrypt).

[content] = [digest][signature][checksum] // one possible strategy

C# Conventions

  • Use properties with backing fields: public byte Val { get => val; set => val = value; } -> public byte Value { get; set; }
  • Don't use abbreviations for member names: Val -> Value
  • Your int to uint conversion seems convoluted; which range of values would you allow, is unchecked conversion an option for you, why not use uint to begin with?
  • Prefer using var: var capsuleConstants = new Stack<CapsuleConstant>();
\$\endgroup\$
  • \$\begingroup\$ Neat and good points, i like it. I'll certainly do updates according to your evaluations. But i have a concern: What is the proper way to implement "calculation of byte array" ? I looked it up but there is various solutions for that and not sure which is the best to calculate CRC. \$\endgroup\$ – Skywarth Jul 25 at 10:32
  • 1
    \$\begingroup\$ For the first layer, that should be fast, I suggest CRC32. For the deeper layer you will find suggestions in the comments to your question. \$\endgroup\$ – dfhwze Jul 25 at 10:35
  • 1
    \$\begingroup\$ I see. Thanks, appreciated. \$\endgroup\$ – Skywarth Jul 25 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.