michael@0: /* michael@0: * crypto_kernel.c michael@0: * michael@0: * header for the cryptographic kernel michael@0: * michael@0: * David A. McGrew michael@0: * Cisco Systems, Inc. michael@0: */ michael@0: /* michael@0: * michael@0: * Copyright(c) 2001-2006 Cisco Systems, Inc. michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * michael@0: * Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * michael@0: * Redistributions in binary form must reproduce the above michael@0: * copyright notice, this list of conditions and the following michael@0: * disclaimer in the documentation and/or other materials provided michael@0: * with the distribution. michael@0: * michael@0: * Neither the name of the Cisco Systems, Inc. nor the names of its michael@0: * contributors may be used to endorse or promote products derived michael@0: * from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS michael@0: * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE michael@0: * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, michael@0: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES michael@0: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR michael@0: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) michael@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, michael@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) michael@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED michael@0: * OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: * michael@0: */ michael@0: michael@0: michael@0: #include "alloc.h" michael@0: michael@0: #include "crypto_kernel.h" michael@0: michael@0: /* the debug module for the crypto_kernel */ michael@0: michael@0: debug_module_t mod_crypto_kernel = { michael@0: 0, /* debugging is off by default */ michael@0: "crypto kernel" /* printable name for module */ michael@0: }; michael@0: michael@0: /* michael@0: * other debug modules that can be included in the kernel michael@0: */ michael@0: michael@0: extern debug_module_t mod_auth; michael@0: extern debug_module_t mod_cipher; michael@0: extern debug_module_t mod_stat; michael@0: extern debug_module_t mod_alloc; michael@0: michael@0: /* michael@0: * cipher types that can be included in the kernel michael@0: */ michael@0: michael@0: extern cipher_type_t null_cipher; michael@0: extern cipher_type_t aes_icm; michael@0: extern cipher_type_t aes_cbc; michael@0: michael@0: michael@0: /* michael@0: * auth func types that can be included in the kernel michael@0: */ michael@0: michael@0: extern auth_type_t null_auth; michael@0: extern auth_type_t hmac; michael@0: michael@0: /* crypto_kernel is a global variable, the only one of its datatype */ michael@0: michael@0: crypto_kernel_t michael@0: crypto_kernel = { michael@0: crypto_kernel_state_insecure, /* start off in insecure state */ michael@0: NULL, /* no cipher types yet */ michael@0: NULL, /* no auth types yet */ michael@0: NULL /* no debug modules yet */ michael@0: }; michael@0: michael@0: #define MAX_RNG_TRIALS 25 michael@0: michael@0: err_status_t michael@0: crypto_kernel_init() { michael@0: err_status_t status; michael@0: michael@0: /* check the security state */ michael@0: if (crypto_kernel.state == crypto_kernel_state_secure) { michael@0: michael@0: /* michael@0: * we're already in the secure state, but we've been asked to michael@0: * re-initialize, so we just re-run the self-tests and then return michael@0: */ michael@0: return crypto_kernel_status(); michael@0: } michael@0: michael@0: /* initialize error reporting system */ michael@0: status = err_reporting_init("crypto"); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* load debug modules */ michael@0: status = crypto_kernel_load_debug_module(&mod_crypto_kernel); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_debug_module(&mod_auth); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_debug_module(&mod_cipher); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_debug_module(&mod_stat); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_debug_module(&mod_alloc); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* initialize random number generator */ michael@0: status = rand_source_init(); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* run FIPS-140 statistical tests on rand_source */ michael@0: status = stat_test_rand_source_with_repetition(rand_source_get_octet_string, MAX_RNG_TRIALS); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* initialize pseudorandom number generator */ michael@0: status = ctr_prng_init(rand_source_get_octet_string); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* run FIPS-140 statistical tests on ctr_prng */ michael@0: status = stat_test_rand_source_with_repetition(ctr_prng_get_octet_string, MAX_RNG_TRIALS); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* load cipher types */ michael@0: status = crypto_kernel_load_cipher_type(&null_cipher, NULL_CIPHER); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_cipher_type(&aes_icm, AES_ICM); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_cipher_type(&aes_cbc, AES_CBC); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* load auth func types */ michael@0: status = crypto_kernel_load_auth_type(&null_auth, NULL_AUTH); michael@0: if (status) michael@0: return status; michael@0: status = crypto_kernel_load_auth_type(&hmac, HMAC_SHA1); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* change state to secure */ michael@0: crypto_kernel.state = crypto_kernel_state_secure; michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_status() { michael@0: err_status_t status; michael@0: kernel_cipher_type_t *ctype = crypto_kernel.cipher_type_list; michael@0: kernel_auth_type_t *atype = crypto_kernel.auth_type_list; michael@0: kernel_debug_module_t *dm = crypto_kernel.debug_module_list; michael@0: michael@0: /* run FIPS-140 statistical tests on rand_source */ michael@0: printf("testing rand_source..."); michael@0: status = stat_test_rand_source_with_repetition(rand_source_get_octet_string, MAX_RNG_TRIALS); michael@0: if (status) { michael@0: printf("failed\n"); michael@0: crypto_kernel.state = crypto_kernel_state_insecure; michael@0: return status; michael@0: } michael@0: printf("passed\n"); michael@0: michael@0: /* for each cipher type, describe and test */ michael@0: while(ctype != NULL) { michael@0: printf("cipher: %s\n", ctype->cipher_type->description); michael@0: printf(" instance count: %d\n", ctype->cipher_type->ref_count); michael@0: printf(" self-test: "); michael@0: status = cipher_type_self_test(ctype->cipher_type); michael@0: if (status) { michael@0: printf("failed with error code %d\n", status); michael@0: exit(status); michael@0: } michael@0: printf("passed\n"); michael@0: ctype = ctype->next; michael@0: } michael@0: michael@0: /* for each auth type, describe and test */ michael@0: while(atype != NULL) { michael@0: printf("auth func: %s\n", atype->auth_type->description); michael@0: printf(" instance count: %d\n", atype->auth_type->ref_count); michael@0: printf(" self-test: "); michael@0: status = auth_type_self_test(atype->auth_type); michael@0: if (status) { michael@0: printf("failed with error code %d\n", status); michael@0: exit(status); michael@0: } michael@0: printf("passed\n"); michael@0: atype = atype->next; michael@0: } michael@0: michael@0: /* describe each debug module */ michael@0: printf("debug modules loaded:\n"); michael@0: while (dm != NULL) { michael@0: printf(" %s ", dm->mod->name); michael@0: if (dm->mod->on) michael@0: printf("(on)\n"); michael@0: else michael@0: printf("(off)\n"); michael@0: dm = dm->next; michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_list_debug_modules() { michael@0: kernel_debug_module_t *dm = crypto_kernel.debug_module_list; michael@0: michael@0: /* describe each debug module */ michael@0: printf("debug modules loaded:\n"); michael@0: while (dm != NULL) { michael@0: printf(" %s ", dm->mod->name); michael@0: if (dm->mod->on) michael@0: printf("(on)\n"); michael@0: else michael@0: printf("(off)\n"); michael@0: dm = dm->next; michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_shutdown() { michael@0: err_status_t status; michael@0: michael@0: /* michael@0: * free dynamic memory used in crypto_kernel at present michael@0: */ michael@0: michael@0: /* walk down cipher type list, freeing memory */ michael@0: while (crypto_kernel.cipher_type_list != NULL) { michael@0: kernel_cipher_type_t *ctype = crypto_kernel.cipher_type_list; michael@0: crypto_kernel.cipher_type_list = ctype->next; michael@0: debug_print(mod_crypto_kernel, michael@0: "freeing memory for cipher %s", michael@0: ctype->cipher_type->description); michael@0: crypto_free(ctype); michael@0: } michael@0: michael@0: /* walk down authetication module list, freeing memory */ michael@0: while (crypto_kernel.auth_type_list != NULL) { michael@0: kernel_auth_type_t *atype = crypto_kernel.auth_type_list; michael@0: crypto_kernel.auth_type_list = atype->next; michael@0: debug_print(mod_crypto_kernel, michael@0: "freeing memory for authentication %s", michael@0: atype->auth_type->description); michael@0: crypto_free(atype); michael@0: } michael@0: michael@0: /* walk down debug module list, freeing memory */ michael@0: while (crypto_kernel.debug_module_list != NULL) { michael@0: kernel_debug_module_t *kdm = crypto_kernel.debug_module_list; michael@0: crypto_kernel.debug_module_list = kdm->next; michael@0: debug_print(mod_crypto_kernel, michael@0: "freeing memory for debug module %s", michael@0: kdm->mod->name); michael@0: crypto_free(kdm); michael@0: } michael@0: michael@0: /* de-initialize random number generator */ status = rand_source_deinit(); michael@0: if (status) michael@0: return status; michael@0: michael@0: /* return to insecure state */ michael@0: crypto_kernel.state = crypto_kernel_state_insecure; michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: static inline err_status_t michael@0: crypto_kernel_do_load_cipher_type(cipher_type_t *new_ct, cipher_type_id_t id, michael@0: int replace) { michael@0: kernel_cipher_type_t *ctype, *new_ctype; michael@0: err_status_t status; michael@0: michael@0: /* defensive coding */ michael@0: if (new_ct == NULL) michael@0: return err_status_bad_param; michael@0: michael@0: if (new_ct->id != id) michael@0: return err_status_bad_param; michael@0: michael@0: /* check cipher type by running self-test */ michael@0: status = cipher_type_self_test(new_ct); michael@0: if (status) { michael@0: return status; michael@0: } michael@0: michael@0: /* walk down list, checking if this type is in the list already */ michael@0: ctype = crypto_kernel.cipher_type_list; michael@0: while (ctype != NULL) { michael@0: if (id == ctype->id) { michael@0: if (!replace) michael@0: return err_status_bad_param; michael@0: status = cipher_type_test(new_ct, ctype->cipher_type->test_data); michael@0: if (status) michael@0: return status; michael@0: new_ctype = ctype; michael@0: break; michael@0: } michael@0: else if (new_ct == ctype->cipher_type) michael@0: return err_status_bad_param; michael@0: ctype = ctype->next; michael@0: } michael@0: michael@0: /* if not found, put new_ct at the head of the list */ michael@0: if (ctype == NULL) { michael@0: /* allocate memory */ michael@0: new_ctype = (kernel_cipher_type_t *) crypto_alloc(sizeof(kernel_cipher_type_t)); michael@0: if (new_ctype == NULL) michael@0: return err_status_alloc_fail; michael@0: new_ctype->next = crypto_kernel.cipher_type_list; michael@0: michael@0: /* set head of list to new cipher type */ michael@0: crypto_kernel.cipher_type_list = new_ctype; michael@0: } michael@0: michael@0: /* set fields */ michael@0: new_ctype->cipher_type = new_ct; michael@0: new_ctype->id = id; michael@0: michael@0: /* load debug module, if there is one present */ michael@0: if (new_ct->debug != NULL) michael@0: crypto_kernel_load_debug_module(new_ct->debug); michael@0: /* we could check for errors here */ michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_load_cipher_type(cipher_type_t *new_ct, cipher_type_id_t id) { michael@0: return crypto_kernel_do_load_cipher_type(new_ct, id, 0); michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_replace_cipher_type(cipher_type_t *new_ct, cipher_type_id_t id) { michael@0: return crypto_kernel_do_load_cipher_type(new_ct, id, 1); michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_do_load_auth_type(auth_type_t *new_at, auth_type_id_t id, michael@0: int replace) { michael@0: kernel_auth_type_t *atype, *new_atype; michael@0: err_status_t status; michael@0: michael@0: /* defensive coding */ michael@0: if (new_at == NULL) michael@0: return err_status_bad_param; michael@0: michael@0: if (new_at->id != id) michael@0: return err_status_bad_param; michael@0: michael@0: /* check auth type by running self-test */ michael@0: status = auth_type_self_test(new_at); michael@0: if (status) { michael@0: return status; michael@0: } michael@0: michael@0: /* walk down list, checking if this type is in the list already */ michael@0: atype = crypto_kernel.auth_type_list; michael@0: while (atype != NULL) { michael@0: if (id == atype->id) { michael@0: if (!replace) michael@0: return err_status_bad_param; michael@0: status = auth_type_test(new_at, atype->auth_type->test_data); michael@0: if (status) michael@0: return status; michael@0: new_atype = atype; michael@0: break; michael@0: } michael@0: else if (new_at == atype->auth_type) michael@0: return err_status_bad_param; michael@0: atype = atype->next; michael@0: } michael@0: michael@0: /* if not found, put new_at at the head of the list */ michael@0: if (atype == NULL) { michael@0: /* allocate memory */ michael@0: new_atype = (kernel_auth_type_t *)crypto_alloc(sizeof(kernel_auth_type_t)); michael@0: if (new_atype == NULL) michael@0: return err_status_alloc_fail; michael@0: michael@0: new_atype->next = crypto_kernel.auth_type_list; michael@0: /* set head of list to new auth type */ michael@0: crypto_kernel.auth_type_list = new_atype; michael@0: } michael@0: michael@0: /* set fields */ michael@0: new_atype->auth_type = new_at; michael@0: new_atype->id = id; michael@0: michael@0: /* load debug module, if there is one present */ michael@0: if (new_at->debug != NULL) michael@0: crypto_kernel_load_debug_module(new_at->debug); michael@0: /* we could check for errors here */ michael@0: michael@0: return err_status_ok; michael@0: michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_load_auth_type(auth_type_t *new_at, auth_type_id_t id) { michael@0: return crypto_kernel_do_load_auth_type(new_at, id, 0); michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_replace_auth_type(auth_type_t *new_at, auth_type_id_t id) { michael@0: return crypto_kernel_do_load_auth_type(new_at, id, 1); michael@0: } michael@0: michael@0: michael@0: cipher_type_t * michael@0: crypto_kernel_get_cipher_type(cipher_type_id_t id) { michael@0: kernel_cipher_type_t *ctype; michael@0: michael@0: /* walk down list, looking for id */ michael@0: ctype = crypto_kernel.cipher_type_list; michael@0: while (ctype != NULL) { michael@0: if (id == ctype->id) michael@0: return ctype->cipher_type; michael@0: ctype = ctype->next; michael@0: } michael@0: michael@0: /* haven't found the right one, indicate failure by returning NULL */ michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: err_status_t michael@0: crypto_kernel_alloc_cipher(cipher_type_id_t id, michael@0: cipher_pointer_t *cp, michael@0: int key_len) { michael@0: cipher_type_t *ct; michael@0: michael@0: /* michael@0: * if the crypto_kernel is not yet initialized, we refuse to allocate michael@0: * any ciphers - this is a bit extra-paranoid michael@0: */ michael@0: if (crypto_kernel.state != crypto_kernel_state_secure) michael@0: return err_status_init_fail; michael@0: michael@0: ct = crypto_kernel_get_cipher_type(id); michael@0: if (!ct) michael@0: return err_status_fail; michael@0: michael@0: return ((ct)->alloc(cp, key_len)); michael@0: } michael@0: michael@0: michael@0: michael@0: auth_type_t * michael@0: crypto_kernel_get_auth_type(auth_type_id_t id) { michael@0: kernel_auth_type_t *atype; michael@0: michael@0: /* walk down list, looking for id */ michael@0: atype = crypto_kernel.auth_type_list; michael@0: while (atype != NULL) { michael@0: if (id == atype->id) michael@0: return atype->auth_type; michael@0: atype = atype->next; michael@0: } michael@0: michael@0: /* haven't found the right one, indicate failure by returning NULL */ michael@0: return NULL; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_alloc_auth(auth_type_id_t id, michael@0: auth_pointer_t *ap, michael@0: int key_len, michael@0: int tag_len) { michael@0: auth_type_t *at; michael@0: michael@0: /* michael@0: * if the crypto_kernel is not yet initialized, we refuse to allocate michael@0: * any auth functions - this is a bit extra-paranoid michael@0: */ michael@0: if (crypto_kernel.state != crypto_kernel_state_secure) michael@0: return err_status_init_fail; michael@0: michael@0: at = crypto_kernel_get_auth_type(id); michael@0: if (!at) michael@0: return err_status_fail; michael@0: michael@0: return ((at)->alloc(ap, key_len, tag_len)); michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_load_debug_module(debug_module_t *new_dm) { michael@0: kernel_debug_module_t *kdm, *new; michael@0: michael@0: /* defensive coding */ michael@0: if (new_dm == NULL) michael@0: return err_status_bad_param; michael@0: michael@0: /* walk down list, checking if this type is in the list already */ michael@0: kdm = crypto_kernel.debug_module_list; michael@0: while (kdm != NULL) { michael@0: if (strncmp(new_dm->name, kdm->mod->name, 64) == 0) michael@0: return err_status_bad_param; michael@0: kdm = kdm->next; michael@0: } michael@0: michael@0: /* put new_dm at the head of the list */ michael@0: /* allocate memory */ michael@0: new = (kernel_debug_module_t *)crypto_alloc(sizeof(kernel_debug_module_t)); michael@0: if (new == NULL) michael@0: return err_status_alloc_fail; michael@0: michael@0: /* set fields */ michael@0: new->mod = new_dm; michael@0: new->next = crypto_kernel.debug_module_list; michael@0: michael@0: /* set head of list to new cipher type */ michael@0: crypto_kernel.debug_module_list = new; michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_kernel_set_debug_module(char *name, int on) { michael@0: kernel_debug_module_t *kdm; michael@0: michael@0: /* walk down list, checking if this type is in the list already */ michael@0: kdm = crypto_kernel.debug_module_list; michael@0: while (kdm != NULL) { michael@0: if (strncmp(name, kdm->mod->name, 64) == 0) { michael@0: kdm->mod->on = on; michael@0: return err_status_ok; michael@0: } michael@0: kdm = kdm->next; michael@0: } michael@0: michael@0: return err_status_fail; michael@0: } michael@0: michael@0: err_status_t michael@0: crypto_get_random(unsigned char *buffer, unsigned int length) { michael@0: if (crypto_kernel.state == crypto_kernel_state_secure) michael@0: return ctr_prng_get_octet_string(buffer, length); michael@0: else michael@0: return err_status_fail; michael@0: }