aboutsummaryrefslogtreecommitdiff
path: root/src/cryptech_novena_i2c_trng.c
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2014-12-13 13:38:50 -0500
committerRob Austein <sra@hactrn.net>2014-12-13 13:38:50 -0500
commit95009f48e05d06fb77a9127731d7f5f2de489055 (patch)
tree6db2ce5af8ec1ac7a70291fd37164d5fafa83166 /src/cryptech_novena_i2c_trng.c
parent1b88a31f228b4b0eddc23b31d71fb1e5ca6abe66 (diff)
Add code to use Cryptech TRNG in place of Cryptlib's default entropy
sources. This may need further attention, particularly once we start working with buses other than the current I2C kludge.
Diffstat (limited to 'src/cryptech_novena_i2c_trng.c')
-rw-r--r--src/cryptech_novena_i2c_trng.c404
1 files changed, 399 insertions, 5 deletions
diff --git a/src/cryptech_novena_i2c_trng.c b/src/cryptech_novena_i2c_trng.c
index 599c0db..5877acc 100644
--- a/src/cryptech_novena_i2c_trng.c
+++ b/src/cryptech_novena_i2c_trng.c
@@ -7,6 +7,9 @@
* the Novena PVT1 development board using the "coretest" byte stream
* protocol. This is compatible with the test/novena_trng FPGA build.
*
+ * Well, except that apparently cryptlib doesn't like it when we just
+ * provide a TRNG, so try also adding all the code for the hash cores.
+ *
* The communication channel used here is not suitable for production
* use, this is just a prototype.
*
@@ -82,14 +85,14 @@
#define I2C_DEV "/dev/i2c-2"
#define I2C_ADDR 0x0f
-/* command codes */
+/* Command codes */
#define SOC 0x55
#define EOC 0xaa
#define READ_CMD 0x10
#define WRITE_CMD 0x11
#define RESET_CMD 0x01
-/* response codes */
+/* Response codes */
#define SOR 0xaa
#define EOR 0x55
#define READ_OK 0x7f
@@ -98,7 +101,7 @@
#define UNKNOWN 0xfe
#define ERROR 0xfd
-/* addresses and codes common to all cores */
+/* Addresses and codes common to all cores */
#define ADDR_NAME0 0x00
#define ADDR_NAME1 0x01
#define ADDR_VERSION 0x02
@@ -110,6 +113,52 @@
#define STATUS_VALID_BIT 2
/*
+ * Addresses and codes for the specific hash cores.
+ * Lengths here are in bytes (not bits, not 32-bit words).
+ */
+
+#define SHA1_ADDR_PREFIX 0x10
+#define SHA1_ADDR_BLOCK 0x10
+#define SHA1_BLOCK_LEN bitsToBytes(512)
+#define SHA1_LENGTH_LEN bitsToBytes(64)
+#define SHA1_ADDR_DIGEST 0x20
+#define SHA1_DIGEST_LEN bitsToBytes(160)
+
+#define SHA256_ADDR_PREFIX 0x20
+#define SHA256_ADDR_BLOCK 0x10
+#define SHA256_BLOCK_LEN bitsToBytes(512)
+#define SHA256_LENGTH_LEN bitsToBytes(64)
+#define SHA256_ADDR_DIGEST 0x20
+#define SHA256_DIGEST_LEN bitsToBytes(256)
+
+#define SHA512_ADDR_PREFIX 0x30
+#define SHA512_CTRL_MODE_LOW 2
+#define SHA512_CTRL_MODE_HIGH 3
+#define SHA512_ADDR_BLOCK 0x10
+#define SHA512_BLOCK_LEN bitsToBytes(1024)
+#define SHA512_LENGTH_LEN bitsToBytes(128)
+#define SHA512_ADDR_DIGEST 0x40
+#define SHA384_DIGEST_LEN bitsToBytes(384)
+#define SHA512_DIGEST_LEN bitsToBytes(512)
+#define MODE_SHA_512_224 (0 << SHA512_CTRL_MODE_LOW)
+#define MODE_SHA_512_256 (1 << SHA512_CTRL_MODE_LOW)
+#define MODE_SHA_384 (2 << SHA512_CTRL_MODE_LOW)
+#define MODE_SHA_512 (3 << SHA512_CTRL_MODE_LOW)
+
+/* Longest digest block we support at the moment */
+#define MAX_BLOCK_LEN SHA512_BLOCK_LEN
+
+/* Hash state */
+typedef struct {
+ unsigned long long msg_length_high; /* Total data hashed in this message */
+ unsigned long long msg_length_low; /* (128 bits in SHA-512 cases) */
+ size_t block_length; /* Block length for this algorithm */
+ unsigned char block[MAX_BLOCK_LEN]; /* Block we're accumulating */
+ size_t block_used; /* How much of the block we've used */
+ unsigned block_count; /* Blocks sent */
+} hash_state_t;
+
+/*
* Address for reading 32 bits of entropy from the noise board.
* TRNG_VALID is nonzero if valid random bits are available.
*/
@@ -357,6 +406,59 @@ static int i2c_wait_valid(const unsigned char addr0)
/****************************************************************************
* *
+ * Hash utilities *
+ * *
+ ****************************************************************************/
+
+/*
+ * Send one block to a core.
+ */
+
+static int hash_write_block(const unsigned char addr_prefix,
+ const unsigned char addr_block,
+ const unsigned char ctrl_mode,
+ const hash_state_t *state)
+{
+ unsigned char ctrl_cmd;
+ int i;
+
+ assert(state != NULL && state->block_length % 4 == 0);
+
+ for (i = 0; i + 3 < state->block_length; i += 4)
+ if (!i2c_write(addr_prefix, addr_block + i/4, state->block + i))
+ return 0;
+
+ ctrl_cmd = state->block_count == 0 ? CTRL_INIT_CMD : CTRL_NEXT_CMD;
+
+ if (debug)
+ fprintf(stderr, "[ %s ]\n", state->block_count == 0 ? "init" : "next");
+
+ return i2c_ctrl(addr_prefix, ctrl_cmd|ctrl_mode) && i2c_wait_ready(addr_prefix);
+}
+
+/*
+ * Read hash result from core.
+ */
+
+static int hash_read_digest(const unsigned char addr_prefix, const unsigned char addr_digest,
+ unsigned char *digest, const size_t digest_length)
+{
+ int i;
+
+ assert(digest_length % 4 == 0);
+
+ if (!i2c_wait_valid(addr_prefix))
+ return 0;
+
+ for (i = 0; i + 3 < digest_length; i += 4)
+ if (!i2c_read(addr_prefix, addr_digest + i/4, digest + i))
+ return 0;
+
+ return 1;
+}
+
+/****************************************************************************
+ * *
* Random Numbers *
* *
****************************************************************************/
@@ -367,6 +469,10 @@ static int i2c_wait_valid(const unsigned char addr0)
* 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.
+ *
+ * If the TRNG isn't installed we need to return failure to our
+ * caller. At least with the current I2C coretest interface, coretest
+ * signals this (deliberately?) by returning all zeros.
*/
#define WAIT_FOR_TRNG_VALID 0
@@ -376,6 +482,9 @@ static int readRandom(void *buffer, const int length)
unsigned char temp[4], *buf = buffer;
int i, last;
+ if (debug)
+ fprintf(stderr, "[ Requesting %d bytes of random data ]\n", length);
+
assert(isWritePtr(buffer, length));
REQUIRES_B(length >= 1 && length < MAX_INTLENGTH);
@@ -410,7 +519,268 @@ static int readRandom(void *buffer, const int length)
}
}
- return 1;
+ for (i = 0, buf = buffer; i < length; i++, buf++)
+ if (*buf != 0)
+ return 1;
+
+ fprintf(stderr, "[ \"Random\" data all zeros, guess TRNG is not installed ]\n");
+ return 0;
+}
+
+/****************************************************************************
+ * *
+ * Hash/MAC Capability Interface Routines *
+ * *
+ ****************************************************************************/
+
+/*
+ * Return context subtype-specific information. All supported hash
+ * algorithms currently use the same state object, so they can all use
+ * this method.
+ */
+
+static int hashGetInfo(const CAPABILITY_INFO_TYPE type,
+ CONTEXT_INFO *contextInfoPtr,
+ void *data, const int length)
+{
+ switch (type) {
+ case CAPABILITY_INFO_STATESIZE:
+ /*
+ * Tell cryptlib how much hash-state storage we want allocated.
+ */
+ *(int *) data = sizeof(hash_state_t);
+ return CRYPT_OK;
+
+ default:
+ return getDefaultInfo(type, contextInfoPtr, data, length);
+ }
+}
+
+/*
+ * Hash data. All supported hash algorithms use similar block
+ * manipulations and padding algorithms, so all can use this method
+ * with a few parameters which we handle via closures below.
+ */
+
+static int doHash(CONTEXT_INFO *contextInfoPtr, const unsigned char *buffer, int length,
+ const size_t block_length, const unsigned char addr_prefix, const unsigned char addr_block,
+ const size_t digest_length, const unsigned char addr_digest, const unsigned char ctrl_mode,
+ const size_t length_length)
+{
+ hash_state_t *state = NULL;
+ size_t n;
+ int i;
+
+ assert(isWritePtr(contextInfoPtr, sizeof(CONTEXT_INFO)));
+ assert(length == 0 || isWritePtr(buffer, length));
+
+ state = (hash_state_t *) contextInfoPtr->ctxHash->hashInfo;
+
+ /*
+ * If the hash state was reset to allow another round of hashing,
+ * reinitialise things.
+ */
+
+ if (!(contextInfoPtr->flags & CONTEXT_FLAG_HASH_INITED)) {
+ memset(state, 0, sizeof(*state));
+ state->block_length = block_length;
+ }
+
+ /* May want an assertion here that state->block_length is correct */
+
+ if (length > 0) { /* More data to hash */
+
+ const unsigned char *p = buffer;
+
+ while ((n = state->block_length - state->block_used) <= length) {
+ /*
+ * We have enough data for another complete block.
+ */
+ if (debug)
+ fprintf(stderr, "[ Full block, length %lu, used %lu, n %lu, msg_length %llu ]\n",
+ (unsigned long) length, (unsigned long) state->block_used, (unsigned long) n, state->msg_length_low);
+ memcpy(state->block + state->block_used, p, n);
+ if ((state->msg_length_low += n) < n)
+ state->msg_length_high++;
+ state->block_used = 0;
+ length -= n;
+ p += n;
+ if (!hash_write_block(addr_prefix, addr_block, ctrl_mode, state))
+ return CRYPT_ERROR_FAILED;
+ state->block_count++;
+ }
+
+ if (length > 0) {
+ /*
+ * Data left over, but not enough for a full block, stash it.
+ */
+ if (debug)
+ fprintf(stderr, "[ Partial block, length %lu, used %lu, n %lu, msg_length %llu ]\n",
+ (unsigned long) length, (unsigned long) state->block_used, (unsigned long) n, state->msg_length_low);
+ assert(length < n);
+ memcpy(state->block + state->block_used, p, length);
+ if ((state->msg_length_low += length) < length)
+ state->msg_length_high++;
+ state->block_used += length;
+ }
+ }
+
+ else { /* Done: add padding, then pull result from chip */
+
+ unsigned long long bit_length_low = (state->msg_length_low << 3);
+ unsigned long long bit_length_high = (state->msg_length_high << 3) | (state->msg_length_low >> 61);
+ unsigned char *p;
+
+ /* Initial pad byte */
+ assert(state->block_used < state->block_length);
+ state->block[state->block_used++] = 0x80;
+
+ /* If not enough room for bit count, zero and push current block */
+ if ((n = state->block_length - state->block_used) < length_length) {
+ if (debug)
+ fprintf(stderr, "[ Overflow block, length %lu, used %lu, n %lu, msg_length %llu ]\n",
+ (unsigned long) length, (unsigned long) state->block_used, (unsigned long) n, state->msg_length_low);
+ if (n > 0)
+ memset(state->block + state->block_used, 0, n);
+ if (!hash_write_block(addr_prefix, addr_block, ctrl_mode, state))
+ return CRYPT_ERROR_FAILED;
+ state->block_count++;
+ state->block_used = 0;
+ }
+
+ /* Pad final block */
+ n = state->block_length - state->block_used;
+ assert(n >= length_length);
+ if (n > 0)
+ memset(state->block + state->block_used, 0, n);
+ if (debug)
+ fprintf(stderr, "[ Final block, length %lu, used %lu, n %lu, msg_length %llu ]\n",
+ (unsigned long) length, (unsigned long) state->block_used, (unsigned long) n, state->msg_length_low);
+ p = state->block + state->block_length;
+ for (i = 0; (bit_length_low || bit_length_high) && i < length_length; i++) {
+ *--p = (unsigned char) (bit_length_low & 0xFF);
+ bit_length_low >>= 8;
+ if (bit_length_high) {
+ bit_length_low |= ((bit_length_high & 0xFF) << 56);
+ bit_length_high >>= 8;
+ }
+ }
+
+ /* Push final block */
+ if (!hash_write_block(addr_prefix, addr_block, ctrl_mode, state))
+ return CRYPT_ERROR_FAILED;
+ state->block_count++;
+
+ /* All data pushed to core, now we just need to read back the result */
+
+ assert(digest_length <= sizeof(contextInfoPtr->ctxHash->hash));
+ if (!hash_read_digest(addr_prefix, addr_digest, contextInfoPtr->ctxHash->hash, digest_length))
+ return CRYPT_ERROR_FAILED;
+ }
+
+ return CRYPT_OK;
+}
+
+/* Perform a self-test */
+
+static int sha1SelfTest(void)
+{
+ /*
+ * If we think of a self-test, insert it here.
+ */
+
+ return CRYPT_OK;
+}
+
+/* Hash data */
+
+static int sha1Hash(CONTEXT_INFO *contextInfoPtr, unsigned char *buffer, int length)
+{
+ return doHash(contextInfoPtr, buffer, length,
+ SHA1_BLOCK_LEN, SHA1_ADDR_PREFIX, SHA1_ADDR_BLOCK,
+ SHA1_DIGEST_LEN, SHA1_ADDR_DIGEST, 0, SHA1_LENGTH_LEN);
+}
+
+/* Perform a self-test */
+
+static int sha2SelfTest(void)
+{
+ /*
+ * If we think of a self-test, insert it here.
+ */
+
+ return CRYPT_OK;
+}
+
+/* Hash data */
+
+static int sha2Hash(CONTEXT_INFO *contextInfoPtr, unsigned char *buffer, int length)
+{
+ assert(contextInfoPtr != NULL && contextInfoPtr->capabilityInfo != NULL);
+
+ switch (contextInfoPtr->capabilityInfo->blockSize) {
+
+ case bitsToBytes(256):
+ return doHash(contextInfoPtr, buffer, length,
+ SHA256_BLOCK_LEN, SHA256_ADDR_PREFIX, SHA256_ADDR_BLOCK,
+ SHA256_DIGEST_LEN, SHA256_ADDR_DIGEST, 0, SHA256_LENGTH_LEN);
+
+ case bitsToBytes(384):
+ return doHash(contextInfoPtr, buffer, length,
+ SHA512_BLOCK_LEN, SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK,
+ SHA384_DIGEST_LEN, SHA512_ADDR_DIGEST, MODE_SHA_384,
+ SHA512_LENGTH_LEN);
+
+ case bitsToBytes(512):
+ return doHash(contextInfoPtr, buffer, length,
+ SHA512_BLOCK_LEN, SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK,
+ SHA512_DIGEST_LEN, SHA512_ADDR_DIGEST, MODE_SHA_512,
+ SHA512_LENGTH_LEN);
+
+ default:
+ return CRYPT_ERROR_FAILED;
+ }
+}
+
+/* Parameter initialization, to handle SHA-2 algorithms other than SHA-256 */
+
+static int sha2InitParams(INOUT CONTEXT_INFO *contextInfoPtr,
+ IN_ENUM(KEYPARAM) const KEYPARAM_TYPE paramType,
+ IN_OPT const void *data,
+ IN_INT const int dataLength)
+{
+ static const CAPABILITY_INFO capabilityInfoSHA384 = {
+ CRYPT_ALGO_SHA2, bitsToBytes( 384 ), "SHA-384", 7,
+ bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ),
+ sha2SelfTest, hashGetInfo, NULL, NULL, NULL, NULL, sha2Hash, sha2Hash
+ };
+
+ static const CAPABILITY_INFO capabilityInfoSHA512 = {
+ CRYPT_ALGO_SHA2, bitsToBytes( 512 ), "SHA-512", 7,
+ bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ),
+ sha2SelfTest, hashGetInfo, NULL, NULL, NULL, NULL, sha2Hash, sha2Hash
+ };
+
+ assert(isWritePtr(contextInfoPtr, sizeof(CONTEXT_INFO)));
+ REQUIRES(contextInfoPtr->type == CONTEXT_HASH);
+ REQUIRES(paramType > KEYPARAM_NONE && paramType < KEYPARAM_LAST);
+
+ if (paramType == KEYPARAM_BLOCKSIZE) {
+ switch (dataLength) {
+ case bitsToBytes(256):
+ return CRYPT_OK;
+ case bitsToBytes(384):
+ contextInfoPtr->capabilityInfo = &capabilityInfoSHA384;
+ return CRYPT_OK;
+ case bitsToBytes(512):
+ contextInfoPtr->capabilityInfo = &capabilityInfoSHA512;
+ return CRYPT_OK;
+ default:
+ return CRYPT_ARGERROR_NUM1;
+ }
+ }
+
+ return initGenericParams(contextInfoPtr, paramType, data, dataLength);
}
/****************************************************************************
@@ -422,6 +792,15 @@ static int readRandom(void *buffer, const int length)
/* The capability information for this device */
static const CAPABILITY_INFO capabilities[] = {
+
+ { CRYPT_ALGO_SHA1, bitsToBytes( 160 ), "SHA-1", 5,
+ bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ),
+ sha1SelfTest, hashGetInfo, NULL, NULL, NULL, NULL, sha1Hash, sha1Hash },
+
+ { CRYPT_ALGO_SHA2, bitsToBytes( 256 ), "SHA-2", 5,
+ bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ),
+ sha2SelfTest, hashGetInfo, NULL, sha2InitParams, NULL, NULL, sha2Hash, sha2Hash },
+
{ CRYPT_ALGO_NONE }, { CRYPT_ALGO_NONE }
};
@@ -438,10 +817,22 @@ int hwGetCapabilities(const CAPABILITY_INFO **capabilityInfo, int *noCapabilitie
return CRYPT_OK;
}
-/* Get random data from the hardware. */
+/*
+ * Get random data from the hardware.
+ *
+ * So, we provide this function because the Cryptlib HAL API seems to
+ * require it, but as far as I can tell nothing ever calls it. Hmm.
+ * See src/cryptech_random.c for how I'm using this to feed Cryptlib's
+ * CSPRNG. Bypassing the CSPRNG would be, well, not hard exactly, but
+ * would require somewhat drastic surgery, so I'm leaving that for
+ * another day.
+ */
int hwGetRandom(void *buffer, const int length)
{
+ if (debug)
+ fprintf(stderr, "[ Requested %d bytes of random data]\n", length);
+
assert(isWritePtr(buffer, length));
REQUIRES(length >= 1 && length < MAX_INTLENGTH);
@@ -480,6 +871,9 @@ int hwDeleteItem(const int keyHandle)
int hwInitialise(void)
{
+ if (debug)
+ fprintf(stderr, "[ Initializing cryptech hardware ]\n");
+
return CRYPT_OK;
}