/*
* cryptech_novena_i2c_entropy.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_entropy 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.
* ENTROPY_VALID is nonzero if there's valid entropy available.
*/
#define ENTROPY_PREFIX 0x20
#define ENTROPY_ADDR 0x20
#define ENTROPY_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.
* Not sure what we should do if ENTROPY_VALID isn't lit, spin wait?
*/
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 (!i2c_wait_valid(ENTROPY_PREFIX)) {
fprintf(stderr, "[ i2c_wait_valid(ENTROPY_PREFIX) failed ]\n");
return 0;
}
do {
if (!i2c_read(ENTROPY_PREFIX, ENTROPY_VALID, temp)) {
fprintf(stderr, "[ i2c_read(ENTROPY_VALID) failed ]\n");
return 0;
}
} while (!temp[3]);
if (!i2c_wait_valid(ENTROPY_PREFIX)) {
fprintf(stderr, "[ i2c_wait_valid(ENTROPY_PREFIX) failed ]\n");
return 0;
}
last = (length - i) < 4;
if (!i2c_read(ENTROPY_PREFIX, ENTROPY_ADDR, last ? temp : buf + i)) {
fprintf(stderr, "[ i2c_read(ENTROPY_ADDR) 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:
*/