Andromeda: The Encryption Object (White Paper, Part 2)

The Yull class handles IO, setup, and managing the read and writing of data and submitting that data to the encryption object. The encryption object is the Andromeda class.

Before encryption (or decryption) begins in the Andromeda class, Yull instantiates the Andromeda object and instructs it to setup the jump table. Andromeda has 60 encryption procedures. Andromeda constructs an array of 256 delegates. (A C# delegate is like a C++ function pointer.) Its construction might look like this:

	
for (int i = 0; i <= 255; i++) 
	{
		if (i < 7) encrypt[i] = new Encrypt(Encrypt1); 
		if (7 <= i && i < 14) encrypt[i] = new Encrypt(Encrypt33);  
		if (14 <= i && i < 20) encrypt[i] = new Encrypt(Encrypt26); 
		if (20 <= i && i < 25) encrypt[i] = new Encrypt(Encrypt21);  
		if (25 <= i && i < 30) encrypt[i] = new Encrypt(Encrypt60);  

and so forth. The decryption array is set up in a similar fashion.

All the Encrypt functions are prototyped liked this:

function (ref byte[] inB, int i) 


After the array is set up, the code starts:


	for (i = 0; i < Key.Length; i++)
	{ 
		encrypt[Key[i]](ref inB, i);
	}


The key length is the same as the read buffer. If the key is shorter than the read buffer, it is lengthened to the size of the read buffer; if it is longer, it is truncated. When this for loop finishes, Andromeda returns control back to the Yull object, which then continues the rounds loop.


	for (int i = 0; i < iRounds; i++) //iRounds is different with each read 
	{
		androm.Key = new byte[iKeyLength]; 
		//androm is the Andromeda object 
		System.Buffer.BlockCopy(Keys[outerRead][i], 0, androm.Key, 0, iKeyLength); 
		androm.EncryptStart();
	}


And when the rounds loop ends Yull cycles back to read the next part of the file and process starts again.
Before starting the encryption loop, Yull create a multidimensional array of keys, sufficient for the entire encryption process:


	sKeys = new byte[Rounds.Length][][];   
	int j = 0; 
	for (int i = 0; i < sKeys.Length; i++)   
	{ 
		sKeys[i] = new byte[Rounds[i]][];  
		for (j = 0; j < Rounds[i]; j++)
		{ 
			encryptKey(ref sKey); 
			sKeys[i][j] = new byte[iKeyLength]; 
			System.Buffer.BlockCopy(sKey, 0, sKeys[i][j], 0,
			iKeyLength); 
		} 
	}

Getting back to the encryption loop, when it completes, the data is written out and the process continues until the last block is read by Yull.

Nearly all (or all) encryption relies heavily on a few instructions on the chip: XOR, NOT, ROL and ROR. These are important because they are reversible. If you XOR (eXclusive OR). a value with another value and then repeat it you will get the original value back:

NOT (NOT 10) = 10

RoR and RoR (bitwise rotation) are a bit more subtle and are related to how data is represented in bytes. RoR and RoL mean Rotate Right and Rotate Left. (image above and below from Wikipedia)

If you call RoL or RoL eight times on the same value you will get that value back. In any case, this is not a primer on encryption. Yull/Andromeda uses these but also extends them in a unique way:


void rotateArrayLeftAsBits(ref byte[] inB)
{ 
    if (inB.Length == 0) return; 
	try

      byte[] outB = new byte[inB.Length]; 
	  byte[] bits = new byte[inB.Length]; 
      int i; 
      for (i = 0; i < inB.Length; i++) 
      { 
          if ((inB[i] & 0x80) == 0x80) bits[i] = 1;
          outB[i] = (byte)(inB[i] << 1); 
      } 
      for (i = 1; i < inB.Length; i++) 
	      outB[i - 1] |= bits[i]; outB[inB.Length - 1] |= bits[0]; 
      System.Buffer.BlockCopy(outB, 0, inB, 0, inB.Length); 
}

While the ROL or ROR functions are bound by the byte boundaries, rotateArrayLeftAsBits (and rotateArrayRightAsBits) do the same but on an array of bytes.

As mentioned above, Andromeda uses 60 encryption functions. Some are fairly atomic:


void Decrypt46(ref byte[] inB, int k)
{
   negateArray(ref inB);
}

void Encrypt27(ref byte[] inB, int k)
{
   for (int j = 0; j < inB.Length; j++) 
   { 
      inB[j] = (byte)(inB[j] ^ Key[j]); 
   } 
}

Some are more complex, building on other functions:

void Encrypt2(ref byte[] inB, int i)
{
   inB[i] = (byte)((inB[i] << 7) | inB[i] >> 1); 
   rotateArrayLeftAsBits(ref inB);
   inB[i] = (byte)(inB[i] ^ Key[i]);
} 

Other routines extract data from the read buffer (perhaps) in various ways. Some create a byte array based on the values from the Key, others from a "matrix" like extraction:

Figure 6 A visual representation of a matrix

Here the black rectangle represents a possible "matrix," that is, extracted data, which is clearly not completely sequential. It will start at some offset into the read buffer, run so long, then go to the next "line" (not a real line but some designated amount; this graphic only helps visualize the process.)

Some byte extractions take data from the inB parameter in a more disjointed fashion:

Figure 7 Non-sequential data extracted

The extracted data is submitted to the range of encryption functions since the array holding it is just another buffer, like any other.

Since all the encrypt functions are prototyped the same with the first parameter being a reference to a byte array they do not care if the byte array is the entire read array or some portion of it. The encryption functions are agnostic as to the source of the data, they can operate on these subarrays as if there were the entire read buffer, performing an encryption function (or several) and then returning the data back to the array the inBufer parameter points to.

Some operate on parts of bytes:

	byte[] getBits(ref byte[] inB, byte Mask)
	{
		byte[] bitArray = new byte[inB.Length]; 
		for (int i = 0; i < inB.Length; i++) 
			bitArray[i] = (byte)(inB[i] & Mask); 
		return bitArray; 
	}

This function extracts masked data from the input buffer (which could be the entire read buffer or another subset of it) and returns an array the length of inB which has been anded with the Mask value. The calling routine will submit this array to some of the other encryption functions. For instance this one:


void Encrypt53(ref byte[] inB, int k) { byte[] values = getBits(ref inB, (byte)k); encrypt[k](ref values, k); returnBits(ref values, ref inB, (byte)k); }

Which uses the int value k, which is probably (though not necessarily) the value Key[k].

Then it calls the masked extraction function getBits( ) and then passes the returned array to whatever function is at encrypt[k], the kth position in the array of delegates. When that function returns, the encrypted array is returned to the original array in the parameter.

The White Paper continues with the White Paper - Andromeda Code Section)



Everything on this website is Copyright © 2015 Ronald Gans / Yull Encryption Company, LLC.
All Rights Reserved.