/* * rpc_pkey.c * ---------- * Remote procedure call server-side public key implementation. * * Authors: Rob Austein * Copyright (c) 2015, NORDUnet A/S All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - 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. * * - Neither the name of the NORDUnet nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * 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 * HOLDER 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. */ #include #include #include "hal.h" #include "hal_internal.h" #ifndef HAL_STATIC_PKEY_STATE_BLOCKS #define HAL_STATIC_PKEY_STATE_BLOCKS 0 #endif #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 static hal_pkey_slot_t pkey_handle[HAL_STATIC_PKEY_STATE_BLOCKS]; #endif /* * Handle allocation is simple: look for an unused (HAL_KEY_TYPE_NONE) * slot in the table, and, assuming we find one, construct a composite * handle consisting of the index into the table and a counter whose * sole purpose is to keep the same handle from reoccurring anytime * soon, to help identify use-after-free bugs in calling code. * * The high order bit of the pkey handle is left free for * HAL_PKEY_HANDLE_PROXIMATE_FLAG, which is used by the mixed-mode * handlers to route calls to the appropriate destination. */ static inline hal_pkey_slot_t *alloc_slot(const hal_key_flags_t flags) { #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 static uint16_t next_glop = 0; uint32_t glop = ++next_glop << 16; next_glop %= 0x7FFF; assert((glop & HAL_PKEY_HANDLE_TOKEN_FLAG) == 0); if ((flags & HAL_KEY_FLAG_TOKEN) != 0) glop |= HAL_PKEY_HANDLE_TOKEN_FLAG; for (int i = 0; i < sizeof(pkey_handle)/sizeof(*pkey_handle); i++) { if (pkey_handle[i].type != HAL_KEY_TYPE_NONE) continue; memset(&pkey_handle[i], 0, sizeof(pkey_handle[i])); pkey_handle[i].pkey_handle.handle = i | glop; return &pkey_handle[i]; } #endif return NULL; } /* * Check a caller-supplied handle. Must be in range, in use, and have * the right glop. Returns slot pointer on success, NULL otherwise. */ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) { #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 const int i = (int) (handle.handle & 0xFFFF); if (i < sizeof(pkey_handle)/sizeof(*pkey_handle) && pkey_handle[i].pkey_handle.handle == handle.handle) return &pkey_handle[i]; #endif return NULL; } #warning Still need access control on pkey objects based on current login state /* * This would be simple, except for PKCS #11 non-token objects (CKA_TOKEN = CK_FALSE). * Need to check detailed PKCS #11 rules, but, from memory, we may be supposed to allow * access to non-token objects even when not logged in. Maybe. Rules are complex. * * I think the libhal translation of this resolves around HAL_KEY_FLAG_TOKEN. * For token objects, we insist on being logged in properly; for non-token * objects, we do whatever silly thing PKCS #11 wants us to do, probably * defaulting to requiring login if PKCS #11 gives us a choice. */ /* * Pad an octet string with PKCS #1.5 padding for use with RSA. * * For the moment, this only handles type 01 encryption blocks, thus * is only suitable for use with signature and verification. If and * when we add support for encryption and decryption, this function * should be extended to take an argument specifying the block type * and include support for generating type 02 encryption blocks. * Other than the block type code, the only difference is the padding * value: for type 01 it's constant (0xFF), for type 02 it should be * non-zero random bytes from the CSPRNG. * * We use memmove() instead of memcpy() so that the caller can * construct the data to be padded in the same buffer. */ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len, uint8_t *block, const size_t block_len) { assert(data != NULL && block != NULL); /* * Congregation will now please turn to RFC 2313 8.1 as we * construct a PKCS #1.5 type 01 encryption block. */ if (data_len > block_len - 11) return HAL_ERROR_RESULT_TOO_LONG; memmove(block + block_len - data_len, data, data_len); block[0] = 0x00; block[1] = 0x01; /* This is where we'd use non-zero random bytes if constructing a type 02 block. */ memset(block + 2, 0xFF, block_len - 3 - data_len); block[block_len - data_len - 1] = 0x00; return HAL_OK; } /* * Given key flags, open appropriate keystore driver. */ static inline hal_error_t ks_open_from_flags(hal_ks_t **ks, const hal_key_flags_t flags) { return hal_ks_open((flags & HAL_KEY_FLAG_TOKEN) == 0 ? hal_ks_volatile_driver : hal_ks_token_driver, ks); } /* * Receive key from application, store it with supplied name, return a key handle. */ static hal_error_t pkey_local_load(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags) { assert(pkey != NULL && name != NULL); hal_pkey_slot_t *slot; hal_ks_t *ks = NULL; hal_error_t err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; slot->client_handle = client; slot->session_handle = session; slot->type = type; slot->curve = curve; slot->flags = flags; if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err != HAL_OK) return err; *pkey = slot->pkey_handle; *name = slot->name; return HAL_OK; } /* * Look up a key given its name, return a key handle. */ static hal_error_t pkey_local_find(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_uuid_t * const name, const hal_key_flags_t flags) { assert(pkey != NULL && name != NULL); hal_pkey_slot_t *slot; hal_ks_t *ks = NULL; hal_error_t err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; slot->name = *name; slot->client_handle = client; slot->session_handle = session; slot->type = type; if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && (err = hal_ks_fetch(ks, slot, NULL, NULL, 0)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err != HAL_OK) return err; *pkey = slot->pkey_handle; return HAL_OK; } /* * Generate a new RSA key with supplied name, return a key handle. */ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, hal_uuid_t *name, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len, const hal_key_flags_t flags) { assert(pkey != NULL && name != NULL && (key_length & 7) == 0); uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; hal_pkey_slot_t *slot; hal_ks_t *ks = NULL; hal_error_t err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; slot->client_handle = client; slot->session_handle = session; slot->type = HAL_KEY_TYPE_RSA_PRIVATE; slot->curve = HAL_CURVE_NONE; slot->flags = flags; if ((err = hal_rsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), key_length / 8, public_exponent, public_exponent_len)) != HAL_OK) return err; uint8_t der[hal_rsa_private_key_to_der_len(key)]; size_t der_len; if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && (err = ks_open_from_flags(&ks, flags)) == HAL_OK && (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); if (err != HAL_OK) return err; *pkey = slot->pkey_handle; *name = slot->name; return HAL_OK; } /* * Generate a new EC key with supplied name, return a key handle. * At the moment, EC key == ECDSA key, but this is subject to change. */ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags) { assert(pkey != NULL && name != NULL); uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_pkey_slot_t *slot; hal_ks_t *ks = NULL; hal_error_t err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; slot->client_handle = client; slot->session_handle = session; slot->type = HAL_KEY_TYPE_EC_PRIVATE; slot->curve = curve; slot->flags = flags; if ((err = hal_ecdsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), curve)) != HAL_OK) return err; uint8_t der[hal_ecdsa_private_key_to_der_len(key)]; size_t der_len; if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && (err = ks_open_from_flags(&ks, flags)) == HAL_OK && (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); if (err != HAL_OK) return err; *pkey = slot->pkey_handle; *name = slot->name; return HAL_OK; } /* * Discard key handle, leaving key intact. */ static hal_error_t pkey_local_close(const hal_pkey_handle_t pkey) { hal_pkey_slot_t *slot; if ((slot = find_handle(pkey)) == NULL) return HAL_ERROR_KEY_NOT_FOUND; memset(slot, 0, sizeof(*slot)); return HAL_OK; } /* * Delete a key from the store, given its key handle. */ static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey) { hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && (err = hal_ks_delete(ks, slot)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err == HAL_OK || err == HAL_ERROR_KEY_NOT_FOUND) memset(slot, 0, sizeof(*slot)); return err; } /* * Get type of key associated with handle. */ static hal_error_t pkey_local_get_key_type(const hal_pkey_handle_t pkey, hal_key_type_t *type) { if (type == NULL) return HAL_ERROR_BAD_ARGUMENTS; hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; *type = slot->type; return HAL_OK; } /* * Get flags of key associated with handle. */ static hal_error_t pkey_local_get_key_flags(const hal_pkey_handle_t pkey, hal_key_flags_t *flags) { if (flags == NULL) return HAL_ERROR_BAD_ARGUMENTS; hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; *flags = slot->flags; return HAL_OK; } /* * Get length of public key associated with handle. */ static size_t pkey_local_get_public_key_len(const hal_pkey_handle_t pkey) { hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return 0; size_t result = 0; uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; hal_rsa_key_t *rsa_key = NULL; hal_ecdsa_key_t *ecdsa_key = NULL; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err == HAL_OK) { switch (slot->type) { case HAL_KEY_TYPE_RSA_PUBLIC: case HAL_KEY_TYPE_EC_PUBLIC: result = der_len; break; case HAL_KEY_TYPE_RSA_PRIVATE: if (hal_rsa_private_key_from_der(&rsa_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK) result = hal_rsa_public_key_to_der_len(rsa_key); break; case HAL_KEY_TYPE_EC_PRIVATE: if (hal_ecdsa_private_key_from_der(&ecdsa_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK) result = hal_ecdsa_public_key_to_der_len(ecdsa_key); break; default: break; } } memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); return result; } /* * Get public key associated with handle. */ static hal_error_t pkey_local_get_public_key(const hal_pkey_handle_t pkey, uint8_t *der, size_t *der_len, const size_t der_max) { hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; hal_rsa_key_t *rsa_key = NULL; hal_ecdsa_key_t *ecdsa_key = NULL; uint8_t buf[HAL_KS_WRAPPED_KEYSIZE]; size_t buf_len; hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && (err = hal_ks_fetch(ks, slot, buf, &buf_len, sizeof(buf))) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err == HAL_OK) { switch (slot->type) { case HAL_KEY_TYPE_RSA_PUBLIC: case HAL_KEY_TYPE_EC_PUBLIC: if (der_len != NULL) *der_len = buf_len; if (der != NULL && der_max < buf_len) err = HAL_ERROR_RESULT_TOO_LONG; else if (der != NULL) memcpy(der, buf, buf_len); break; case HAL_KEY_TYPE_RSA_PRIVATE: if ((err = hal_rsa_private_key_from_der(&rsa_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK) err = hal_rsa_public_key_to_der(rsa_key, der, der_len, der_max); break; case HAL_KEY_TYPE_EC_PRIVATE: if ((err = hal_ecdsa_private_key_from_der(&ecdsa_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK) err = hal_ecdsa_public_key_to_der(ecdsa_key, der, der_len, der_max); break; default: err = HAL_ERROR_UNSUPPORTED_KEY; break; } } memset(keybuf, 0, sizeof(keybuf)); memset(buf, 0, sizeof(buf)); return err; } /* * Sign something using private key associated with handle. * * RSA has enough quirks that it's simplest to split this out into * algorithm-specific functions. */ static hal_error_t pkey_local_sign_rsa(uint8_t *keybuf, const size_t keybuf_len, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * input, size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { hal_rsa_key_t *key = NULL; hal_error_t err; assert(signature != NULL && signature_len != NULL); assert((hash.handle == HAL_HANDLE_NONE) != (input == NULL || input_len == 0)); if ((err = hal_rsa_private_key_from_der(&key, keybuf, keybuf_len, der, der_len)) != HAL_OK || (err = hal_rsa_key_get_modulus(key, NULL, signature_len, 0)) != HAL_OK) return err; if (*signature_len > signature_max) return HAL_ERROR_RESULT_TOO_LONG; if (input == NULL) { if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, signature, &input_len, *signature_len)) != HAL_OK) return err; input = signature; } if ((err = pkcs1_5_pad(input, input_len, signature, *signature_len)) != HAL_OK || (err = hal_rsa_decrypt(NULL, key, signature, *signature_len, signature, *signature_len)) != HAL_OK) return err; return HAL_OK; } static hal_error_t pkey_local_sign_ecdsa(uint8_t *keybuf, const size_t keybuf_len, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * input, size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { hal_ecdsa_key_t *key = NULL; hal_error_t err; assert(signature != NULL && signature_len != NULL); assert((hash.handle == HAL_HANDLE_NONE) != (input == NULL || input_len == 0)); if ((err = hal_ecdsa_private_key_from_der(&key, keybuf, keybuf_len, der, der_len)) != HAL_OK) return err; if (input == NULL) { hal_digest_algorithm_t alg; if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || (err = hal_rpc_hash_get_digest_length(alg, &input_len)) != HAL_OK) return err; if (input_len > signature_max) return HAL_ERROR_RESULT_TOO_LONG; if ((err = hal_rpc_hash_finalize(hash, signature, input_len)) != HAL_OK) return err; input = signature; } if ((err = hal_ecdsa_sign(NULL, key, input, input_len, signature, signature_len, signature_max)) != HAL_OK) return err; return HAL_OK; } static hal_error_t pkey_local_sign(const hal_session_handle_t session, const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; hal_error_t (*signer)(uint8_t *keybuf, const size_t keybuf_len, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max); switch (slot->type) { case HAL_KEY_TYPE_RSA_PRIVATE: signer = pkey_local_sign_rsa; break; case HAL_KEY_TYPE_EC_PRIVATE: signer = pkey_local_sign_ecdsa; break; default: return HAL_ERROR_UNSUPPORTED_KEY; } uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err == HAL_OK) err = signer(keybuf, sizeof(keybuf), der, der_len, hash, input, input_len, signature, signature_len, signature_max); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); return err; } /* * Verify something using public key associated with handle. * * RSA has enough quirks that it's simplest to split this out into * algorithm-specific functions. */ static hal_error_t pkey_local_verify_rsa(uint8_t *keybuf, const size_t keybuf_len, const hal_key_type_t type, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * input, size_t input_len, const uint8_t * const signature, const size_t signature_len) { uint8_t expected[signature_len], received[(signature_len + 3) & ~3]; hal_rsa_key_t *key = NULL; hal_error_t err; assert(signature != NULL && signature_len > 0); assert((hash.handle == HAL_HANDLE_NONE) != (input == NULL || input_len == 0)); switch (type) { case HAL_KEY_TYPE_RSA_PRIVATE: err = hal_rsa_private_key_from_der(&key, keybuf, keybuf_len, der, der_len); break; case HAL_KEY_TYPE_RSA_PUBLIC: err = hal_rsa_public_key_from_der(&key, keybuf, keybuf_len, der, der_len); break; default: err = HAL_ERROR_IMPOSSIBLE; } if (err != HAL_OK) return err; if (input == NULL) { if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, expected, &input_len, sizeof(expected))) != HAL_OK) return err; input = expected; } if ((err = pkcs1_5_pad(input, input_len, expected, sizeof(expected))) != HAL_OK || (err = hal_rsa_encrypt(NULL, key, signature, signature_len, received, sizeof(received))) != HAL_OK) return err; unsigned diff = 0; for (int i = 0; i < signature_len; i++) diff |= expected[i] ^ received[i + sizeof(received) - sizeof(expected)]; if (diff != 0) return HAL_ERROR_INVALID_SIGNATURE; return HAL_OK; } static hal_error_t pkey_local_verify_ecdsa(uint8_t *keybuf, const size_t keybuf_len, const hal_key_type_t type, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * input, size_t input_len, const uint8_t * const signature, const size_t signature_len) { uint8_t digest[signature_len]; hal_ecdsa_key_t *key = NULL; hal_error_t err; assert(signature != NULL && signature_len > 0); assert((hash.handle == HAL_HANDLE_NONE) != (input == NULL || input_len == 0)); switch (type) { case HAL_KEY_TYPE_EC_PRIVATE: err = hal_ecdsa_private_key_from_der(&key, keybuf, keybuf_len, der, der_len); break; case HAL_KEY_TYPE_EC_PUBLIC: err = hal_ecdsa_public_key_from_der(&key, keybuf, keybuf_len, der, der_len); break; default: err = HAL_ERROR_IMPOSSIBLE; } if (err != HAL_OK) return err; if (input == NULL) { hal_digest_algorithm_t alg; if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || (err = hal_rpc_hash_get_digest_length(alg, &input_len)) != HAL_OK || (err = hal_rpc_hash_finalize(hash, digest, sizeof(digest))) != HAL_OK) return err; input = digest; } if ((err = hal_ecdsa_verify(NULL, key, input, input_len, signature, signature_len)) != HAL_OK) return err; return HAL_OK; } static hal_error_t pkey_local_verify(const hal_session_handle_t session, const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len) { hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; hal_error_t (*verifier)(uint8_t *keybuf, const size_t keybuf_len, const hal_key_type_t type, const uint8_t * const der, const size_t der_len, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len); switch (slot->type) { case HAL_KEY_TYPE_RSA_PRIVATE: case HAL_KEY_TYPE_RSA_PUBLIC: verifier = pkey_local_verify_rsa; break; case HAL_KEY_TYPE_EC_PRIVATE: case HAL_KEY_TYPE_EC_PUBLIC: verifier = pkey_local_verify_ecdsa; break; default: return HAL_ERROR_UNSUPPORTED_KEY; } uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); if (err == HAL_OK) err = verifier(keybuf, sizeof(keybuf), slot->type, der, der_len, hash, input, input_len, signature, signature_len); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); return err; } /* * List keys in the key store. */ static hal_error_t pkey_local_list(hal_pkey_info_t *result, unsigned *result_len, const unsigned result_max, hal_key_flags_t flags) { hal_ks_t *ks = NULL; hal_error_t err; if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && (err = hal_ks_list(ks, result, result_len, result_max)) == HAL_OK) err = hal_ks_close(ks); else if (ks != NULL) (void) hal_ks_close(ks); return err; } const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = { pkey_local_load, pkey_local_find, pkey_local_generate_rsa, pkey_local_generate_ec, pkey_local_close, pkey_local_delete, pkey_local_get_key_type, pkey_local_get_key_flags, pkey_local_get_public_key_len, pkey_local_get_public_key, pkey_local_sign, pkey_local_verify, pkey_local_list }; /* * Local variables: * indent-tabs-mode: nil * End: */ y h3 { font-size: 18px; } .markdown-body h4 { font-size: 16px; } .markdown-body h5 { font-size: 14px; } .markdown-body h6 { color: #777; font-size: 14px; } .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre { margin: 15px 0; } .markdown-body hr { background: transparent url("/dirty-shade.png") repeat-x 0 0; border: 0 none; color: #ccc; height: 4px; padding: 0; } .markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child { margin-top: 0; padding-top: 0; } .markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 { margin-top: 0; padding-top: 0; } .markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p { margin-top: 0; } .markdown-body li p.first { display: inline-block; } .markdown-body ul, .markdown-body ol { padding-left: 30px; } .markdown-body ul.no-list, .markdown-body ol.no-list { list-style-type: none; padding: 0; } .markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type { margin-top: 0px; } .markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type { margin-bottom: 0; } .markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul { margin-bottom: 0; } .markdown-body dl { padding: 0; } .markdown-body dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px; } .markdown-body dl dt:first-child { padding: 0; } .markdown-body dl dt>:first-child { margin-top: 0px; } .markdown-body dl dt>:last-child { margin-bottom: 0px; } .markdown-body dl dd { margin: 0 0 15px; padding: 0 15px; } .markdown-body dl dd>:first-child { margin-top: 0px; } .markdown-body dl dd>:last-child { margin-bottom: 0px; } .markdown-body blockquote { border-left: 4px solid #DDD; padding: 0 15px; color: #777; } .markdown-body blockquote>:first-child { margin-top: 0px; } .markdown-body blockquote>:last-child { margin-bottom: 0px; } .markdown-body table th { font-weight: bold; } .markdown-body table th, .markdown-body table td { border: 1px solid #ccc; padding: 6px 13px; } .markdown-body table tr { border-top: 1px solid #ccc; background-color: #fff; } .markdown-body table tr:nth-child(2n) { background-color: #f8f8f8; } .markdown-body img { max-width: 100%; -moz-box-sizing: border-box; box-sizing: border-box; } .markdown-body span.frame { display: block; overflow: hidden; } .markdown-body span.frame>span { border: 1px solid #ddd; display: block; float: left; overflow: hidden; margin: 13px 0 0; padding: 7px; width: auto; } .markdown-body span.frame span img { display: block; float: left; } .markdown-body span.frame span span { clear: both; color: #333; display: block; padding: 5px 0 0; } .markdown-body span.align-center { display: block; overflow: hidden; clear: both; } .markdown-body span.align-center>span { display: block; overflow: hidden; margin: 13px auto 0; text-align: center; } .markdown-body span.align-center span img { margin: 0 auto; text-align: center; } .markdown-body span.align-right { display: block; overflow: hidden; clear: both; } .markdown-body span.align-right>span { display: block; overflow: hidden; margin: 13px 0 0; text-align: right; } .markdown-body span.align-right span img { margin: 0; text-align: right; } .markdown-body span.float-left { display: block; margin-right: 13px; overflow: hidden; float: left; } .markdown-body span.float-left span { margin: 13px 0 0; } .markdown-body span.float-right { display: block; margin-left: 13px; overflow: hidden; float: right; } .markdown-body span.float-right>span { display: block; overflow: hidden; margin: 13px auto 0; text-align: right; } .markdown-body code, .markdown-body tt { margin: 0 2px; padding: 0px 5px; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; } .markdown-body code { white-space: nowrap; } .markdown-body pre>code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent; } .markdown-body .highlight pre, .markdown-body pre { background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } .markdown-body pre code, .markdown-body pre tt { margin: 0; padding: 0; background-color: transparent; border: none; } pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight { background: #ffffff; } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */

