Skip to content
ikizir edited this page Jan 30, 2016 · 10 revisions

Creating the encryption key

int xorGetKey(uint8_t NumJumps, uint32_t BodyLen, uint8_t *KeyBuf)
{
  // Creates XOR key
  // The first byte will be equal to NumJumps
  // Following 2 bytes is key body length
  // Following 4 bytes are random salt data
  // Following BodyLen bytes are random numbers obtained from buffered /dev/urandom data. BODYLEN MUST BE A POWER OF 2!
  // Result buffer must be enough to store key!! No error checking is done!!!
  // Return negative on error; zero if successfull

  if (((BodyLen-1) & BodyLen) != 0)
    return -1; // Key body length must be a power of 2!
  if (NumJumps < 2)
    return -2; // Number of jumps must be greater than or equal to 2
  if (NumJumps > MAX_NUM_JUMPS)
    return -3;
  if (BodyLen < 16)
    return -4;
  if (BodyLen > MAX_BODY_SIZE)
    return -5;
  KeyBuf[SP_NUM_JUMPS] = (uint8_t)(NumJumps&255);
  KeyBuf[SP_BODY_LEN] = (uint8_t)((BodyLen % 256) & 0xff);
  KeyBuf[SP_BODY_LEN+1] = (uint8_t)((BodyLen / 256) & 0xff);
  GetRandomNumbers(SALT_SIZE + BodyLen, KeyBuf + SP_SALT_DATA); // Fill 8 bytes salt + key body data with random numbers
  return 0;
}
  • NumJumps is the number of jumps(or rounds) to encrypt or decrypt data.

The actual maximum value is 4(But if you find it weak, We(or you) can increase that limit. We just have to write hand optimized functions). This parameter directly affects speed and strength of the algorithm. If you choose higher values, the encryption will be more secure but slower. For a busy site with no critical data, 2 jumps is ideal. We suggest using minimum 3 jumps for critical data.

  • BodyLen is the number of bytes in the key body. It must be a power of 2 (e.g. 64,128,256 ...).

We set current key body "hard" maximum limit to 256 due to our algorithm design. If you want to create derivatives to use higher key body lengths, you must make some minor modifications on encryptor and decryptor function to assure right key coverage. It has negligible impact on the speed of the algorithm, but a direct impact on strength: Higher values you choose, higher security you get. Choose large numbers especially if you are going to encrypt large files.

  • KeyBuf is pointer to an "already allocated" buffer to hold the entire key.

To compute the size of the resulting key, you may use xorComputeKeyBufLen macro.

Key creation example

Suppose that we want to create a key with 2 jumps and a body size of 128 bytes, which are fairly enough for most cases. The code will be:

#define BODY_LEN 128
#define NUM_JUMPS 2

uint32_t KeyCheckSum;
unsigned RawKeyLen = xorComputeKeyBufLen(BODY_LEN);
uint8_t *KeyBuf = (uint8_t *)malloc(RawKeyLen);
Err = xorGetKey(NumJumps, BodyLen, KeyBuf);
if (Err != 0)
{
  printf("Couldn't create the key. Error: %d\n",Err);
  exit(-1);
}
KeyCheckSum = xorComputeKeyCheckSum(KeyBuf);

KeyCheckSum is the 32 bit CRC checksum of the key. Every time you create a key, you must also compute its checksum. In order to use the key for encryption, or decryption, we must give that checksum as a parameter. Now, we have the key and the checksum. We want to encrypt our data.

Every key is created with it's own Salt(or iv) value. This Salt must "only" be used to encrypt salt values. For each encryption. When you want to encrypt a data, you must first create an 8 random bytes as Salt value of that encryption. Let's call it Nonce. You must encrypt your data with Nonce; you must encrypt Nonce with key's original salt and transmit ciphertext and salt to receiver. It is extremely crucial to transmit Nonce secretly. Or, your encryption is nearly useless. This is the unique weakness of algorithm I've detected so far: If the attacker intercepts both the plaintext AND the actual salt(nonce) used for encryption, algorithm will be vulnerable. If the attacker intercepts just the plaintext, but not the nonce; it is not an issue. That's why, we never use raw encryption or decryption functions directly. Instead, we use a special Packet format to deal with the details of salt creation, padding and CRC verification.

Encryption

NOTE: We have generic functions to encrypt or decrypt data, but, we don't suggest using them in real life. Instead, use, void CreateHohhaCommunicationPacketx family functions.

Function signatures:

void CreateHohhaCommunicationPacket2(
  uint8_t *K, 
  uint32_t KeyCheckSum, 
  size_t InDataLen, 
  uint8_t *InBuf, 
  uint32_t DataAlignment, 
  uint8_t *OutBuf);
uint8_t *CreateHohhaCommunicationPacket(
  uint8_t *K, 
  uint32_t KeyCheckSum, 
  size_t InDataLen, 
  uint8_t *InBuf, 
  uint32_t DataAlignment);
  • K is the key buffer we created earlier as shown in the example above.

  • KeyCheckSum is the 32 bit CRC we obtained via xorComputeKeyCheckSum macro as shown in the example InOutDataLen is the number of bytes to be encrypted.

  • InDataLen is the number of bytes to be encrypted

  • InBuf is the input buffer

  • DataAlignment is the alignment of the packet. Valid values are 8,16,32 or 64

  • OutBuf is the already allocated buffer for CreateHohhaCommunicationPacket2 function. CreateHohhaCommunicationPacket allocates and returns OutBuf itself.

Decryption

Function signatures:

uint8_t *DecryptCommPacket(
  uint8_t *K, 
  uint32_t KeyCheckSum, 
  size_t TotalPacketLen, 
  uint8_t *InOutBuf, 
  ssize_t *PlainTextLen);
  • K is the key buffer we created earlier as shown in the example above.

  • KeyCheckSum is the 32 bit CRC we obtained via xorComputeKeyCheckSum macro as shown in the example.

  • TotalPacketLen is the number of bytes in InOutBuf.

  • InOutBuf is the input and also output buffer.

  • PlainTextLen on return, this variable will be set to plaintext length.

On return:

The function will return a pointer to plaintext. or NULL if the packet is corrupted(In this case, *PlainText will set to a negative value indicating the reason of failure)

Full usage example

Here is the code snippet taken from benchmark program to encrypt, decrypt and verify data:

void CommPacketTest(unsigned NumJumps, unsigned BodyLen)
{
  unsigned long long int DLen;
  unsigned RawKeyLen = xorComputeKeyBufLen(BodyLen);
  uint8_t *KeyBuf = (uint8_t *)malloc(RawKeyLen); assert (KeyBuf);
  uint8_t Data[2048],Data2[2048];
  char *Base64EncodedKeyStr;
  uint32_t KeyCheckSum;
  uint8_t SaltData[SALT_SIZE];
  
  GetRandomNumbers(SALT_SIZE, SaltData);  
  printf("----------- COMM PACKETS TEST(%u Jumps) --------------\n",NumJumps);
  int Err = xorGetKey(NumJumps, BodyLen, KeyBuf);
  if (Err != 0)
  {
    printf("Couldn't create the key. Error: %d\n",Err);
    exit(-1);
  }
  KeyCheckSum = xorComputeKeyCheckSum(KeyBuf);
  Base64EncodedKeyStr = Base64Encode((const char *)KeyBuf, RawKeyLen);
  printf("Base64 encoded key: %s\n", Base64EncodedKeyStr);

  memset(&Data, 0, sizeof(Data));
  memset(&Data2, 0, sizeof(Data2));
  DLen = TESTSTR1_LEN; 
  memcpy(Data, TESTSTR1, DLen);
  
  
  #define PACK_ALIGNMENT 16
  uint32_t CommPacketTotalSize = HOHHA_TOTAL_COMM_PACKET_SIZE(DLen,PACK_ALIGNMENT);
  uint8_t *CommPacket = malloc(CommPacketTotalSize);
  CreateHohhaCommunicationPacket2(KeyBuf, KeyCheckSum, DLen, Data, PACK_ALIGNMENT, CommPacket);
  
  char *Base64EncodedCommPacket = Base64Encode((const char *)CommPacket, CommPacketTotalSize);
  printf("Encrypted communication packet: %s\n", Base64EncodedCommPacket);
  free(Base64EncodedCommPacket);
  
  // Now, let's decrypt it
  ssize_t DpRes;
  uint8_t *PPText = DecryptCommPacket(KeyBuf, KeyCheckSum, CommPacketTotalSize, CommPacket, &DpRes);
  if (DpRes < 0 || DpRes != DLen)
  {
    printf("DecryptCommPacket error: %lld. DLen: %lld\n", (long long int)DpRes, (long long int)DLen);
    exit(-1);
  }
//  THohhaPacketHeader *PacketHeader = (THohhaPacketHeader *)CommPacket;
  if (memcmp((char *)PPText, TESTSTR1, DLen) == 0)
  {
    printf("String: %.*s ... Hohha Comm Packet Test result: SUCCESSFUL!!!!\n----------------------------------------\n", (int)(DpRes), PPText);
  }
  else {
    printf("String: %.*s ... Hohha Comm Packet Test result: FAILED!!!!\n----------------------------------------\n", (int)(DpRes), PPText);
    exit(-1);
  }
  free(CommPacket);
  
  CommPacketTotalSize = HOHHA_TOTAL_COMM_PACKET_SIZE_WITHOUT_ENCRYPTION(DLen);
  CommPacket = calloc(1, CommPacketTotalSize);
  CreateHohhaCommunicationPacket2Plaintext(DLen, Data, CommPacket);
  
  Base64EncodedCommPacket = Base64Encode((const char *)CommPacket, CommPacketTotalSize);
  printf("Plaintext communication packet in base64: %s\n", Base64EncodedCommPacket);
  free(Base64EncodedCommPacket);
  
  // Now, let's decrypt it
  PPText = DecryptCommPacket(KeyBuf, KeyCheckSum, CommPacketTotalSize, CommPacket, &DpRes);
  if (DpRes < 0 || DpRes != DLen)
  {
    printf("DecryptCommPacket error: %lld. DLen: %lld\n", (long long int)DpRes, (long long int)DLen);
    exit(-1);
  }
  //PacketHeader = (THohhaPacketHeader *)CommPacket;
  if (memcmp((char *)PPText, TESTSTR1, DLen) == 0)
  {
    printf("String: %.*s ... Hohha plaintext Comm Packet Test result: SUCCESSFUL!!!!\n----------------------------------------\n", (int)(DpRes), PPText);
  }
  else {
    printf("String: %.*s ... Hohha plaintext Comm Packet Test result: FAILED!!!!\n----------------------------------------\n", (int)(DpRes), PPText);
    exit(-1);
  }
  free(CommPacket);
  
  
  //exit(-1);
}

Notes

In real life scenarios, it is crucial to transmit Salt data secretly. In order to realize this, you must create a random salt; use that random salt to encrypt the plaintext and you must encrypt the salt with the key, using key's original salt data. The receiver will first decrypt the salt data using key's original salt data in order to obtain actual salt data, then will decrypt the ciphertext using this actual salt data. Consequently, the usage of communication packets is a must. Use, CreateHohhaCommunicationPacketX and DecryptCommPacket functions. Do not try to use raw encryption functions.

Clone this wiki locally