Designing Custom Cryptographic Systems: Part 3

This post has been long in coming. For those who haven't read the previous posts in this series, below are links to bring you up to speed.

In Part 1, we covered the basic mathematical information and technical information required to understand and begin to implement your own cryptography. Part 1: HERE

In Part 2, we set up a Cryptographically Secure Pseudo-Random Number Generator to be used by our cryptographic algorithms. Part 2: HERE

If you're stuck, the previous posts may help with bringing you up to speed. Now, let's add in the hashing algorithm!

Hashing? What's that?

A Hashing Algorithm is this carefully crafted program that takes a string as an input, performs a bunch of cryptographic functions on it, and spits out a fixed-length string that appears to be random. However, if you compute the hash again with the same string, you will achieve the same output.

A sample hashing function could be a simple XOR operator. You take random bytes to produce “entropy” for the hashing function, saving the information somewhere. Then you could just XOR the string with the saved random bytes, chopping the hash off at a fixed length. This is known to be highly insecure as performing an XOR against itself is effectively an inverse operation and will undo the work of creating a hash.

Since we are simply using a hashing function on our CSPRNG to produce more useful randomness (effectively increasing the efficiency), we can afford to use a hashing algorithm that has been compromised as there's no user data flowing through it.

Wait, Compromised?

Yes. Remember that we're simply using SHA512 to produce more bytes of pseudorandom information that our cryptographic system can utilize, by feeding it small amounts of pseudorandom characters (characters are actually integers from 0-255 in C).

Due to this, performance matters more than security. SHA512 isn't the most performant, but it's high bit modulus means that the algorithm will have some level of security as well. However, the REAL security will come from our encrypted asymmetric keys. Some argue that this is entirely unnecessary and in the final version of this program, we may even eliminate this bit of code if deemed not necessary.

The Implementation

For our hashing algorithm, we are going to borrow the Free Software Foundation's implementation of SHA512 and take a look at a sample implementation by ggaarder on GitHub, located here.

We notice that SHA512 uses 64 bit integers, and for compatibility, a custom headerfile was included to allow other architectures to process 64 bit numbers as if they were uint64_t (Thanks, Paul Eggert for your contribution to Open Source Software!).

Along with these two things, sample usage of the program is provided:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include "sha512.h"

#define F "sha512.c"
#define F1 "sha512.cp"

void psum(char *out)
{
    char *p;
    for (p = out; p < out+64; p += 8) {
        // 8 byte is 64 bit
        printf("%lx", *(uint64_t*)p);
    }
}

int main(void)
{
    FILE *fp = fopen(F, "rb");
    char out[64];
    sha512_stream(fp, out);
    psum(out);
    putchar('\n');
    fp = fopen(F1, "rb");
    sha512_stream(fp, out);
    psum(out);
}

As we can see, this code involved the opening of the file SHA512.c in binary read-only mode, computing a sha512 stream using the contents of the file pointer and assigning it to a char array of size 64. the psum() function allows the printing of the array to standard output. So if we integrated this with our CSPRNG (so we can produce pseudorandom characters), we would need to make our CSPRNG produce pseudorandom strings and then use those for SHA512.

We would need to do something like this:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>

#include "duthomhas/csprng.h"

#define MESSAGE "message.txt"

extern int csprng_get(CSPRNG, void* dest, unsigned long long size);
extern CSPRNG csprng_destroy(CSPRNG);

// Our implementation of the CSPRNG
void * randstuff(void * x) {
  CSPRNG rng = csprng_create( rng ); // create CSPRNG
  if (!rng) { 
    //if the CSPRNG fails to load
    fprintf( stderr, "%s\n", "No CSPRNG! Crap." );
    exit(1); //crash and return an error
  }

  csprng_get(rng, &x, sizeof(x)); // use CSPRNG
  rng = csprng_destroy(rng); // destroy the CSPRNG

  return x;
}


// Print out Hash
void psum(char *out) {
    char *p;
    for (p = out; p < out+64; p += 8) {
        // 8 byte is 64 bit
        printf("%lx", *(uint64_t*)p);
    }
}

// Main function
int main(void) {
  char rand[64]; //To be filled with random garbage
  char hash[64]; //To hold the hash
  int errchk;
  FILE * fp;

  // Wipe the values in allocated memory before assignment.
  memset(rand, '\0', sizeof(rand));
  memset(hash, '\0', sizeof(hash));

  strcpy(rand, randstuff(rand)); // assign randstuff to rand.

  /* CREATE TEMPFILE FOR SHA512 */

  fp = fopen(MESSAGE, "w+"); // open message.txt for writing
  for (int i=0; i<(sizeof(rand)/sizeof(char)); i++) { 
    //loop through each item in rand, creating a tempfile
    //with pseudorandom content.
    fprintf(fp, "%c", rand[i]);
  }
  fclose(fp);

  /* CALCULATE SHA512 HASH */

  fp = fopen(MESSAGE, "rb"); // Open file called message.txt 
  sha512_stream(fp, out);
  psum(out);

  fclose(fp);

  errchk = remove(MESSAGE);

  if(errchk != 0) {
    printf("Error deleting entropy file. CSPRNG may be compromised.\n");
  }

  return 0;
}

Note that this code doesn't have error checking, and it's just the raw algorithm. We will fix that in a coming post.

To explain what was added, we utilized the same CSPRNG code from before and assigned it to an array called rand. Since the SHA512 calculation requires a FILE data type (an Input/Output stream), we need to write the contents of rand to a temporary text file character by character. In doing so, we can calculate the SHA512 hash of the randomness, print it to standard output courtesy of the psum() function, and then remove the temporary file by calling the remove() funtion.

In the future, it might be smart to create a second temporary file to write the SHA512 hash to and delete the first, but time will tell if we need to make this change.

With this code tested and working, we can proceed onward to the design and implementation of our Elliptic Curve Diffie Hellman Algorithm to allow for asymmetric encryption!

Liked This Content? Check Out Our Discord Community and Become an email subscriber!