How to start using coretest_hashes on the TerasIC C5G Board

This is a writeup on how to setup, build and testrun the coretest_hashes Cryptech subsystem on a TerasiC C5G Cyclone V GX Starter Kit FPGA development board 1.

Introduction

Test Setup

The test setup consists of:

  • A development computer running the Altera Quartus II FPGA development software. This computer will be building the FPGA comfiguration image (a sof-file) and then use the Altera USB-blaster to load the image into the FPGA on the TerasIC board. This computer shall therefore be connected to the USB-blaster port on the TerasIC board.

  • A host computer that runs the hash_tester application that communicates with the FPGA design downloaded into the FPGA and perform tests on the hash functions. The host computer is connected to the USB-serial port on the TerasIC board.

  • The TerasIC Cyclone 5 GX Starter Kit (C5G) board.

The TerasIC Cyclone 5 GX Starter Kit board.

The USB ports are the shown in the upper left corner. These are USB type B ports. The port to the left is the USB-blaster port. The port to the right is the USB-serial port. In the bottom right corner there is a row of buttons and just above them 8 LEDs. These will also be used by the coretest_hashes subsystem. There is a HDMI port on the C5G board but it will not be used. All communication is done in CLI on the host computer.

NOTE: You don't actually need two separate computers. You can use one computer with one or two USB ports. If you only have one USB port you will need to switch from connecting to the USB-Blaster port to the USB-serial port on the C5G board once the coretest_hashes FPGA configuration has been downloaded to the board.

