aboutsummaryrefslogblamecommitdiff
path: root/src/cryptech_novena_i2c_trng.c
blob: 599c0dbc38e855ac2400229232bec5ba7ff66d92 (plain) (tree)
1
2
3
4
5
6
7
8
   
                             




                                                                     
                                                                      



































































































                                                                        

                                 


                                                               
                                                            

   


                                    





















































































































































































































































                                                                                                               



                                                                      

   

                                 









                                                     



                                                                  


               

                                                             


                       

                                                                  

               

                                 
                            

                                                                       





























































































                                                                                  
/* 
 * cryptech_novena_i2c_trng.c
 * ------------------------------
 *
 * This is an early prototype Hardware Adaption Layer (HAL) for using
 * Cryptlib with the Cryptech project's FGPA cores over an I2C bus on
 * the Novena PVT1 development board using the "coretest" byte stream
 * protocol.  This is compatible with the test/novena_trng FPGA build.
 *
 * The communication channel used here is not suitable for production
 * use, this is just a prototype.
 * 
 * Authors: Joachim Strömbergson, Paul Selkirk, Rob Austein
 * Copyright (c) 2014, SUNET
 * 
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The HAL framework is taken from the Cryptlib hw_dummy.c template,
 * and is Copyright 1998-2009 by Peter Gutmann.
 */

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#if defined( INC_ALL )
  #include "crypt.h"
  #include "context.h"
  #include "hardware.h"
#else
  #include "crypt.h"
  #include "context/context.h"
  #include "device/hardware.h"
#endif /* Compiler-specific includes */

/*
 * I2C_SLAVE comes from /usr/include/linux/i2c-dev.h, but if we
 * include that we won't be able to compile this except on Linux.  It
 * won't *run* anywhere but on Linux, but it's useful to be able to do
 * compilation tests on other platforms, eg, with Clang, so for now we
 * take the small risk that this one magic constant might change.
 */

#define I2C_SLAVE       0x0703


#ifdef USE_HARDWARE

/*
 * I2C-related parameters, copied from hash_tester.c
 */

/* I2C configuration */
#define I2C_DEV   "/dev/i2c-2"
#define I2C_ADDR  0x0f

/* command codes */
#define SOC       0x55
#define EOC       0xaa
#define READ_CMD  0x10
#define WRITE_CMD 0x11
#define RESET_CMD 0x01

/* response codes */
#define SOR       0xaa
#define EOR       0x55
#define READ_OK   0x7f
#define WRITE_OK  0x7e
#define RESET_OK  0x7d
#define UNKNOWN   0xfe
#define ERROR     0xfd

/* addresses and codes common to all cores */
#define ADDR_NAME0              0x00
#define ADDR_NAME1              0x01
#define ADDR_VERSION            0x02
#define ADDR_CTRL               0x08
#define CTRL_INIT_CMD           1
#define CTRL_NEXT_CMD           2
#define ADDR_STATUS             0x09
#define STATUS_READY_BIT        1
#define STATUS_VALID_BIT        2

/*
 * Address for reading 32 bits of entropy from the noise board.
 * TRNG_VALID is nonzero if valid random bits are available.
 */

#define	TRNG_PREFIX		0x0b
#define	TRNG_DATA		0x20
#define	TRNG_VALID		0x11

static int i2cfd = -1;
static int debug = 0;

/*
 * I2C low-level code
 */

static int i2c_open(void)
{
  if (i2cfd >= 0)
    return 1;

  i2cfd = open(I2C_DEV, O_RDWR);

  if (i2cfd < 0) {
    perror("Unable to open " I2C_DEV);
    i2cfd = -1;
    return 0;
  }

  if (ioctl(i2cfd, I2C_SLAVE, I2C_ADDR) < 0) {
    perror("Unable to set i2c slave device");
    return 0;
  }

  if (debug)
    fprintf(stderr, "[ Opened %s, fd %d ]\n", I2C_DEV, i2cfd);

  return 1;
}

static int i2c_write_bytes(const unsigned char *buf, const size_t len)
{
  if (debug) {
    int i;
    fprintf(stderr, "write [");
    for (i = 0; i < len; ++i)
      fprintf(stderr, " %02x", buf[i]);
    fprintf(stderr, " ]\n");
  }

  if (!i2c_open())
    return 0;

  if (write(i2cfd, buf, len) != len) {
    perror("i2c write failed");
    return 0;
  }

  return 1;
}


static int i2c_read_byte(unsigned char *b)
{
  /*
   * read() on the i2c device only returns one byte at a time,
   * and we need to parse the response one byte at a time anyway.
   */

  if (!i2c_open())
    return 0;

  if (read(i2cfd, b, 1) != 1) {
    perror("i2c read failed");
    return 0;
  }

  return 1;
}

