aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--.gitignore1
-rw-r--r--GNUmakefile36
-rw-r--r--README.md22
-rw-r--r--src/cryptech_novena_i2c_trng.c404
-rw-r--r--src/cryptech_random.c123
-rw-r--r--tests/test_trng.py48
6 files changed, 609 insertions, 25 deletions
diff --git a/.gitignore b/.gitignore
index a25d100..4cd35ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*~
+.gdbinit
TAGS
build
diff --git a/GNUmakefile b/GNUmakefile
index aed7b4a..9742a8f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -12,7 +12,18 @@
# documentation for details).
ifndef CRYPTECH_HAL
- CRYPTECH_HAL := src/cryptech_novena_i2c_simple.c
+ CRYPTECH_HAL := src/cryptech_novena_i2c_trng.c
+endif
+
+# RNG hack defaults to enabled if we're building the TRNG, disabled
+# otherwise. This is a kludge, do better later.
+
+ifndef CRYPTECH_RANDOM
+ CRYPTECH_RANDOM := $(and $(findstring trng,${CRYPTECH_HAL}),src/cryptech_random.c)
+endif
+
+ifndef CRYPTECH_DEBUG
+ CRYPTECH_DEBUG := yes
endif
# Notes on the option settings we use when building cryptlib:
@@ -39,8 +50,22 @@ LIB = build/libcl.a
PYTHONPATH = $(firstword $(wildcard build/bindings/build/lib.*))
+SED_COMMAND := -e '/^CFLAGS/s=$$= -fPIC -DUSE_SHA2_EXT -DUSE_HARDWARE -DUSE_DEVICES -DNO_THREADS='
+
+ifeq "${CRYPTECH_DEBUG}" "yes"
+ SED_COMMAND += -e 's=-DNDEBUG==g' -e 's=-O2==g' -e '/^CFLAGS /s,^.*$$,CFLAGS = $$(CFLAGS_DEBUG),'
+endif
+
+ifneq "$(strip ${CRYPTECH_HAL})" ""
+ SED_COMMAND += -e 's=device/hw_dummy=../$(basename ${CRYPTECH_HAL})=g' -e 's=hw_dummy=$(notdir $(basename ${CRYPTECH_HAL}))=g'
+endif
+
+ifneq "$(strip ${CRYPTECH_RANDOM})" ""
+ SED_COMMAND += -e 's=random/unix=../$(basename ${CRYPTECH_RANDOM})=g' -e 's=unix\.o=$(notdir $(basename ${CRYPTECH_RANDOM})).o=g'
+endif
+
all: build/makefile.ready
- cd build; ${MAKE}
+ cd build; ${MAKE} debug
@${MAKE} python-bindings
clean:
@@ -50,14 +75,11 @@ build/makefile.ready: GNUmakefile dist/cl342.zip
rm -rf build
mkdir build
cd build; unzip -a ../dist/cl342.zip
- sed <build/makefile >build/makefile.cryptech \
- -e 's=device/hw_dummy=../$(basename ${CRYPTECH_HAL})=g' \
- -e 's=hw_dummy=$(notdir $(basename ${CRYPTECH_HAL}))=g' \
- -e '/^CFLAGS/s=$$= -fPIC -DUSE_SHA2_EXT -DUSE_HARDWARE -DUSE_DEVICES='
+ sed <build/makefile >build/makefile.cryptech ${SED_COMMAND}
mv build/makefile.cryptech build/makefile
touch $@
-ifeq (,${PYTHONPATH})
+ifeq "${PYTHONPATH}" ""
python-bindings:
cd build/bindings; python setup.py build
diff --git a/README.md b/README.md
index 0a654f8..94994ce 100644
--- a/README.md
+++ b/README.md
@@ -31,11 +31,9 @@ different protocols:
`core/novena_i2c_simple` environment.
* An implementation using the `coretest` byt-stream protocol as
- implemented by the `test/novena_entropy` FPGA build. This differs
- from the others in that it supports the entropy generators. Note
- that neither this HAL nor this FPGA build supports any cryptographic
- algorithms, nor do they support the Cryptech TRNG. They're just for
- testing the entropy generators.
+ implemented by the `test/novena_trng` FPGA build. This differs from
+ the others in that it supports the Cryptech TRNG. Note that neither
+ this HAL nor this FPGA build supports any cryptographic algorithms.
All of these HAL implementations are in the `src/` directory. See the
`GNUmakefile` for details on how to select the variant you want.
@@ -45,10 +43,6 @@ digest algorithms. The current HAL uses the SHA-1, SHA-256, and
SHA-512 cores to implement the SHA-1, SHA-256, SHA-384, and SHA-512
digests. SHA-512/224 and SHA-512/256 are not supported.
-The TRGN is not yet supported, due to lack of an I2C interface. At
-some point we will either add an I2C interface to the TRNG or skip
-over I2C entirely and go straight to EIM.
-
In principal there is no reason why one could not write a HAL which
spoke to a Terasic board, perhaps via the `coretest` protocol over a
UART, but to date this has not been done.
@@ -69,11 +63,13 @@ Packaging Cryptlib this way has two implications:
## Test code ##
-The `tests/` directory contains an initial test script, written in
-Python, using the standard Cryptlib Python bindings. The Cryptlib
-Python environment is a fairly literaly translation of the Cryptlib C
+The `tests/` directory contains a few test scripts, written in Python,
+using the standard Cryptlib Python bindings. The Cryptlib Python
+environment is a fairly literaly translation of the Cryptlib C
environment, so portions of it will be a bit, um, surprising to Python
-programmers, but the basic functionality works.
+programmers, but the basic functionality works. Note that it's normal
+for test scripts to fail when the functionality they're testing isn't
+loaded on the FPGA.
## Copyright status ##
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;
}
diff --git a/src/cryptech_random.c b/src/cryptech_random.c
new file mode 100644
index 0000000..d8f711b
--- /dev/null
+++ b/src/cryptech_random.c
@@ -0,0 +1,123 @@
+/*
+ * cryptech_random.c
+ * -----------------
+ *
+ * This is a shim to connect the Cryptech TRNG to Cryptlib's CSPRNG.
+ *
+ * Prototype HAL code for the Cryptech environment already provides
+ * the RNG code required by the HAL, but it doesn't look like Cryptlib
+ * itself ever calls that, it only seems to use the system device's
+ * random function. So this shim just uses the code we already wrote
+ * to meet the HAL API requirement to feed Cryptlib's CSPRNG.
+ *
+ * Whether it makes sense to use what we hope is already a good TRNG
+ * just to provide entropy for a CSPRNG is a question for another day.
+ *
+ * Author: 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.
+ */
+
+#if defined( INC_ALL )
+ #include "crypt.h"
+ #include "context.h"
+ #include "hardware.h"
+ #include "random.h"
+#else
+ #include "crypt.h"
+ #include "context/context.h"
+ #include "device/hardware.h"
+ #include "random/random.h"
+#endif
+
+#define FAST_BUFSIZE bitsToBytes(64)
+#define SLOW_BUFSIZE (5 * FAST_BUFSIZE)
+
+void fastPoll(void)
+{
+ MESSAGE_DATA msgData;
+ unsigned char buffer[FAST_BUFSIZE];
+
+ if (hwGetRandom(buffer, sizeof(buffer)) == CRYPT_OK) {
+ setMessageData(&msgData, buffer, sizeof(buffer));
+ krnlSendMessage(SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_ENTROPY);
+ zeroise(buffer, sizeof(buffer));
+ }
+}
+
+void slowPoll(void)
+{
+ MESSAGE_DATA msgData;
+ unsigned char buffer[SLOW_BUFSIZE];
+ int quality = 100;
+
+ if (hwGetRandom(buffer, sizeof(buffer)) == CRYPT_OK) {
+ setMessageData(&msgData, buffer, sizeof(buffer));
+ krnlSendMessage(SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_ENTROPY);
+ zeroise(buffer, sizeof(buffer));
+ krnlSendMessage(SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE, &quality, CRYPT_IATTRIBUTE_ENTROPY_QUALITY);
+ }
+}
+
+/*
+ * Might patch these out in random/random.h eventually, but no-op
+ * stubs will do for testing.
+ */
+
+void initRandomPolling(void)
+{
+ return;
+}
+
+void endRandomPolling(void)
+{
+ return;
+}
+
+CHECK_RETVAL \
+int waitforRandomCompletion(const BOOLEAN force)
+{
+ return CRYPT_OK;
+}
+
+CHECK_RETVAL_BOOL \
+BOOLEAN checkForked(void)
+{
+ return FALSE;
+}
+
+/*
+ * "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:
+ */
diff --git a/tests/test_trng.py b/tests/test_trng.py
new file mode 100644
index 0000000..5207eda
--- /dev/null
+++ b/tests/test_trng.py
@@ -0,0 +1,48 @@
+# Trivial test of cryptech trng and noise cores via cryptlib python interface.
+# Might upgrade to Python's unittest framework eventually.
+
+import atexit
+import os
+
+print "[Loading cryptlib]"
+from cryptlib_py import *
+
+if os.getenv("SET_GDB_BREAKPOINTS"):
+ print "[Sending SIGINT to self to throw to gdb]"
+ os.kill(os.getpid(), 2)
+ print "[Continuing]"
+
+print "[Initializing cryptlib]"
+cryptInit()
+atexit.register(cryptEnd)
+
+print "[Opening device]"
+dev = cryptDeviceOpen(CRYPT_UNUSED, CRYPT_DEVICE_HARDWARE, None)
+atexit.register(cryptDeviceClose, dev)
+
+use_dev_context = False
+
+def generate_key(i):
+ label = "RSA-%04d" % i
+ print "[Generating key %s]" % label
+ ctx = None
+ try:
+ if use_dev_context:
+ ctx = cryptDeviceCreateContext(dev, CRYPT_ALGO_RSA)
+ else:
+ ctx = cryptCreateContext(CRYPT_UNUSED, CRYPT_ALGO_RSA)
+ ctx.CTXINFO_LABEL = label
+ ctx.CTXINFO_KEYSIZE = 2048 / 8
+ cryptGenerateKey(ctx)
+ finally:
+ if ctx is not None:
+ cryptDestroyContext(ctx)
+
+have_i2c = os.path.exists("/dev/i2c-2")
+
+if not have_i2c:
+ print
+ print "[I2C device not found, so testing software only, no hardware cores tested]"
+
+for i in xrange(100):
+ generate_key(i)