diff --git a/lib/gssapi/krb5/cfx.c b/lib/gssapi/krb5/cfx.c index 3a35facab5..6708bd06ae 100644 --- a/lib/gssapi/krb5/cfx.c +++ b/lib/gssapi/krb5/cfx.c @@ -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 @@ -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; @@ -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) @@ -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; @@ -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; @@ -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; @@ -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) { @@ -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; @@ -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++; @@ -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]; @@ -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 */ @@ -1405,7 +1398,7 @@ 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 */ @@ -1413,33 +1406,29 @@ OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status, 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); @@ -1553,7 +1542,7 @@ 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 */ @@ -1561,15 +1550,11 @@ OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status, 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; diff --git a/lib/krb5/crypto-aes-gcm.c b/lib/krb5/crypto-aes-gcm.c index 4f1c22f34a..fddcb16c5e 100644 --- a/lib/krb5/crypto-aes-gcm.c +++ b/lib/krb5/crypto-aes-gcm.c @@ -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 */ @@ -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); } } @@ -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, @@ -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, diff --git a/lib/krb5/sp800-108-kdf.c b/lib/krb5/sp800-108-kdf.c index 6c5b63bd19..e2030862b9 100755 --- a/lib/krb5/sp800-108-kdf.c +++ b/lib/krb5/sp800-108-kdf.c @@ -123,34 +123,21 @@ _krb5_SP800_108_KDF_CMAC(krb5_context context, unsigned int h = sizeof(mac); const size_t L = kdf_K0->length; const EVP_CIPHER *cipher; - size_t ckeylen; EVP_CIPHER_CTX c; int outlen; - unsigned char ivec[EVP_MAX_IV_LENGTH]; - size_t iveclen; EVP_CIPHER_CTX_init(&c); memset(mac, 0, sizeof(mac)); n = L / h; - /* AEAD keys may include extra 4 bytes that are injected into the IV */ - if (kdf_K1->length >= 32) + if (kdf_K1->length == 32) cipher = EVP_aes_256_ccm(); - else if (kdf_K1->length >= 16) + else if (kdf_K1->length == 16) cipher = EVP_aes_128_ccm(); else heim_assert(0, "Invalid K1 length passed to _krb5_SP800_108_KDF_CMAC"); - ckeylen = EVP_CIPHER_key_length(cipher); - heim_assert(kdf_K1->length >= ckeylen && - kdf_K1->length - ckeylen <= EVP_MAX_IV_LENGTH, - "Invalid extended K1 length passed to _krb5_SP800_108_KDF_CMAC"); - - iveclen = kdf_K1->length - ckeylen; - memcpy(ivec, (unsigned char *)kdf_K1->data + ckeylen, iveclen); - memset(&ivec[kdf_K1->length - ckeylen], 0, EVP_MAX_IV_LENGTH - iveclen); - for (i = 0; i <= n; i++) { unsigned char tmp[4]; size_t len; @@ -166,7 +153,7 @@ _krb5_SP800_108_KDF_CMAC(krb5_context context, EVP_CIPHER_CTX_ctrl(&c, EVP_CTRL_CCM_SET_TAG, 16, NULL) != 1) goto failure; - if (EVP_CipherInit_ex(&c, NULL, NULL, kdf_K1->data, ivec, 1) != 1 || + if (EVP_CipherInit_ex(&c, NULL, NULL, kdf_K1->data, NULL, 1) != 1 || EVP_CipherUpdate(&c, NULL, &outlen, NULL, 0) != 1) goto failure;