static int i2c_send_write_cmd(const unsigned char addr0, const unsigned char addr1, const unsigned char data[])
{
  unsigned char buf[9];

  buf[0] = SOC;
  buf[1] = WRITE_CMD;
  buf[2] = addr0;
  buf[3] = addr1;
  buf[4] = data[0];
  buf[5] = data[1];
  buf[6] = data[2];
  buf[7] = data[3];
  buf[8] = EOC;

  return i2c_write_bytes(buf, sizeof(buf));
}

static int i2c_send_read_cmd(const unsigned char addr0, const unsigned char addr1)
{
  unsigned char buf[5];

  buf[0] = SOC;
  buf[1] = READ_CMD;
  buf[2] = addr0;
  buf[3] = addr1;
  buf[4] = EOC;

  return i2c_write_bytes(buf, sizeof(buf));
}

static int i2c_get_resp(unsigned char *buf, const size_t length)
{
  int i, len = length;

  for (i = 0; i < len; ++i) {
    assert(len <= length);      /* Paranoia */

    if (!i2c_read_byte(&buf[i]))
      return 0;

    switch (i) {                /* Special handling for certain positions in response */

    case 0:
      if (buf[i] == SOR)        /* Start of record (we hope) */
        continue;
      fprintf(stderr, "Lost sync: expected 0x%02x (SOR), got 0x%02x\n", SOR, buf[0]);
      return 0;

    case 1:                     /* Response code */
      switch (buf[i]) {
      case READ_OK:
        len = 9;
        continue;
      case WRITE_OK:
        len = 5;
        continue;
      case RESET_OK:
        len = 3;
        continue;
      case ERROR:
      case UNKNOWN:
        len = 4;
        continue;
      default:
        fprintf(stderr, "Lost sync: unknown response code 0x%02x\n", buf[i]);
        return 0;
      }
    }
  }

  if (debug) {
    fprintf(stderr, "read  [");
    for (i = 0; i < len; ++i)
      fprintf(stderr, " %02x", buf[i]);
    fprintf(stderr, " ]\n");
  }

  return 1;
}

static int i2c_check_expected(const unsigned char buf[], const int i, const unsigned char expected)
{
  if (buf[i] == expected)
    return 1;
  fprintf(stderr, "Response byte %d: expected 0x%02x, got 0x%02x\n", i, expected, buf[i]);
  return 0;
}


static int i2c_write(const unsigned char addr0, const unsigned char addr1, const unsigned char data[])
{
  unsigned char buf[5];

  if (!i2c_send_write_cmd(addr0, addr1, data) ||
      !i2c_get_resp(buf, sizeof(buf))         ||
      !i2c_check_expected(buf, 0, SOR)        ||
      !i2c_check_expected(buf, 1, WRITE_OK)   ||
      !i2c_check_expected(buf, 2, addr0)      ||
      !i2c_check_expected(buf, 3, addr1)      ||
      !i2c_check_expected(buf, 4, EOR))
    return 0;

  return 1;
}

static int i2c_read(const unsigned char addr0, const unsigned char addr1, unsigned char data[])
{
  unsigned char buf[9];

  if (!i2c_send_read_cmd(addr0, addr1)     ||
      !i2c_get_resp(buf, sizeof(buf))      ||
      !i2c_check_expected(buf, 0, SOR)     ||
      !i2c_check_expected(buf, 1, READ_OK) ||
      !i2c_check_expected(buf, 2, addr0)   ||
      !i2c_check_expected(buf, 3, addr1)   ||
      !i2c_check_expected(buf, 8, EOR))
    return 0;

  data[0] = buf[4];
  data[1] = buf[5];
  data[2] = buf[6];
  data[3] = buf[7];
  return 1;
}

static int i2c_ctrl(const unsigned char addr0, const unsigned char ctrl_cmd)
{
  unsigned char data[4];
  memset(data, 0, sizeof(data));
  data[3] = ctrl_cmd;
  return i2c_write(addr0, ADDR_CTRL, data);
}

static int i2c_wait(const unsigned char addr0, const unsigned char status)
{
  unsigned char buf[9];

  do {
    if (!i2c_send_read_cmd(addr0, ADDR_STATUS))
      return 0;
    if (!i2c_get_resp(buf, sizeof(buf)))
      return 0;
    if (buf[1] != READ_OK)
      return 0;
  } while ((buf[7] & status) != status);

  if (debug)
    fprintf(stderr, "[ Done waiting ]\n");

  return 1;
}

