Skip to content

Commit

Permalink
Revert "salt IV with last 4 bytes of key, make key longer"
Browse files Browse the repository at this point in the history
This reverts commit 6f6c869.
  • Loading branch information
lhoward committed Dec 17, 2015
1 parent 7c5dc50 commit c15e5ff
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 129 deletions.
153 changes: 69 additions & 84 deletions lib/gssapi/krb5/cfx.c
Expand Up @@ -216,29 +216,25 @@ _gk_verify_buffers(OM_uint32 *minor_status,
}

static void
init_aead_ivec(const void *token, void *ivec)
init_aead_ivec(void *token, void *ivec)
{
const gss_cfx_mic_token ttoken = (const gss_cfx_mic_token)token;
unsigned char *p = token;

memset(ivec, 0, EVP_MAX_IV_LENGTH);

/*
* GCM initialization vector looks like:
*
* Last 4 bytes of derived key || Direction Bit || SND_SEQ || Counter
* TOK_ID || Flags || 0xFF || SND_SEQ || Counter
*
* The counter is not exposed at the RFC3169 level.
*
* Note that this protects all of the token header fields except
* for EC, so we are responsible for asserting it is the correct
* value elsewhere.
*/
memcpy(ivec, ttoken->SND_SEQ, 8);

/*
* High bit indicates direction, this does not overlap with
* SND_SEQ as we only have 32-bit sequence numbers.
*/
if (ttoken->Flags & CFXSentByAcceptor)
((uint8_t *)ivec)[0] |= 0x80;
else
((uint8_t *)ivec)[0] &= ~(0x80);
memcpy(ivec, p, 4);
memcpy((uint8_t *)ivec + 4, p + 8, 8);
}

OM_uint32
Expand Down Expand Up @@ -452,7 +448,7 @@ _gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
++seq_number);
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);

data = calloc(iov_count + 4, sizeof(data[0]));
data = calloc(iov_count + 3, sizeof(data[0]));
if (data == NULL) {
*minor_status = ENOMEM;
major_status = GSS_S_FAILURE;
Expand Down Expand Up @@ -504,34 +500,24 @@ _gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
* encrypted token header is always at the end of the
* ciphertext.
*/

/* encrypted CFX header in trailer (or after the header if in
DCE mode). Copy in header into E"header"
*/
data[i].flags = KRB5_CRYPTO_TYPE_DATA;
if (trailer)
data[i].data.data = trailer->buffer.value;
else
data[i].data.data = (uint8_t *)header->buffer.value + sizeof(*token);
data[i].data.length = ec;
data[i].data.length = ec + etsize;
memset(data[i].data.data, 0xFF, ec);
i++;

/* encrypted CFX header in trailer (or after the header if in
DCE mode). Copy in header into E"header". For AEAD, this is
signed only.
*/
if (ctx->more_flags & AEAD) {
data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[i].data.data = token;
} else {
data[i].flags = KRB5_CRYPTO_TYPE_DATA;
data[i].data.data = (uint8_t *)data[i - 1].data.data + ec;
memcpy(data[i].data.data, token, sizeof(*token));
}
data[i].data.length = sizeof(*token);
memcpy(((uint8_t *)data[i].data.data) + ec, token, etsize);
i++;

/* Kerberos trailer comes after the gss trailer */
data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[i].data.data = (uint8_t *)data[i - 1].data.data + ec + etsize;
data[i].data.length = k5tsize;
data[i].data.data = (uint8_t *)data[i - 2].data.data + ec + etsize;
i++;

if (ctx->more_flags & AEAD)
Expand All @@ -544,6 +530,7 @@ _gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
major_status = GSS_S_FAILURE;
goto failure;
}

if (rrc) {
token->RRC[0] = (rrc >> 8) & 0xFF;
token->RRC[1] = (rrc >> 0) & 0xFF;
Expand Down Expand Up @@ -705,7 +692,6 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
OM_uint32 seq_number_lo, seq_number_hi, major_status, junk;
gss_iov_buffer_desc *header, *trailer, *padding;
gss_cfx_wrap_token token;
gss_cfx_wrap_token_desc etoken;
u_char token_flags;
krb5_error_code ret;
unsigned usage;
Expand Down Expand Up @@ -799,7 +785,7 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
usage = KRB5_KU_USAGE_INITIATOR_SEAL;
}

data = calloc(iov_count + 4, sizeof(data[0]));
data = calloc(iov_count + 3, sizeof(data[0]));
if (data == NULL) {
*minor_status = ENOMEM;
major_status = GSS_S_FAILURE;
Expand All @@ -826,6 +812,17 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
goto failure;
}

/* AEAD types don't protect EC, so assert it is correct constant value */
if (ctx->more_flags & AEAD) {
size_t required_ec = (token_flags & CFXSealed) ? 0 : k5tsize;

if (ec != required_ec) {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto failure;
}
ec = 0; /* don't mess up any other calculations */
}