My personal setup is a laptop with two USB ports which allows me to have connections to both USB ports on the C5G boards simultaneously.

Coretest_hashes

The coretest_hashes is a subsystem that is a FPGA design that contains Cryptech application cores as well as support cores used to run tests of the SHA-1 and SHA-256 hash functions from a host computer via a serial interface connected to a FPGA device. The subsystem consists of:

  • [browser:/core/sha1 "sha1"]: A HW implementation of the SHA-1 hash function.

  • [browser:/core/sha256 "sha56"]: A HW implementation of the SHA-256 hash function.

  • [browser:/core/coretest "coretest"]: A command parser that accepts read/write commands from a

host, executes the commands and sends the response.

  • [browser:/core/usrt "uart"]: A simple serial interface that connects coretest to the host.

  • [browser:/core/coretest_hashes "coretest_hashes"]: A top level wrapper that connects all the cores as

well as connecting the rxd and txd ports on the uart to external pins as well as clk and reset. This core repo also contains the Python command line program hash_tester we will be using to talk to coretester and perform tests of the sha1 and sha256 cores.

coretest_hashes.png

The coretest_hashes subsystem with sha1 and sha256 cores. The system is connected to a host computer via a serial interface.

SW and system requirements

You need to download and install the Altera Quartus II Web Edition software 2. There are versions of Quartus II Web Edition for Windows and Linux. I'm using the Windows version, but Linux should work too.

