From e50faea7f01738f4638e21f820c12619d64f0ee8 Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Mon, 14 May 2018 14:21:28 +0100 Subject: [PATCH] krb5: Store a digest context in the crypto structure Creating and destroying an EVP_CTX_MD structure with every hash operation is very expensive. Speed things up by caching one within the krb5_crypto structure. krb5_crypto can already only be safely used by one thread at a time - adding a message digest context here shouldn't introduce any further threading risks. Users of the stashed context must be careful to ensure that they call no other hash functions whilst they are in the middle of using the context. --- lib/krb5/crypto-aes-sha1.c | 2 +- lib/krb5/crypto-aes-sha2.c | 1 + lib/krb5/crypto-arcfour.c | 47 +++++++++++++++++++++--------------- lib/krb5/crypto-des-common.c | 4 ++- lib/krb5/crypto-des.c | 9 ++++++- lib/krb5/crypto-des3.c | 4 ++- lib/krb5/crypto-evp.c | 18 ++++++++++---- lib/krb5/crypto-null.c | 1 + lib/krb5/crypto.c | 29 +++++++++++++++------- lib/krb5/crypto.h | 20 +++++++++------ lib/krb5/pac.c | 3 ++- lib/krb5/test_rfc3961.c | 12 ++++++--- 12 files changed, 100 insertions(+), 50 deletions(-) diff --git a/lib/krb5/crypto-aes-sha1.c b/lib/krb5/crypto-aes-sha1.c index d07ad0e8b..bc7f0fc39 100644 --- a/lib/krb5/crypto-aes-sha1.c +++ b/lib/krb5/crypto-aes-sha1.c @@ -106,7 +106,7 @@ AES_SHA1_PRF(krb5_context context, iov[0].data = *in; iov[0].flags = KRB5_CRYPTO_TYPE_DATA; - ret = (*ct->checksum)(context, NULL, 0, iov, 1, &result); + ret = (*ct->checksum)(context, crypto, NULL, 0, iov, 1, &result); if (ret) { krb5_data_free(&result.checksum); return ret; diff --git a/lib/krb5/crypto-aes-sha2.c b/lib/krb5/crypto-aes-sha2.c index c7d8dc10d..78bbf025f 100644 --- a/lib/krb5/crypto-aes-sha2.c +++ b/lib/krb5/crypto-aes-sha2.c @@ -58,6 +58,7 @@ _krb5_aes_sha2_md_for_enctype(krb5_context context, static krb5_error_code SP_HMAC_SHA2_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, diff --git a/lib/krb5/crypto-arcfour.c b/lib/krb5/crypto-arcfour.c index dfc5d2f36..2a5d295a2 100644 --- a/lib/krb5/crypto-arcfour.c +++ b/lib/krb5/crypto-arcfour.c @@ -57,6 +57,7 @@ static struct _krb5_key_type keytype_arcfour = { krb5_error_code _krb5_HMAC_MD5_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -75,17 +76,22 @@ _krb5_HMAC_MD5_checksum(krb5_context context, krb5_error_code ret; int i; - m = EVP_MD_CTX_create(); - if (m == NULL) - return krb5_enomem(context); + if (crypto != NULL) { + if (crypto->mdctx == NULL) + crypto->mdctx = EVP_MD_CTX_create(); + if (crypto->mdctx == NULL) + return krb5_enomem(context); + m = crypto->mdctx; + } else + m = EVP_MD_CTX_create(); + ksign_c.checksum.length = sizeof(ksign_c_data); ksign_c.checksum.data = ksign_c_data; - ret = _krb5_internal_hmac(context, c, signature, sizeof(signature), + ret = _krb5_internal_hmac(context, crypto, c, signature, sizeof(signature), 0, key, &ksign_c); - if (ret) { - EVP_MD_CTX_destroy(m); - return ret; - } + if (ret) + goto out; + ksign.key = &kb; kb.keyvalue = ksign_c.checksum; EVP_DigestInit_ex(m, EVP_md5(), NULL); @@ -99,12 +105,13 @@ _krb5_HMAC_MD5_checksum(krb5_context context, EVP_DigestUpdate(m, iov[i].data.data, iov[i].data.length); } EVP_DigestFinal_ex (m, tmp, NULL); - EVP_MD_CTX_destroy(m); - ret = _krb5_internal_hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result); - if (ret) - return ret; - return 0; + ret = _krb5_internal_hmac(context, crypto, c, tmp, sizeof(tmp), 0, &ksign, result); +out: + if (crypto == NULL) + EVP_MD_CTX_destroy(m); + + return ret; } struct _krb5_checksum_type _krb5_checksum_hmac_md5 = { @@ -153,7 +160,7 @@ ARCFOUR_subencrypt(krb5_context context, k1_c.checksum.length = sizeof(k1_c_data); k1_c.checksum.data = k1_c_data; - ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c); + ret = _krb5_internal_hmac(context, NULL, c, t, sizeof(t), 0, key, &k1_c); if (ret) krb5_abortx(context, "hmac failed"); @@ -168,7 +175,7 @@ ARCFOUR_subencrypt(krb5_context context, cksum.checksum.length = 16; cksum.checksum.data = data; - ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum); + ret = _krb5_internal_hmac(context, NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); if (ret) krb5_abortx(context, "hmac failed"); @@ -178,7 +185,7 @@ ARCFOUR_subencrypt(krb5_context context, k3_c.checksum.length = sizeof(k3_c_data); k3_c.checksum.data = k3_c_data; - ret = _krb5_internal_hmac(context, c, data, 16, 0, &ke, &k3_c); + ret = _krb5_internal_hmac(context, NULL, c, data, 16, 0, &ke, &k3_c); if (ret) krb5_abortx(context, "hmac failed"); @@ -225,7 +232,7 @@ ARCFOUR_subdecrypt(krb5_context context, k1_c.checksum.length = sizeof(k1_c_data); k1_c.checksum.data = k1_c_data; - ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c); + ret = _krb5_internal_hmac(context, NULL, c, t, sizeof(t), 0, key, &k1_c); if (ret) krb5_abortx(context, "hmac failed"); @@ -240,7 +247,7 @@ ARCFOUR_subdecrypt(krb5_context context, k3_c.checksum.length = sizeof(k3_c_data); k3_c.checksum.data = k3_c_data; - ret = _krb5_internal_hmac(context, c, cdata, 16, 0, &ke, &k3_c); + ret = _krb5_internal_hmac(context, NULL, c, cdata, 16, 0, &ke, &k3_c); if (ret) krb5_abortx(context, "hmac failed"); @@ -255,7 +262,7 @@ ARCFOUR_subdecrypt(krb5_context context, cksum.checksum.length = 16; cksum.checksum.data = cksum_data; - ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum); + ret = _krb5_internal_hmac(context, NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); if (ret) krb5_abortx(context, "hmac failed"); @@ -336,7 +343,7 @@ ARCFOUR_prf(krb5_context context, res.checksum.data = out->data; res.checksum.length = out->length; - ret = _krb5_internal_hmac(context, c, in->data, in->length, 0, &crypto->key, &res); + ret = _krb5_internal_hmac(context, crypto, c, in->data, in->length, 0, &crypto->key, &res); if (ret) krb5_data_free(out); return 0; diff --git a/lib/krb5/crypto-des-common.c b/lib/krb5/crypto-des-common.c index b6d765066..a8344ae5b 100644 --- a/lib/krb5/crypto-des-common.c +++ b/lib/krb5/crypto-des-common.c @@ -133,13 +133,15 @@ _krb5_des_verify(krb5_context context, static krb5_error_code RSA_MD5_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, int niov, Checksum *C) { - if (_krb5_evp_digest_iov(iov, niov, C->checksum.data, NULL, EVP_md5(), NULL) != 1) + if (_krb5_evp_digest_iov(crypto, iov, niov, C->checksum.data, + NULL, EVP_md5(), NULL) != 1) krb5_abortx(context, "md5 checksum failed"); return 0; diff --git a/lib/krb5/crypto-des.c b/lib/krb5/crypto-des.c index 152c01550..fbb7c347b 100644 --- a/lib/krb5/crypto-des.c +++ b/lib/krb5/crypto-des.c @@ -98,6 +98,7 @@ static struct _krb5_key_type keytype_des = { static krb5_error_code CRC32_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -124,19 +125,22 @@ CRC32_checksum(krb5_context context, static krb5_error_code RSA_MD4_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, int niov, Checksum *C) { - if (_krb5_evp_digest_iov(iov, niov, C->checksum.data, NULL, EVP_md4(), NULL) != 1) + if (_krb5_evp_digest_iov(crypto, iov, niov, C->checksum.data, + NULL, EVP_md4(), NULL) != 1) krb5_abortx(context, "md4 checksum failed"); return 0; } static krb5_error_code RSA_MD4_DES_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -148,6 +152,7 @@ RSA_MD4_DES_checksum(krb5_context context, static krb5_error_code RSA_MD4_DES_verify(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -159,6 +164,7 @@ RSA_MD4_DES_verify(krb5_context context, static krb5_error_code RSA_MD5_DES_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -170,6 +176,7 @@ RSA_MD5_DES_checksum(krb5_context context, static krb5_error_code RSA_MD5_DES_verify(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, diff --git a/lib/krb5/crypto-des3.c b/lib/krb5/crypto-des3.c index 98faed853..9b8853db9 100644 --- a/lib/krb5/crypto-des3.c +++ b/lib/krb5/crypto-des3.c @@ -73,7 +73,7 @@ DES3_prf(krb5_context context, iov[0].data = *in; iov[0].flags = KRB5_CRYPTO_TYPE_DATA; - ret = (*ct->checksum)(context, NULL, 0, iov, 1, &result); + ret = (*ct->checksum)(context, crypto, NULL, 0, iov, 1, &result); if (ret) { krb5_data_free(&result.checksum); return ret; @@ -142,6 +142,7 @@ static struct _krb5_key_type keytype_des3_derived = { #ifdef DES3_OLD_ENCTYPE static krb5_error_code RSA_MD5_DES3_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -153,6 +154,7 @@ RSA_MD5_DES3_checksum(krb5_context context, static krb5_error_code RSA_MD5_DES3_verify(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, diff --git a/lib/krb5/crypto-evp.c b/lib/krb5/crypto-evp.c index 387e5672f..594614910 100644 --- a/lib/krb5/crypto-evp.c +++ b/lib/krb5/crypto-evp.c @@ -57,7 +57,8 @@ _krb5_evp_cleanup(krb5_context context, struct _krb5_key_data *kd) } int -_krb5_evp_digest_iov(const struct krb5_crypto_iov *iov, +_krb5_evp_digest_iov(krb5_crypto crypto, + const struct krb5_crypto_iov *iov, int niov, void *hash, unsigned int *hsize, @@ -68,9 +69,14 @@ _krb5_evp_digest_iov(const struct krb5_crypto_iov *iov, int ret, i; krb5_data current = {0,0}; - ctx = EVP_MD_CTX_create(); - if (ctx == NULL) - return 0; + if (crypto != NULL) { + if (crypto->mdctx == NULL) + crypto->mdctx = EVP_MD_CTX_create(); + if (crypto->mdctx == NULL) + return 0; + ctx = crypto->mdctx; + } else + ctx = EVP_MD_CTX_create(); ret = EVP_DigestInit_ex(ctx, md, engine); if (ret != 1) @@ -100,7 +106,9 @@ _krb5_evp_digest_iov(const struct krb5_crypto_iov *iov, ret = EVP_DigestFinal_ex(ctx, hash, hsize); out: - EVP_MD_CTX_destroy(ctx); + if (crypto == NULL) + EVP_MD_CTX_destroy(ctx); + return ret; } diff --git a/lib/krb5/crypto-null.c b/lib/krb5/crypto-null.c index 8aca50239..4e5934282 100644 --- a/lib/krb5/crypto-null.c +++ b/lib/krb5/crypto-null.c @@ -53,6 +53,7 @@ static struct _krb5_key_type keytype_null = { static krb5_error_code NONE_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, diff --git a/lib/krb5/crypto.c b/lib/krb5/crypto.c index 1ed9fac83..4df2a12a4 100644 --- a/lib/krb5/crypto.c +++ b/lib/krb5/crypto.c @@ -168,14 +168,17 @@ _key_schedule(krb5_context context, static krb5_error_code SHA1_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, int niov, Checksum *C) { - if (_krb5_evp_digest_iov(iov, niov, C->checksum.data, - NULL, EVP_sha1(), NULL) != 1) + if (_krb5_evp_digest_iov(crypto, + iov, niov, + C->checksum.data, NULL, + EVP_sha1(), NULL) != 1) krb5_abortx(context, "sha1 checksum failed"); return 0; @@ -184,6 +187,7 @@ SHA1_checksum(krb5_context context, /* HMAC according to RFC2104 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_internal_hmac_iov(krb5_context context, + krb5_crypto crypto, struct _krb5_checksum_type *cm, unsigned usage, const struct krb5_crypto_iov *iov, @@ -221,6 +225,7 @@ _krb5_internal_hmac_iov(krb5_context context, working[0].data = keyblock->key->keyvalue; working[0].flags = KRB5_CRYPTO_TYPE_DATA; (*cm->checksum)(context, + crypto, keyblock, usage, working, @@ -243,14 +248,14 @@ _krb5_internal_hmac_iov(krb5_context context, for (i = 0; i < niov; i++) working[i + 1] = iov[i]; - (*cm->checksum)(context, keyblock, usage, working, niov + 1, result); + (*cm->checksum)(context, crypto, keyblock, usage, working, niov + 1, result); memcpy(opad + cm->blocksize, result->checksum.data, result->checksum.length); working[0].data.data = opad; working[0].data.length = cm->blocksize + cm->checksumsize; working[0].flags = KRB5_CRYPTO_TYPE_DATA; - (*cm->checksum)(context, keyblock, usage, working, 1, result); + (*cm->checksum)(context, crypto, keyblock, usage, working, 1, result); memset(ipad, 0, cm->blocksize); free(ipad); memset(opad, 0, cm->blocksize + cm->checksumsize); @@ -262,6 +267,7 @@ _krb5_internal_hmac_iov(krb5_context context, KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_internal_hmac(krb5_context context, + krb5_crypto crypto, struct _krb5_checksum_type *cm, const void *data, size_t len, @@ -274,7 +280,7 @@ _krb5_internal_hmac(krb5_context context, iov[0].data.data = (void *) data; iov[0].data.length = len; iov[0].flags = KRB5_CRYPTO_TYPE_DATA; - return _krb5_internal_hmac_iov(context, cm, usage, iov, 1, + return _krb5_internal_hmac_iov(context, crypto, cm, usage, iov, 1, keyblock, result); } @@ -302,7 +308,7 @@ krb5_hmac(krb5_context context, kd.key = key; kd.schedule = NULL; - ret = _krb5_internal_hmac(context, c, data, len, usage, &kd, result); + ret = _krb5_internal_hmac(context, NULL, c, data, len, usage, &kd, result); if (kd.schedule) krb5_free_data(context, kd.schedule); @@ -312,6 +318,7 @@ krb5_hmac(krb5_context context, krb5_error_code _krb5_SP_HMAC_SHA1_checksum(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, @@ -425,7 +432,7 @@ create_checksum_iov(krb5_context context, result->cksumtype = ct->type; - return (*ct->checksum)(context, dkey, usage, iov, niov, result); + return (*ct->checksum)(context, crypto, dkey, usage, iov, niov, result); } static krb5_error_code @@ -539,7 +546,7 @@ verify_checksum_iov(krb5_context context, */ if(ct->verify) { - ret = (*ct->verify)(context, dkey, usage, iov, niov, cksum); + ret = (*ct->verify)(context, crypto, dkey, usage, iov, niov, cksum); if (ret) krb5_set_error_message(context, ret, N_("Decrypt integrity check failed for checksum " @@ -552,7 +559,7 @@ verify_checksum_iov(krb5_context context, if (ret) return ret; - ret = (*ct->checksum)(context, dkey, usage, iov, niov, &c); + ret = (*ct->checksum)(context, crypto, dkey, usage, iov, niov, &c); if (ret) { krb5_data_free(&c.checksum); return ret; @@ -2515,6 +2522,10 @@ krb5_crypto_destroy(krb5_context context, free_key_usage(context, &crypto->key_usage[i], crypto->et); free(crypto->key_usage); _krb5_free_key_data(context, &crypto->key, crypto->et); + + if (crypto->mdctx) + EVP_MD_CTX_destroy(crypto->mdctx); + free (crypto); return 0; } diff --git a/lib/krb5/crypto.h b/lib/krb5/crypto.h index 1480e5ea4..908443b3a 100644 --- a/lib/krb5/crypto.h +++ b/lib/krb5/crypto.h @@ -42,13 +42,6 @@ struct _krb5_key_data { struct _krb5_key_usage; -struct krb5_crypto_data { - struct _krb5_encryption_type *et; - struct _krb5_key_data key; - int num_key_usage; - struct _krb5_key_usage *key_usage; -}; - #define CRYPTO_ETYPE(C) ((C)->et->type) /* bits for `flags' below */ @@ -97,11 +90,13 @@ struct _krb5_checksum_type { size_t checksumsize; unsigned flags; krb5_error_code (*checksum)(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, int niov, Checksum *csum); krb5_error_code (*verify)(krb5_context context, + krb5_crypto crypto, struct _krb5_key_data *key, unsigned usage, const struct krb5_crypto_iov *iov, int niov, @@ -200,9 +195,18 @@ _krb5_crypto_iov_should_sign(const struct krb5_crypto_iov *iov) struct _krb5_evp_schedule { /* * Normally we'd say EVP_CIPHER_CTX here, but! this header gets - * included in lib/krb5/pkinit-ec.ck + * included in lib/krb5/pkinit-ec.c */ EVP_CIPHER_CTX ectx; EVP_CIPHER_CTX dctx; }; + +struct krb5_crypto_data { + struct _krb5_encryption_type *et; + struct _krb5_key_data key; + EVP_MD_CTX *mdctx; + int num_key_usage; + struct _krb5_key_usage *key_usage; +}; + #endif diff --git a/lib/krb5/pac.c b/lib/krb5/pac.c index 1788b59bd..853a84b70 100644 --- a/lib/krb5/pac.c +++ b/lib/krb5/pac.c @@ -108,7 +108,8 @@ HMAC_MD5_any_checksum(krb5_context context, iov.data.length = len; iov.flags = KRB5_CRYPTO_TYPE_DATA; - ret = _krb5_HMAC_MD5_checksum(context, &local_key, usage, &iov, 1, result); + ret = _krb5_HMAC_MD5_checksum(context, NULL, &local_key, usage, &iov, 1, + result); if (ret) krb5_data_free(&result->checksum); diff --git a/lib/krb5/test_rfc3961.c b/lib/krb5/test_rfc3961.c index 3f85e0333..f1255948f 100644 --- a/lib/krb5/test_rfc3961.c +++ b/lib/krb5/test_rfc3961.c @@ -85,6 +85,7 @@ time_hmac_evp(krb5_context context, size_t size, int iterations) struct _krb5_key_data kd; krb5_error_code ret; krb5_keyblock key; + krb5_crypto crypto; char sha1_data[20]; Checksum result; char *buf; @@ -110,9 +111,14 @@ time_hmac_evp(krb5_context context, size_t size, int iterations) kd.key = &key; kd.schedule = NULL; + ret = krb5_crypto_init(context, &key, ETYPE_AES128_CTS_HMAC_SHA1_96, + &crypto); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_init"); + for (i = 0; i < iterations; i++) { - ret = _krb5_SP_HMAC_SHA1_checksum(context, &kd, 0, - &iov, 1, &result); + ret = _krb5_SP_HMAC_SHA1_checksum(context, crypto, &kd, 0, + &iov, 1, &result); if (ret) krb5_err(context, 1, ret, "hmac: %d", i); } @@ -413,7 +419,7 @@ test_rfc2202(krb5_context context) iov.flags = KRB5_CRYPTO_TYPE_DATA; kd.key = &keyblock; kd.schedule = NULL; - code = _krb5_SP_HMAC_SHA1_checksum(context, &kd, 0, + code = _krb5_SP_HMAC_SHA1_checksum(context, NULL, &kd, 0, &iov, 1, &result); if (code != 0)