/* Rotate by RRC; bogus to do this in-place XXX */
/* Check RRC */
if (trailer == NULL) {
Expand All @@ -837,7 +834,7 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
goto failure;
}

if (IS_DCE_STYLE(ctx) && !(ctx->more_flags & AEAD))
if (IS_DCE_STYLE(ctx))
gsstsize += ec;

gsshsize += gsstsize;
Expand Down Expand Up @@ -883,41 +880,23 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
data[i].data.data = iov[j].buffer.value;
}

/* EC bytes */
if (trailer)
/* encrypted CFX header in trailer (or after the header if in
DCE mode). Copy in header into E"header" (for non-AEAD modes).
For AEAD modes, this is just the EC bytes (if any).
*/
data[i].flags = KRB5_CRYPTO_TYPE_DATA;
if (trailer) {
data[i].data.data = trailer->buffer.value;
else
data[i].data.data = (uint8_t *)header->buffer.value + sizeof(*token);
if (token_flags & CFXSealed) {
data[i].flags = KRB5_CRYPTO_TYPE_DATA;
data[i].data.length = ec;
} else {
data[i].flags = KRB5_CRYPTO_TYPE_EMPTY; /* for AEAD MIC path */
data[i].data.length = 0;
}
i++;

/* Encrypted or signed token header */
if (ctx->more_flags & AEAD) {
memcpy(&etoken, token, sizeof(etoken));
etoken.RRC[0] = etoken.RRC[1] = 0;
if ((token_flags & CFXSealed) == 0)
etoken.EC[0] = etoken.EC[1] = 0;

data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[i].data.data = &etoken;
} else {
data[i].flags = KRB5_CRYPTO_TYPE_DATA;
data[i].data.data = (uint8_t *)data[i - 1].data.data +
data[i - 1].data.length;
data[i].data.data = (uint8_t *)header->buffer.value + sizeof(*token);
}
data[i].data.length = sizeof(*token);
data[i].data.length = ec + etsize;
i++;

/* Kerberos trailer comes after the gss trailer */
data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[i].data.data = (uint8_t *)data[i - 2].data.data +
data[i - 2].data.length + etsize;
data[i].data.data = ((uint8_t *)data[i-1].data.data) +
data[i-1].data.length;
data[i].data.length = k5tsize;
i++;

Expand All @@ -935,7 +914,7 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
if ((ctx->more_flags & AEAD) == 0) {
gss_cfx_wrap_token ttoken;

ttoken = (gss_cfx_wrap_token)((uint8_t *)data[i - 2].data.data);
ttoken = (gss_cfx_wrap_token)(((uint8_t *)data[i - 2].data.data) + ec);
ttoken->RRC[0] = token->RRC[0];
ttoken->RRC[1] = token->RRC[1];

Expand All @@ -949,7 +928,21 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
size_t gsstsize;
size_t gsshsize = sizeof(*token);

gsstsize = ec;
if (ctx->more_flags & AEAD) {
/* EC is not protected, but it is a constant value */
*minor_status = krb5_crypto_length(context, ctx->crypto,
KRB5_CRYPTO_TYPE_TRAILER, &gsstsize);
if (*minor_status != 0) {
major_status = GSS_S_FAILURE;
goto failure;
}

if (gsstsize != ec) {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto failure;
}
} else
gsstsize = ec;

if (trailer == NULL) {
/* Check RRC */
Expand Down Expand Up @@ -1405,41 +1398,37 @@ OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
}

if (ctx->more_flags & AEAD) {
krb5_crypto_iov data[3];
krb5_crypto_iov data[2];
uint8_t ivec[EVP_MAX_IV_LENGTH];

/* XXX assuming header is zero-length */
data[0].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[0].data.length = message_buffer->length;
data[0].data.data = message_buffer->value;

data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[1].data.length = sizeof(*token);
data[1].data.data = token;

ret = krb5_crypto_length(context, ctx->crypto,
KRB5_CRYPTO_TYPE_TRAILER,
&data[2].data.length);
&data[1].data.length);
if (ret) {
*minor_status = ret;
free(buf);
return GSS_S_FAILURE;
}

data[2].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[2].data.data = malloc(data[2].data.length);
if (data[2].data.data == NULL) {
data[1].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[1].data.data = malloc(data[1].data.length);
if (data[1].data.data == NULL) {
*minor_status = ENOMEM;
free(buf);
return GSS_S_FAILURE;
}

init_aead_ivec(token, ivec);
ret = krb5_encrypt_iov_ivec(context, ctx->crypto, usage, data, 3, ivec);
ret = krb5_encrypt_iov_ivec(context, ctx->crypto, usage, data, 2, ivec);
if (ret)
krb5_data_free(&data[2].data);
krb5_data_free(&data[1].data);
else
cksum.checksum = data[2].data;
cksum.checksum = data[1].data;
} else {
ret = krb5_create_checksum(context, ctx->crypto,
usage, 0, buf, len, &cksum);
Expand Down Expand Up @@ -1553,23 +1542,19 @@ OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
}

if (ctx->more_flags & AEAD) {
krb5_crypto_iov data[3];
krb5_crypto_iov data[2];
uint8_t ivec[EVP_MAX_IV_LENGTH];

/* XXX assuming header is zero-length */
data[0].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[0].data.length = message_buffer->length;
data[0].data.data = message_buffer->value;

data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
data[1].data.length = sizeof(*token);
data[1].data.data = token;

data[2].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[2].data = cksum.checksum;
data[1].flags = KRB5_CRYPTO_TYPE_TRAILER;
data[1].data = cksum.checksum;

init_aead_ivec(token, ivec);
ret = krb5_decrypt_iov_ivec(context, ctx->crypto, usage, data, 3, ivec);
ret = krb5_decrypt_iov_ivec(context, ctx->crypto, usage, data, 2, ivec);
if (ret) {
*minor_status = ret;
return GSS_S_BAD_MIC;
Expand Down
45 changes: 16 additions & 29 deletions lib/krb5/crypto-aes-gcm.c
Expand Up @@ -50,10 +50,10 @@ _krb5_evp_encrypt_gcm(krb5_context context,
int usage,
void *ivec)
{
const size_t ivecsz = 12;
struct _krb5_evp_schedule *ctx = key->schedule->data;
EVP_CIPHER_CTX *c;
krb5_boolean preludep;
krb5_data *keydata = &key->key->keyvalue;

c = encryptp ? &ctx->ectx : &ctx->dctx;
preludep = !!data ^ encryptp; /* is being called before encrypt/decrypt */
Expand All @@ -62,38 +62,25 @@ _krb5_evp_encrypt_gcm(krb5_context context,
return KRB5_PROG_ETYPE_NOSUPP; /* XXX */

if (preludep) {
unsigned char nonce[12];

heim_assert(key->key->keyvalue.length >= 4, "Invalid GCM key length");
memcpy(nonce, (unsigned char *)keydata->data + keydata->length - 4, 4);
memcpy(&nonce[4], ivec, sizeof(nonce) - 4);

/*
* OpenSSL API doesn't let you set the IV without generating a
* new one unless you use EVP_CTRL_GCM_SET_IV_FIXED with a length
* of -1. So we set a fake IV len to let us copy in the "fixed"
* component independently from the invocation.
*/
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IVLEN, 12, NULL);
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IV_FIXED, -1, nonce);
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IVLEN, ivecsz, NULL);
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IV_FIXED, -1, ivec);
if (encryptp) {
/* Copy IV from caller and update */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_IV_GEN, 8, ivec);
/* Copy in/out IV from caller (nonce or chained cipherstate) */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_IV_GEN, ivecsz, ivec);
} else {
/* Copy (but do not update) IV and tag from caller */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IV_INV, 8, ivec);
/* Copy in IV from caller without incrementing counter */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_IV_INV, ivecsz, ivec);
/* Copy in tag for verification */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_SET_TAG, len, data);
}