static int i2c_wait_ready(const unsigned char addr0)
{
  if (debug)
    fprintf(stderr, "[ Waiting for ready ]\n");
  return i2c_wait(addr0, STATUS_READY_BIT);
}

static int i2c_wait_valid(const unsigned char addr0)
{
  if (debug)
    fprintf(stderr, "[ Waiting for valid ]\n");
  return i2c_wait(addr0, STATUS_VALID_BIT);
}

/****************************************************************************
 *                                                                          *
 *                               Random Numbers                             *
 *                                                                          *
 ****************************************************************************/

/*
 * First attempt at reading random data from the Novena.
 *
 * In theory, we should wait for TRNG_VALID before reading random
 * data, but as long as this is running over I2C we're going to be so
 * slow that there's no point, and checking would just make us slower.
 */

#define WAIT_FOR_TRNG_VALID	0

static int readRandom(void *buffer, const int length)
{
  unsigned char temp[4], *buf = buffer;
  int i, last;

  assert(isWritePtr(buffer, length));

  REQUIRES_B(length >= 1 && length < MAX_INTLENGTH);

  for (i = 0; i < length; i += 4) {

#if WAIT_FOR_TRNG_VALID
    if (!i2c_wait_valid(TRNG_PREFIX)) {
      fprintf(stderr, "[ i2c_wait_valid(TRNG_PREFIX) failed ]\n");
      return 0;
    }
    do {
      if (!i2c_read(TRNG_PREFIX, TRNG_VALID, temp)) {
        fprintf(stderr, "[ i2c_read(TRNG_VALID) failed ]\n");
        return 0;
      }
    } while (!temp[3]);
    if (!i2c_wait_valid(TRNG_PREFIX)) {
      fprintf(stderr, "[ i2c_wait_valid(TRNG_PREFIX) failed ]\n");
      return 0;
    }
#endif  /* WAIT_FOR_TRNG_VALID */

    last = (length - i) < 4;
    if (!i2c_read(TRNG_PREFIX, TRNG_DATA, (last ? temp : (buf + i)))) {
      fprintf(stderr, "[ i2c_read(TRNG_DATA) failed ]\n");
      return 0;
    }
    if (last) {
      for (; i < length; i++)
        buf[i] = temp[i & i];
    }
  }

  return 1;
}

/****************************************************************************
 *                                                                          *
 *                           Hardware External Interface                    *
 *                                                                          *
 ****************************************************************************/

/* The capability information for this device */

static const CAPABILITY_INFO capabilities[] = {
  { CRYPT_ALGO_NONE }, { CRYPT_ALGO_NONE }
};

/* Return the hardware capabilities list */

int hwGetCapabilities(const CAPABILITY_INFO **capabilityInfo, int *noCapabilities)
{
  assert(isReadPtr(capabilityInfo, sizeof(CAPABILITY_INFO *)));
  assert(isWritePtr(noCapabilities, sizeof(int)));

  *capabilityInfo = capabilities;
  *noCapabilities = FAILSAFE_ARRAYSIZE(capabilities, CAPABILITY_INFO);

  return CRYPT_OK;
}

/* Get random data from the hardware. */

int hwGetRandom(void *buffer, const int length)
{
  assert(isWritePtr(buffer, length));

  REQUIRES(length >= 1 && length < MAX_INTLENGTH);

  if (readRandom(buffer, length))
    return CRYPT_OK;
  else
    return CRYPT_ERROR_RANDOM;
}

/*
 * These "personality" methods are trivial stubs, as we do not yet
 * have any cores which do encyrption or signature.  When we do, these
 * methods will need to be rewritten, and whoever does that rewriting
 * will definitely want to look at the detailed comments and template
 * code in device/hw_dummy.c.
 */

/* Look up an item held in the hardware */

int hwLookupItem(const void *keyID, const int keyIDlength, int *keyHandle)
{
  assert(keyHandle != NULL);
  *keyHandle = CRYPT_ERROR;
  return CRYPT_ERROR_NOTFOUND;
}

/* Delete an item held in the hardware */

int hwDeleteItem(const int keyHandle)
{
  return CRYPT_OK;
}

/* Initialise/zeroise the hardware */

int hwInitialise(void)
{
  return CRYPT_OK;
}

#endif /* USE_HARDWARE */

/*
 * "Any programmer who fails to comply with the standard naming, formatting,
 *  or commenting conventions should be shot.  If it so happens that it is
 *  inconvenient to shoot him, then he is to be politely requested to recode
 *  his program in adherence to the above standard."
 *                      -- Michael Spier, Digital Equipment Corporation
 *
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */