michael@0: /* michael@0: * xfm.c michael@0: * michael@0: * Crypto transform implementation 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: #include "cryptoalg.h" michael@0: #include "aes_cbc.h" michael@0: #include "hmac.h" michael@0: #include "crypto_kernel.h" /* for crypto_get_random() */ michael@0: michael@0: #define KEY_LEN 16 michael@0: #define ENC_KEY_LEN 16 michael@0: #define MAC_KEY_LEN 16 michael@0: #define IV_LEN 16 michael@0: #define TAG_LEN 12 michael@0: #define MAX_EXPAND 27 michael@0: michael@0: err_status_t michael@0: aes_128_cbc_hmac_sha1_96_func(void *key, michael@0: void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len, michael@0: void *auth_tag) { michael@0: aes_cbc_ctx_t aes_ctx; michael@0: hmac_ctx_t hmac_ctx; michael@0: unsigned char enc_key[ENC_KEY_LEN]; michael@0: unsigned char mac_key[MAC_KEY_LEN]; michael@0: err_status_t status; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: michael@0: /* derive encryption and authentication keys from the input key */ michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); michael@0: if (status) return status; michael@0: michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); michael@0: if (status) return status; michael@0: michael@0: michael@0: /* perform encryption and authentication */ michael@0: michael@0: /* set aes key */ michael@0: status = aes_cbc_context_init(&aes_ctx, key, ENC_KEY_LEN, direction_encrypt); michael@0: if (status) return status; michael@0: michael@0: /* set iv */ michael@0: status = crypto_get_random(iv, IV_LEN); michael@0: if (status) return status; michael@0: status = aes_cbc_set_iv(&aes_ctx, iv); michael@0: michael@0: /* encrypt the opaque data */ michael@0: status = aes_cbc_nist_encrypt(&aes_ctx, opaque, opaque_len); michael@0: if (status) return status; michael@0: michael@0: /* authenticate clear and opaque data */ michael@0: status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); michael@0: if (status) return status; michael@0: michael@0: status = hmac_start(&hmac_ctx); michael@0: if (status) return status; michael@0: michael@0: status = hmac_update(&hmac_ctx, clear, clear_len); michael@0: if (status) return status; michael@0: michael@0: status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, auth_tag); michael@0: if (status) return status; michael@0: michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: aes_128_cbc_hmac_sha1_96_inv(void *key, michael@0: void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len, michael@0: void *auth_tag) { michael@0: aes_cbc_ctx_t aes_ctx; michael@0: hmac_ctx_t hmac_ctx; michael@0: unsigned char enc_key[ENC_KEY_LEN]; michael@0: unsigned char mac_key[MAC_KEY_LEN]; michael@0: unsigned char tmp_tag[TAG_LEN]; michael@0: unsigned char *tag = auth_tag; michael@0: err_status_t status; michael@0: int i; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: michael@0: /* derive encryption and authentication keys from the input key */ michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); michael@0: if (status) return status; michael@0: michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); michael@0: if (status) return status; michael@0: michael@0: /* perform encryption and authentication */ michael@0: michael@0: /* set aes key */ michael@0: status = aes_cbc_context_init(&aes_ctx, key, ENC_KEY_LEN, direction_decrypt); michael@0: if (status) return status; michael@0: michael@0: /* set iv */ michael@0: status = rand_source_get_octet_string(iv, IV_LEN); michael@0: if (status) return status; michael@0: status = aes_cbc_set_iv(&aes_ctx, iv); michael@0: michael@0: /* encrypt the opaque data */ michael@0: status = aes_cbc_nist_decrypt(&aes_ctx, opaque, opaque_len); michael@0: if (status) return status; michael@0: michael@0: /* authenticate clear and opaque data */ michael@0: status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); michael@0: if (status) return status; michael@0: michael@0: status = hmac_start(&hmac_ctx); michael@0: if (status) return status; michael@0: michael@0: status = hmac_update(&hmac_ctx, clear, clear_len); michael@0: if (status) return status; michael@0: michael@0: status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, tmp_tag); michael@0: if (status) return status; michael@0: michael@0: /* compare the computed tag with the one provided as input */ michael@0: for (i=0; i < TAG_LEN; i++) michael@0: if (tmp_tag[i] != tag[i]) michael@0: return err_status_auth_fail; michael@0: michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: michael@0: #define ENC 1 michael@0: michael@0: #define DEBUG 0 michael@0: michael@0: err_status_t michael@0: aes_128_cbc_hmac_sha1_96_enc(void *key, michael@0: const void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len) { michael@0: aes_cbc_ctx_t aes_ctx; michael@0: hmac_ctx_t hmac_ctx; michael@0: unsigned char enc_key[ENC_KEY_LEN]; michael@0: unsigned char mac_key[MAC_KEY_LEN]; michael@0: unsigned char *auth_tag; michael@0: err_status_t status; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: michael@0: #if DEBUG michael@0: printf("ENC using key %s\n", octet_string_hex_string(key, KEY_LEN)); michael@0: #endif michael@0: michael@0: /* derive encryption and authentication keys from the input key */ michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); michael@0: if (status) return status; michael@0: michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); michael@0: if (status) return status; michael@0: michael@0: michael@0: /* perform encryption and authentication */ michael@0: michael@0: /* set aes key */ michael@0: status = aes_cbc_context_init(&aes_ctx, key, ENC_KEY_LEN, direction_encrypt); michael@0: if (status) return status; michael@0: michael@0: /* set iv */ michael@0: status = rand_source_get_octet_string(iv, IV_LEN); michael@0: if (status) return status; michael@0: status = aes_cbc_set_iv(&aes_ctx, iv); michael@0: if (status) return status; michael@0: michael@0: #if DEBUG michael@0: printf("plaintext len: %d\n", *opaque_len); michael@0: printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); michael@0: printf("plaintext: %s\n", octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: michael@0: #if ENC michael@0: /* encrypt the opaque data */ michael@0: status = aes_cbc_nist_encrypt(&aes_ctx, opaque, opaque_len); michael@0: if (status) return status; michael@0: #endif michael@0: michael@0: #if DEBUG michael@0: printf("ciphertext len: %d\n", *opaque_len); michael@0: printf("ciphertext: %s\n", octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: michael@0: /* michael@0: * authenticate clear and opaque data, then write the michael@0: * authentication tag to the location immediately following the michael@0: * ciphertext michael@0: */ michael@0: status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); michael@0: if (status) return status; michael@0: michael@0: status = hmac_start(&hmac_ctx); michael@0: if (status) return status; michael@0: michael@0: status = hmac_update(&hmac_ctx, clear, clear_len); michael@0: if (status) return status; michael@0: #if DEBUG michael@0: printf("hmac input: %s\n", michael@0: octet_string_hex_string(clear, clear_len)); michael@0: #endif michael@0: auth_tag = (unsigned char *)opaque; michael@0: auth_tag += *opaque_len; michael@0: status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, auth_tag); michael@0: if (status) return status; michael@0: #if DEBUG michael@0: printf("hmac input: %s\n", michael@0: octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: /* bump up the opaque_len to reflect the authentication tag */ michael@0: *opaque_len += TAG_LEN; michael@0: michael@0: #if DEBUG michael@0: printf("prot data len: %d\n", *opaque_len); michael@0: printf("prot data: %s\n", octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: aes_128_cbc_hmac_sha1_96_dec(void *key, michael@0: const void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len) { michael@0: aes_cbc_ctx_t aes_ctx; michael@0: hmac_ctx_t hmac_ctx; michael@0: unsigned char enc_key[ENC_KEY_LEN]; michael@0: unsigned char mac_key[MAC_KEY_LEN]; michael@0: unsigned char tmp_tag[TAG_LEN]; michael@0: unsigned char *auth_tag; michael@0: unsigned ciphertext_len; michael@0: err_status_t status; michael@0: int i; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: #if DEBUG michael@0: printf("DEC using key %s\n", octet_string_hex_string(key, KEY_LEN)); michael@0: #endif michael@0: michael@0: /* derive encryption and authentication keys from the input key */ michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); michael@0: if (status) return status; michael@0: michael@0: status = hmac_init(&hmac_ctx, key, KEY_LEN); michael@0: if (status) return status; michael@0: status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); michael@0: if (status) return status; michael@0: michael@0: #if DEBUG michael@0: printf("prot data len: %d\n", *opaque_len); michael@0: printf("prot data: %s\n", octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: michael@0: /* michael@0: * set the protected data length to that of the ciphertext, by michael@0: * subtracting out the length of the authentication tag michael@0: */ michael@0: ciphertext_len = *opaque_len - TAG_LEN; michael@0: michael@0: #if DEBUG michael@0: printf("ciphertext len: %d\n", ciphertext_len); michael@0: #endif michael@0: /* verify the authentication tag */ michael@0: michael@0: /* michael@0: * compute the authentication tag for the clear and opaque data, michael@0: * and write it to a temporary location michael@0: */ michael@0: status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); michael@0: if (status) return status; michael@0: michael@0: status = hmac_start(&hmac_ctx); michael@0: if (status) return status; michael@0: michael@0: status = hmac_update(&hmac_ctx, clear, clear_len); michael@0: if (status) return status; michael@0: michael@0: #if DEBUG michael@0: printf("hmac input: %s\n", michael@0: octet_string_hex_string(clear, clear_len)); michael@0: #endif michael@0: michael@0: status = hmac_compute(&hmac_ctx, opaque, ciphertext_len, TAG_LEN, tmp_tag); michael@0: if (status) return status; michael@0: michael@0: #if DEBUG michael@0: printf("hmac input: %s\n", michael@0: octet_string_hex_string(opaque, ciphertext_len)); michael@0: #endif michael@0: michael@0: /* michael@0: * compare the computed tag with the one provided as input (which michael@0: * immediately follows the ciphertext) michael@0: */ michael@0: auth_tag = (unsigned char *)opaque; michael@0: auth_tag += ciphertext_len; michael@0: #if DEBUG michael@0: printf("auth_tag: %s\n", octet_string_hex_string(auth_tag, TAG_LEN)); michael@0: printf("tmp_tag: %s\n", octet_string_hex_string(tmp_tag, TAG_LEN)); michael@0: #endif michael@0: for (i=0; i < TAG_LEN; i++) { michael@0: if (tmp_tag[i] != auth_tag[i]) michael@0: return err_status_auth_fail; michael@0: } michael@0: michael@0: /* bump down the opaque_len to reflect the authentication tag */ michael@0: *opaque_len -= TAG_LEN; michael@0: michael@0: /* decrypt the confidential data */ michael@0: status = aes_cbc_context_init(&aes_ctx, key, ENC_KEY_LEN, direction_decrypt); michael@0: if (status) return status; michael@0: status = aes_cbc_set_iv(&aes_ctx, iv); michael@0: if (status) return status; michael@0: michael@0: #if DEBUG michael@0: printf("ciphertext: %s\n", octet_string_hex_string(opaque, *opaque_len)); michael@0: printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); michael@0: #endif michael@0: michael@0: #if ENC michael@0: status = aes_cbc_nist_decrypt(&aes_ctx, opaque, &ciphertext_len); michael@0: if (status) return status; michael@0: #endif michael@0: michael@0: #if DEBUG michael@0: printf("plaintext len: %d\n", ciphertext_len); michael@0: printf("plaintext: %s\n", michael@0: octet_string_hex_string(opaque, ciphertext_len)); michael@0: #endif michael@0: michael@0: /* indicate the length of the plaintext */ michael@0: *opaque_len = ciphertext_len; michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: cryptoalg_ctx_t cryptoalg_ctx = { michael@0: aes_128_cbc_hmac_sha1_96_enc, michael@0: aes_128_cbc_hmac_sha1_96_dec, michael@0: KEY_LEN, michael@0: IV_LEN, michael@0: TAG_LEN, michael@0: MAX_EXPAND, michael@0: }; michael@0: michael@0: cryptoalg_t cryptoalg = &cryptoalg_ctx; michael@0: michael@0: #define NULL_TAG_LEN 12 michael@0: michael@0: err_status_t michael@0: null_enc(void *key, michael@0: const void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len) { michael@0: int i; michael@0: unsigned char *auth_tag; michael@0: unsigned char *init_vec = iv; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: michael@0: #if DEBUG michael@0: printf("NULL ENC using key %s\n", octet_string_hex_string(key, KEY_LEN)); michael@0: printf("NULL_TAG_LEN: %d\n", NULL_TAG_LEN); michael@0: printf("plaintext len: %d\n", *opaque_len); michael@0: #endif michael@0: for (i=0; i < IV_LEN; i++) michael@0: init_vec[i] = i + (i * 16); michael@0: #if DEBUG michael@0: printf("iv: %s\n", michael@0: octet_string_hex_string(iv, IV_LEN)); michael@0: printf("plaintext: %s\n", michael@0: octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: auth_tag = opaque; michael@0: auth_tag += *opaque_len; michael@0: for (i=0; i < NULL_TAG_LEN; i++) michael@0: auth_tag[i] = i + (i * 16); michael@0: *opaque_len += NULL_TAG_LEN; michael@0: #if DEBUG michael@0: printf("protected data len: %d\n", *opaque_len); michael@0: printf("protected data: %s\n", michael@0: octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: err_status_t michael@0: null_dec(void *key, michael@0: const void *clear, michael@0: unsigned clear_len, michael@0: void *iv, michael@0: void *opaque, michael@0: unsigned *opaque_len) { michael@0: unsigned char *auth_tag; michael@0: michael@0: /* check if we're doing authentication only */ michael@0: if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { michael@0: michael@0: /* perform authentication only */ michael@0: michael@0: } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { michael@0: michael@0: /* michael@0: * bad parameter - we expect either all three pointers to be NULL, michael@0: * or none of those pointers to be NULL michael@0: */ michael@0: return err_status_fail; michael@0: michael@0: } else { michael@0: michael@0: #if DEBUG michael@0: printf("NULL DEC using key %s\n", octet_string_hex_string(key, KEY_LEN)); michael@0: michael@0: printf("protected data len: %d\n", *opaque_len); michael@0: printf("protected data: %s\n", michael@0: octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: auth_tag = opaque; michael@0: auth_tag += (*opaque_len - NULL_TAG_LEN); michael@0: #if DEBUG michael@0: printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); michael@0: #endif michael@0: *opaque_len -= NULL_TAG_LEN; michael@0: #if DEBUG michael@0: printf("plaintext len: %d\n", *opaque_len); michael@0: printf("plaintext: %s\n", michael@0: octet_string_hex_string(opaque, *opaque_len)); michael@0: #endif michael@0: } michael@0: michael@0: return err_status_ok; michael@0: } michael@0: michael@0: cryptoalg_ctx_t null_cryptoalg_ctx = { michael@0: null_enc, michael@0: null_dec, michael@0: KEY_LEN, michael@0: IV_LEN, michael@0: NULL_TAG_LEN, michael@0: MAX_EXPAND, michael@0: }; michael@0: michael@0: cryptoalg_t null_cryptoalg = &null_cryptoalg_ctx; michael@0: michael@0: int michael@0: cryptoalg_get_id(cryptoalg_t c) { michael@0: if (c == cryptoalg) michael@0: return 1; michael@0: return 0; michael@0: } michael@0: michael@0: cryptoalg_t michael@0: cryptoalg_find_by_id(int id) { michael@0: switch(id) { michael@0: case 1: michael@0: return cryptoalg; michael@0: default: michael@0: break; michael@0: } michael@0: return 0; michael@0: }