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