You will probably also install drivers for the Altera USB-blaster in order to program the FPGA on the development board. For instructions on how to install the driver, please see the Altera page for USB-blaster 7.

For communication with the coretest_hashes in the FPGA we will be using the USB-serial device on the development board. The USB-serial chip on the board is a FTDI FT232R 3. If your host OS does not have support for this device you will need to install drivers. For Windows the correct file to download seems to be a VCP file 7.

Finally, in order to talk to coretest_hashes from the host there is application SW. This SW is written in Python and uses the Pyserial5 library. If you don't have Python and/or Pyserial installed you will need to install that too.

NOTE: Python and Pyserial does not have to be installed in the same OS as Quartus II but can be run from a separate system and OS.

(I'm using Quartus II 13.1 64-bit version running in Win8.1 in a VM in Parallels Desktop in OX 10.9.2 during this writeup. And I use Python and Pyserial in an iTerm in OSX for the serial communication.)

With all this SW installed you should be ready to proceed to create the coretest_hashes project.

I also recommend that you download the TerasIC C5G User Manual 4. It is a really good document that describes the boards with all functions, pins etc.

Downloading the cores

Create a project directory. I'm using test_coretest_hashes. In it I add a core directory and a toolruns directory:

#> ls test_coretest_hashes
cores/    toolruns/

The cores we need to build the subsystem must be downloaded from the Cryptech server. The cores we need are:

  • sha1
  • sha256
  • uart
  • coretest
  • coretest_hashes
#> cd cores
#> ssh git@git.cryptech.is
hello <FOO>, this is git@cryptech running gitolite3 v3.5.2-0-g926bd5f on git 1.9.0

 R   C  [a-zA-Z0-9].*
 R W    core/coretest
 R W    core/coretest_hashes
 R W    core/coretest_test_core
 R W    core/sha1
 R W    core/sha256
 R W    core/test_core
 R W    core/uart
 R W    doc/presentations
 R      gitolite-admin
Connection to cryptech.is closed.

We can see the relevant cores and check them out one by one:

#> git clone git@git.cryptech.is:core/sha1.git
#> git clone git@git.cryptech.is:core/sha256.git
#> git clone git@git.cryptech.is:core/uart.git
#> git clone git@git.cryptech.is:core/coretest.git
#> git clone git@git.cryptech.is:core/coretest_hashes.git

We should now have a cores directory like this:

#> ls
coretest/  coretest_hashes/  sha1/  sha256/  uart/

In each of these cores there are RTL and testbenches needed to simulate and build each of them. For example the sha1 core contains:

#> cd sha1
#> ls
LICENSE.txt  README.md  src/  toolruns/

The sha1 RTL source is in src/rtl. Lets take a look:

#> cd src/rtl
#> ls
sha1.v  sha1_core.v  sha1_w_mem.v

These files are:

  • sha1.v: A top level wrapper that provides an interface to the core. In

this case a 32-bit memory like interface.

  • sha1_core.v: The actual SHA-1 hash function core.

  • sha1_w_mem.v: The W memory including sliding window functionality used

by the core.

The other cores follows a similar pattern with a top level wrapper named .v, the main functionality in _core.v and then one or more submodules.

Creating the project in Quartus

  • Start Quartus and select file/new... and select New Quartus II

Project.

  • Select destination directory to be toolruns/ in your project

directory.

  • Set 'coretest_hashes' as name of the project

  • Set 'coretest_hashes' as nem of the top level design entity. (Should be

done automatically when entering the name of the project.)

  • Press next.

  • You should now be on the 'Add Files' page. Press '...'.

  • Navigate to test_coretest_hashes/cores/coretest/src/rtl.

  • Select coretest and press 'Open'. (Note: Quartus seems to sometimes omit the .v suffix

for the files depending on Windows/OS version.)

  • Back on the 'Add Files' page. Press Add to actually add coretest to

the project.

  • Press '...' again and navigate to the rtl directory in

coretest_hashes. Add it like you did with coretest.

  • Navigate to test_coretest_hashes/cores/sha1/src/rtl and add the files sha1, sha1_core,

sha1_w_mem. This time you don't need to press 'Add' on the 'Add Files'. It is done automatically when adding more than one file at a time.

  • Navigate to test_coretest_hashes/cores/sha256/src/rtl and add the files sha256, sha256_core,

sha256_k_constants, sha256_w_mem. Do NOT add the file wb_sha256. This file contains an alternative top level wrapper to the one in sha256.v that instead provides a "WISHBONE" interface. This interface is not used in the coretest_hashes design.

  • Finally navigate to test_coretest_hashes/cores/uart/src/rtl and add uart, uart_core.

Back on the 'Add Files page you should now see a list of source files:

../cores/uart/src/rtl/uart_core.v
../cores/uart/src/rtl/uart.v
../cores/sha256/src/rtl/sha256_w_mem.v
../cores/sha256/src/rtl/sha256_k_constants.v
../cores/sha256/src/rtl/sha256_core.v
../cores/sha256/src/rtl/sha256.v
../cores/sha1/src/rtl/sha1_w_mem.v
../cores/sha1/src/rtl/sha1_core.v
../cores/sha1/src/rtl/sha1.v
../cores/coretest_hashes/src/rtl/coretest_hashes.v
../cores/coretest/src/rtl/coretest.v

Press 'Next' to get to the 'Family & Device Settings' page.

  • In 'Device Family', 'Family' list select 'Cyclone V (E/GX/GT/SX/SE/ST)'.

  • In 'Device Family', 'Devices' list select 'Cyclone V GX Extended Features'

  • In the 'Available Devices' list select: 5CGXFC5C6F27C7.

Press 'Finish'.

Setting up and building the FPGA design

You should now be in the main Quartus II window. In the project navigator you can see all files, open the source files etc.

You could now just press 'Start Compilation' button in the menue row (the purple play/triangle button.) This will build the complete subsystem for the type of device selected. But the generated FPGA configuration image will not map to the correct pins on the C5G board. But this build should go through without errors or warnings related to problems in the source files. It is therefore a good test to see that all files has been included.

The result from this generic build should be a FPGA configuration that uses 3666 registers, 2846 ALMs, 12 pins and can run at 88.3 MHz in worst case temperature and timing.

You now need to define the correct pins and define the clock to allow Quartus to create a FPGA configuration for our board.

All pins needed are described in the C5G manual. To save time there is also a pin list available in the coretest_hashes directory.

  • Navigate to test_coretest_hashes/cores/coretest_hashes/toolruns/quartus/terasic_c5g

  • The file coretest_hashes.qsf contains assignments for a project like

the one we are setting up. It contains the pin assignments. The follwing list is a slightly cleaned up version of the pin assignments:

set_location_assignment PIN_R20 -to clk
set_location_assignment PIN_P11 -to reset_n
set_location_assignment PIN_M9 -to rxd
set_location_assignment PIN_L9 -to txd
set_location_assignment PIN_L7 -to debug[0]
set_location_assignment PIN_K6 -to debug[1]
set_location_assignment PIN_D8 -to debug[2]
set_location_assignment PIN_E9 -to debug[3]
set_location_assignment PIN_A5 -to debug[4]
set_location_assignment PIN_B6 -to debug[5]
set_location_assignment PIN_H8 -to debug[6]
set_location_assignment PIN_H9 -to debug[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk
set_instance_assignment -name IO_STANDARD "1.2 V" -to reset_n
set_instance_assignment -name IO_STANDARD "2.5 V" -to txd
set_instance_assignment -name IO_STANDARD "2.5 V" -to rxd
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[0]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[1]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[2]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[3]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[4]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[5]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[6]
set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[7]

As you can see, for each pin we want to use we need to define the actual pin in the FPGA (PIN_R20 for example) and the I/O standard the pin should use/support.

In this design I've mapped the reset signal to the button 'KEY0' on the board which you can find in the lower right corner. There is also a debug port that in the coretest_hashes design is connected to the debug port in the uart. This allows us to see byte values received by the uart. This debug port is connected to pins that control the green LEDs just above the row of buttons that includes 'KEY0'.

In order to enter the pin assignments select 'Assignments' in the Quartus top level menue bar. The select 'Assignment Editor'. Then either manually enter each of the assignments above. This will require two rows for each pin. For example for the clock ('clk') we would enter:

  • Row 1: 'To': clk, 'Assignment name': Location, 'Value': PIN_R20
  • Row 2: 'To': clk, 'Assignment name': I/O Standard, 'Value': 3.3-V LVTTL

An easier way is to open up the file coretest_hashes.qsf in test_coretest_hashes/cores/coretest_hashes/toolruns/quartus/terasic_c5g and add the pin assignment from that file to your qsf file in test_coretest_hashes/toolruns. If you then open up the Assignments Editor the same definitions should be shown.

We now need to define the clock. Under 'Assignments' in the top level menue select 'TimeQuest Timing Analyzer Wizard'. Press 'Next' to get from the 'Intro' page.

Under 'Specify base clock settings' enter 'clk' as 'Clock Name' and 'Input Pin'. Enter '20' in 'Period' and note that 'ns' is selected as time scale. In the 'Equivalent SDC Commands' you should see:

  create_clock -name "clk" -period 20.000ns [get_ports {clk}]

Now press 'Next' four times to get to the final page and then press 'Finish' to complete the clock setup. If we now look in the test_coretest_hashes/toolruns directory there should be a file called coretest_hashes.sdc that contains the SDC command above.

Now we are ready to build the real FPGA configuration. Press the purple 'Start Compilation' button again. After build we should now have an FPGA configuration that requires 2852 ALMs, 3666 registers, 12 pins and meets timing. The max clock frequency for the design should be about 72 MHz.

Time to load the design onto the board.

Configuring the FPGA on the C5G board

If you haven't turned on the C5G board and connected the board to the computer Quartus is installed on, do so now. You should see the 7-segment displays and LEDs start flashing in a simple sequence. This shows that the default configuration in the FPGA has been loaded and the board works.

In Quartus now locate the 'Programmer' menue button (it looks like a chip with waves). Alternatively Select 'Tools' in the top level Menue and then 'Programmer'.

In the Programmer window if everything is working magically we should see a list view with toolruns/output_files/coretest_hashes.sof selected. And below this list a graphic that shows a 'TDI' arrow pointing to an Altera 5CGXFC5C6F27C7 device with a 'TDO' going out from the device.

If the graphic is not showing (probably), you need to press 'Hardware Setup'. In the Window you should see 'USB-blaster'. If not you need to fix the drivers for the USB-blaster in your OS. If the USB-blaster is present make sure it is selected and then press 'Close'.

If the file is not showing, in the main Programmer window, select 'Add File' and navigate and to toolruns/output_files in the project directory. Select 'coretest_hashes.sof' and press 'Open'.

In the main Programmer window now press 'Start' to start programming. When this has been completed (See 'Progress' in the upper right hand corner in the Programmer board) the LEDs etc should have stopped blinking. We should now have coretest_hashes alive on the development board. Time for host communication and testing!

Talking to coretest_hashes and test of SHA-1 and SHA-256

There is a (currently rather ugly) test program for coretest_hashes. Navigate to test_coretest_hashes/cores/coretest_hashes/src/sw

#> ls
hash_tester.py

This is a Python2.x program that uses Pyserial 5 to open up a serial port and talk to coretest via the uart. The command and response format used is a very simple byte oriented format. For more info, see the README.md in [browser:/core/coretest "the top of coretest"].

The program hash_tester.py needs to know which serial interface to use. This is defined in the main() function (yes, VERY ugly). You will need to edit the program source to point to the serial interface connected to the USB-serial chip on the C5G board. For me that device is:

  ser.port='/dev/cu.usbserial-A801SA6T'

If everthing is working properly you should now just have to do:

  python hash_tester.py

If the communication has been set up properly you should now see:

  TC1-1: Reading name, type and version words from SHA-1 core.
  READ_OK. address 0x1000 = 0x73686131.
  READ_OK. address 0x1001 = 0x20202020.
  READ_OK. address 0x1002 = 0x302e3530.
  ...

That is the first test case that reads from specific registers in the SHA-1 core. If we look in sha1/src/rtl/sha1.v there are some defines:

  parameter CORE_NAME0     = 32'h73686131; // "sha1"
  parameter CORE_NAME1     = 32'h20202020; // "    "
  parameter CORE_VERSION   = 32'h302e3530; // "0.50"

As we can see those hex values matches what is being read from the FPGA and is the name and version strings in the core.

Moving on, hash_tester.py also performs single block message hash tests of both the SHA-1 and SHA-256 core. The message is "abc" padded to the correct block size for SHA-1 and SHA-256. These tests are defined by NIST including the expected result in 6. The block is written as a sequence of 32-bit words to addresses mapped to the block registers in the sha1 core.

Finally we set the init_flag in the control register in sha1 to one which should make the sha1 core initialize and then process the first (of possible several) message block. This takes in total 82 cycles for the core. This means that by the time the host gets the 'WRITE_OK. address 0x1008.' message, the core is done since many cycles ago. We therefore check status and try to extract the digest.

Looking at the output from hash_tester.py we see:

  TC1-3: Reading SHA-1 status and digest.
  READ_OK. address 0x1009 = 0x00000003.
  READ_OK. address 0x1020 = 0xa9993e36.
  READ_OK. address 0x1021 = 0x4706816a.
  READ_OK. address 0x1022 = 0xba3e2571.
  READ_OK. address 0x1023 = 0x7850c26c.
  READ_OK. address 0x1024 = 0x9cd0d89d.

Address 0x1009 corresponds to address 0x09 in the SHA-1 core. This address contains the status of the core. 0x03 means that the data in the digest is valid and that the core is ready to accept now commnands.

The digest generated by the sha1 core is in MSB format which means that the digest generated is:

  0xa9993e36 0x4706816a 0xba3e2571 0x7850c26c 0x9cd0d89d

If we compare that to the expected result in 6 we can see that this is correct. Similarly, for SHA-256 we get:

  TC2-3: Reading SHA-256 status and digest.
  READ_OK. address 0x2009 = 0x00000003.
  READ_OK. address 0x2020 = 0xba7816bf.
  READ_OK. address 0x2021 = 0x8f01cfea.
  READ_OK. address 0x2022 = 0x414140de.
  READ_OK. address 0x2023 = 0x5dae2223.
  READ_OK. address 0x2024 = 0xb00361a3.
  READ_OK. address 0x2025 = 0x96177a9c.
  READ_OK. address 0x2026 = 0xb410ff61.
  READ_OK. address 0x2027 = 0xf20015ad.

The digest generated is thus:

  0xba7816bf 0x8f01cfea 0x414140de 0x5dae2223
  0xb00361a3 0x96177a9c 0xb410ff61 0xf20015ad

Which again matches what is specified in 6

Summary

We have now set up a complete development and verification environment for Cryptech. We have setup and built the coretest_hashes subsystem for the TerasIC C5G board. Finally we have connected to coretest_hashes from SW in the host and verified that we can write to and receive response needed to perform SHA-1 and SHA-256 hash operations and get correct digest back.

If you have not been able to complete this, please contact me (Joachim Strömbergson).

Happy Hashing!

References