------------------------------------------------------------------------
r23734 | ghudson | 2010-02-18 13:04:47 -0500 (Thu, 18 Feb 2010) | 17 lines
ticket: 6665
subject: Fix cipher state chaining in OpenSSL back end
target_version: 1.8
tags: pullup
Make cipher state chaining work in the OpenSSL back end for des, des3,
and arcfour enc providers. Subtleties:
* DES and DES3 have checks to avoid clobbering ivec with uninitialized
data if there is no data to encrypt.
* Arcfour saves the OpenSSL cipher context across calls. To protect
against a caller improperly copying the state (which happens to work
with other enc providers), a loopback pointer is used, as in GSSAPI.
* EVP_EncryptFinal_ex is unnecessary with stream ciphers and would
interfere with cipher state chaining if it did anything, so just
remove it.
ticket: 6665
version_fixed: 1.8
status: resolved
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-8@23746
dc483132-0cff-0310-8789-
dd5450dbe970
#define DES_KEY_BYTES 7
static krb5_error_code
-validate(krb5_key key, const krb5_data *ivec,
- const krb5_crypto_iov *data, size_t num_data)
+validate(krb5_key key, const krb5_data *ivec, const krb5_crypto_iov *data,
+ size_t num_data, krb5_boolean *empty)
{
size_t i, input_length;
if (ivec && (ivec->length != 8))
return(KRB5_BAD_MSIZE);
+ *empty = (input_length == 0);
return 0;
}
unsigned char iblock[MIT_DES_BLOCK_LENGTH], oblock[MIT_DES_BLOCK_LENGTH];
struct iov_block_state input_pos, output_pos;
EVP_CIPHER_CTX ciph_ctx;
+ krb5_boolean empty;
IOV_BLOCK_STATE_INIT(&input_pos);
IOV_BLOCK_STATE_INIT(&output_pos);
-
- ret = validate(key, ivec, data, num_data);
- if (ret)
+ ret = validate(key, ivec, data, num_data, &empty);
+ if (ret != 0 || empty)
return ret;
EVP_CIPHER_CTX_init(&ciph_ctx);
&output_pos);
}
+ if (ivec != NULL)
+ memcpy(ivec->data, oblock, MIT_DES_BLOCK_LENGTH);
+
EVP_CIPHER_CTX_cleanup(&ciph_ctx);
zap(iblock, sizeof(iblock));
unsigned char iblock[MIT_DES_BLOCK_LENGTH], oblock[MIT_DES_BLOCK_LENGTH];
struct iov_block_state input_pos, output_pos;
EVP_CIPHER_CTX ciph_ctx;
+ krb5_boolean empty;
IOV_BLOCK_STATE_INIT(&input_pos);
IOV_BLOCK_STATE_INIT(&output_pos);
- ret = validate(key, ivec, data, num_data);
- if (ret)
+ ret = validate(key, ivec, data, num_data, &empty);
+ if (ret != 0 || empty)
return ret;
EVP_CIPHER_CTX_init(&ciph_ctx);
MIT_DES_BLOCK_LENGTH, &output_pos);
}
+ if (ivec != NULL)
+ memcpy(ivec->data, iblock, MIT_DES_BLOCK_LENGTH);
+
EVP_CIPHER_CTX_cleanup(&ciph_ctx);
zap(iblock, sizeof(iblock));
#define DES_BLOCK_SIZE 8
static krb5_error_code
-validate(krb5_key key, const krb5_data *ivec,
- const krb5_crypto_iov *data, size_t num_data)
+validate(krb5_key key, const krb5_data *ivec, const krb5_crypto_iov *data,
+ size_t num_data, krb5_boolean *empty)
{
size_t i, input_length;
if (ivec && (ivec->length != 8))
return(KRB5_BAD_MSIZE);
+ *empty = (input_length == 0);
return 0;
}
unsigned char iblock[MIT_DES_BLOCK_LENGTH], oblock[MIT_DES_BLOCK_LENGTH];
struct iov_block_state input_pos, output_pos;
EVP_CIPHER_CTX ciph_ctx;
+ krb5_boolean empty;
- ret = validate(key, ivec, data, num_data);
- if (ret)
+ ret = validate(key, ivec, data, num_data, &empty);
+ if (ret != 0 || empty)
return ret;
IOV_BLOCK_STATE_INIT(&input_pos);
oblock, MIT_DES_BLOCK_LENGTH, &output_pos);
}
- /*if (ivec != NULL && ivec->data)
- memcpy(ivec->data, oblock, MIT_DES_BLOCK_LENGTH); */
+ if (ivec != NULL)
+ memcpy(ivec->data, oblock, MIT_DES_BLOCK_LENGTH);
EVP_CIPHER_CTX_cleanup(&ciph_ctx);
unsigned char iblock[MIT_DES_BLOCK_LENGTH], oblock[MIT_DES_BLOCK_LENGTH];
struct iov_block_state input_pos, output_pos;
EVP_CIPHER_CTX ciph_ctx;
+ krb5_boolean empty;
- ret = validate(key, ivec, data, num_data);
- if (ret)
+ ret = validate(key, ivec, data, num_data, &empty);
+ if (ret != 0 || empty)
return ret;
IOV_BLOCK_STATE_INIT(&input_pos);
&output_pos);
}
- /*if (ivec != NULL && ivec->data)
- memcpy(ivec->data, oblock, MIT_DES_BLOCK_LENGTH); */
+ if (ivec != NULL)
+ memcpy(ivec->data, iblock, MIT_DES_BLOCK_LENGTH);
EVP_CIPHER_CTX_cleanup(&ciph_ctx);
#include <rand2key.h>
#include <openssl/evp.h>
-typedef struct
-{
- EVP_CIPHER_CTX evp_ctx;
- unsigned int x;
- unsigned int y;
- unsigned char state[256];
-
-} ArcfourContext;
-
-typedef struct {
- int initialized;
- ArcfourContext ctx;
-} ArcFourCipherState;
+/*
+ * The loopback field is NULL if ctx is uninitialized (no encrypt or decrypt
+ * operation has taken place), or a pointer to the structure address if ctx is
+ * initialized. If the application copies the state (not a valid operation,
+ * but one which happens to works with some other enc providers), we can detect
+ * it via the loopback field and return a sane error code.
+ */
+struct arcfour_state {
+ struct arcfour_state *loopback;
+ EVP_CIPHER_CTX ctx;
+};
#define RC4_KEY_SIZE 16
#define RC4_BLOCK_SIZE 1
size_t num_data)
{
size_t i;
- int ret = 0, tmp_len = 0;
- unsigned char *tmp_buf = NULL;
+ int ret = 1, tmp_len = 0;
krb5_crypto_iov *iov = NULL;
- EVP_CIPHER_CTX ciph_ctx;
-
-
- EVP_CIPHER_CTX_init(&ciph_ctx);
-
- ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_rc4(), NULL, key->keyblock.contents, NULL);
- if (!ret){
- EVP_CIPHER_CTX_cleanup(&ciph_ctx);
- return KRB5_CRYPTO_INTERNAL;
+ EVP_CIPHER_CTX ciph_ctx, *ctx;
+ struct arcfour_state *arcstate;
+ krb5_boolean do_init = TRUE;
+
+ arcstate = (state != NULL) ? (struct arcfour_state *) state->data : NULL;
+ if (arcstate != NULL) {
+ ctx = &arcstate->ctx;
+ if (arcstate->loopback == arcstate)
+ do_init = FALSE;
+ else if (arcstate->loopback != NULL)
+ return KRB5_CRYPTO_INTERNAL;
+ } else {
+ ctx = &ciph_ctx;
+ }
+ if (do_init) {
+ EVP_CIPHER_CTX_init(ctx);
+ ret = EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, key->keyblock.contents,
+ NULL);
+ if (!ret)
+ return KRB5_CRYPTO_INTERNAL;
}
for (i = 0; i < num_data; i++) {
iov = &data[i];
- if (iov->data.length <= 0) break;
- tmp_len = iov->data.length;
-
if (ENCRYPT_IOV(iov)) {
- tmp_buf=(unsigned char *)iov->data.data;
- ret = EVP_EncryptUpdate(&ciph_ctx,
- tmp_buf, &tmp_len,
- (unsigned char *)iov->data.data, iov->data.length);
- if (!ret) break;
- iov->data.length = tmp_len;
+ ret = EVP_EncryptUpdate(ctx,
+ (unsigned char *) iov->data.data, &tmp_len,
+ (unsigned char *) iov->data.data,
+ iov->data.length);
+ if (!ret)
+ break;
}
}
- if(ret)
- ret = EVP_EncryptFinal_ex(&ciph_ctx, (unsigned char *)tmp_buf, &tmp_len);
- EVP_CIPHER_CTX_cleanup(&ciph_ctx);
+ if (arcstate) /* Context is saved; mark as initialized. */
+ arcstate->loopback = arcstate;
+ else /* Context is not saved; clean it up now. */
+ EVP_CIPHER_CTX_cleanup(ctx);
- if (ret != 1)
+ if (!ret)
return KRB5_CRYPTO_INTERNAL;
- iov->data.length += tmp_len;
-
return 0;
}
static krb5_error_code
k5_arcfour_free_state ( krb5_data *state)
{
- return 0; /* not implemented */
+ struct arcfour_state *arcstate = (struct arcfour_state *) state->data;
+
+ /* Clean up the OpenSSL context if it was initialized. */
+ if (arcstate && arcstate->loopback == arcstate)
+ EVP_CIPHER_CTX_cleanup(&arcstate->ctx);
+ free(arcstate);
+ return 0;
}
static krb5_error_code
k5_arcfour_init_state (const krb5_keyblock *key,
krb5_keyusage keyusage, krb5_data *new_state)
{
- return 0; /* not implemented */
-
+ struct arcfour_state *arcstate;
+
+ /* Create a state structure with an uninitialized context. */
+ arcstate = calloc(1, sizeof(*arcstate));
+ if (arcstate == NULL)
+ return ENOMEM;
+ arcstate->loopback = NULL;
+ new_state->data = (char *) arcstate;
+ new_state->length = sizeof(*arcstate);
+ return 0;
}
/* Since the arcfour cipher is identical going forwards and backwards,
k5_arcfour_docrypt,
NULL,
krb5int_arcfour_make_key,
- k5_arcfour_init_state, /*xxx not implemented */
- k5_arcfour_free_state /*xxx not implemented */
+ k5_arcfour_init_state,
+ k5_arcfour_free_state
};