memset_s(nonce, sizeof(nonce), 0, sizeof(nonce));
} else {
/* Copy out ivec to caller, if cipherstate chaining required */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_IV_GEN, ivecsz, ivec);

/* Copy out tag to caller */
if (encryptp) {
/* Copy tag to caller */
if (EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_GET_TAG, len, data) != 1)
return KRB5_CRYPTO_INTERNAL;
} else {
/* Copy updated IV to caller */
EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_GCM_IV_GEN, 8, ivec);
}
}

Expand All @@ -103,8 +90,8 @@ _krb5_evp_encrypt_gcm(krb5_context context,
static struct _krb5_key_type keytype_aes128_gcm = {
KRB5_ENCTYPE_AES128_GCM_128,
"aes-128-gcm",
160,
20, /* extra 4 bytes for nonce salt */
128,
16,
sizeof(struct _krb5_evp_schedule),
NULL,
_krb5_evp_schedule,
Expand All @@ -117,8 +104,8 @@ static struct _krb5_key_type keytype_aes128_gcm = {
static struct _krb5_key_type keytype_aes256_gcm = {
KRB5_ENCTYPE_AES256_GCM_128,
"aes-256-gcm",
288,
36, /* extra 4 bytes for nonce salt */
256,
32,
sizeof(struct _krb5_evp_schedule),
NULL,
_krb5_evp_schedule,
Expand Down

0 comments on commit c15e5ff

Please sign in to comment.