Blame SOURCES/0024-use-encrypted-JWTs-for-storing-encrypted-cache-conte.patch

bef612
From 3c4949e1436473644836f0c4634b809c7526c43a Mon Sep 17 00:00:00 2001
bef612
From: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
bef612
Date: Thu, 10 Jun 2021 15:32:48 +0200
bef612
Subject: [PATCH 1/3] use encrypted JWTs for storing encrypted cache contents
bef612
bef612
- avoid using static AAD/IV; thanks @niebardzo
bef612
- bump to 2.4.9-dev
bef612
bef612
Signed-off-by: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
bef612
---
bef612
 ChangeLog                 |   4 +
bef612
 src/cache/common.c        | 331 ++++----------------------------------
bef612
bef612
diff --git a/ChangeLog b/ChangeLog
bef612
index 075f98d..b4ab0a1 100644
bef612
--- a/ChangeLog
bef612
+++ b/ChangeLog
bef612
@@ -1,3 +1,7 @@
bef612
+06/10/2021
bef612
+- use encrypted JWTs for storing encrypted cache contents and avoid using static AAD/IV; thanks @niebardzo
bef612
+- bump to 2.4.9-dev
bef612
+
bef612
 09/03/2020
bef612
 - add SameSite attribute on cookie clearance / logout; thanks @v0gler
bef612
 - bump to 2.4.4.1
bef612
diff --git a/src/cache/common.c b/src/cache/common.c
bef612
index c33876a..e665370 100644
bef612
--- a/src/cache/common.c
bef612
+++ b/src/cache/common.c
bef612
@@ -229,324 +229,59 @@ apr_byte_t oidc_cache_mutex_destroy(server_rec *s, oidc_cache_mutex_t *m) {
bef612
 	return rv;
bef612
 }
bef612
 
bef612
-#define oidc_cache_crypto_openssl_error(r, fmt, ...) \
bef612
-		oidc_error(r, "%s: %s", apr_psprintf(r->pool, fmt, ##__VA_ARGS__), ERR_error_string(ERR_get_error(), NULL))
bef612
-
bef612
-#define OIDC_CACHE_CIPHER							EVP_aes_256_gcm()
bef612
-#define OIDC_CACHE_TAG_LEN							16
bef612
-
bef612
-#if (OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER))
bef612
-#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_AEAD_GET_TAG
bef612
-#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_AEAD_SET_TAG
bef612
-#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_AEAD_SET_IVLEN
bef612
-#else
bef612
-#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_GCM_GET_TAG
bef612
-#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_GCM_SET_TAG
bef612
-#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_GCM_SET_IVLEN
bef612
-#endif
bef612
+#define OIDC_CACHE_CRYPTO_JSON_KEY "c"
bef612
 
bef612
 /*
bef612
- * AES GCM encrypt
bef612
+ * AES GCM encrypt using the crypto passphrase as symmetric key
bef612
  */
bef612
-static int oidc_cache_crypto_encrypt_impl(request_rec *r,
bef612
-		unsigned char *plaintext, int plaintext_len, const unsigned char *aad,
bef612
-		int aad_len, unsigned char *key, const unsigned char *iv, int iv_len,
bef612
-		unsigned char *ciphertext, const unsigned char *tag, int tag_len) {
bef612
-	EVP_CIPHER_CTX *ctx;
bef612
-
bef612
-	int len;
bef612
-
bef612
-	int ciphertext_len;
bef612
-
bef612
-	/* create and initialize the context */
bef612
-	if (!(ctx = EVP_CIPHER_CTX_new())) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* initialize the encryption cipher */
bef612
-	if (!EVP_EncryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* set IV length */
bef612
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* initialize key and IV */
bef612
-	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* provide AAD data */
bef612
-	if (!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
bef612
-				aad_len);
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* provide the message to be encrypted and obtain the encrypted output */
bef612
-	if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptUpdate ciphertext");
bef612
-		return -1;
bef612
-	}
bef612
-	ciphertext_len = len;
bef612
-
bef612
-	/*
bef612
-	 * finalize the encryption; normally ciphertext bytes may be written at
bef612
-	 * this stage, but this does not occur in GCM mode
bef612
-	 */
bef612
-	if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptFinal_ex");
bef612
-		return -1;
bef612
-	}
bef612
-	ciphertext_len += len;
bef612
-
bef612
-	/* get the tag */
bef612
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_GET_TAG, tag_len,
bef612
-			(void *) tag)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* clean up */
bef612
-	EVP_CIPHER_CTX_free(ctx);
bef612
-
bef612
-	return ciphertext_len;
bef612
-}
bef612
-
bef612
-/*
bef612
- * AES GCM decrypt
bef612
- */
bef612
-static int oidc_cache_crypto_decrypt_impl(request_rec *r,
bef612
-		unsigned char *ciphertext, int ciphertext_len, const unsigned char *aad,
bef612
-		int aad_len, const unsigned char *tag, int tag_len, unsigned char *key,
bef612
-		const unsigned char *iv, int iv_len, unsigned char *plaintext) {
bef612
-	EVP_CIPHER_CTX *ctx;
bef612
-	int len;
bef612
-	int plaintext_len;
bef612
-	int ret;
bef612
-
bef612
-	/* create and initialize the context */
bef612
-	if (!(ctx = EVP_CIPHER_CTX_new())) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* initialize the decryption cipher */
bef612
-	if (!EVP_DecryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* set IV length */
bef612
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* initialize key and IV */
bef612
-	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* provide AAD data */
bef612
-	if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
bef612
-				aad_len);
bef612
-		return -1;
bef612
-	}
bef612
-
bef612
-	/* provide the message to be decrypted and obtain the plaintext output */
bef612
-	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate ciphertext");
bef612
-		return -1;
bef612
-	}
bef612
-	plaintext_len = len;
bef612
-
bef612
-	/* set expected tag value; works in OpenSSL 1.0.1d and later */
bef612
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_TAG, tag_len,
bef612
-			(void *) tag)) {
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
bef612
-		return -1;
bef612
-	}
bef612
+static apr_byte_t oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext, const char *key,
bef612
+		char **result) {
bef612
+	apr_byte_t rv = FALSE;
bef612
+	json_t *json = NULL;
bef612
 
bef612
-	/*
bef612
-	 * finalize the decryption; a positive return value indicates success,
bef612
-	 * anything else is a failure - the plaintext is not trustworthy
bef612
-	 */
bef612
-	ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len;;
bef612
+	json = json_object();
bef612
+	json_object_set_new(json, OIDC_CACHE_CRYPTO_JSON_KEY, json_string(plaintext));
bef612
 
bef612
-	/* clean up */
bef612
-	EVP_CIPHER_CTX_free(ctx);
bef612
-
bef612
-	if (ret > 0) {
bef612
-		/* success */
bef612
-		plaintext_len += len;
bef612
-		return plaintext_len;
bef612
-	} else {
bef612
-		/* verify failed */
bef612
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptFinal_ex");
bef612
-		return -1;
bef612
-	}
bef612
-}
bef612
+	rv = oidc_util_jwt_create(r, (const char*) key, json, result);
bef612
 
bef612
-/*
bef612
- * static AAD value for encryption/decryption
bef612
- */
bef612
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_AAD[] = { 0x4d, 0x23, 0xc3,
bef612
-		0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78,
bef612
-		0xde };
bef612
+	if (json)
bef612
+		json_decref(json);
bef612
 
bef612
-/*
bef612
- * static IV value for encryption/decryption
bef612
- */
bef612
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_IV[] = { 0x00, 0x01, 0x02,
bef612
-		0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
bef612
-		0x0f };
bef612
-
bef612
-/*
bef612
- * AES GCM encrypt using the static AAD and IV
bef612
- */
bef612
-static int oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext,
bef612
-		unsigned char *key, char **result) {
bef612
-	char *encoded = NULL, *p = NULL, *e_tag = NULL;
bef612
-	unsigned char *ciphertext = NULL;
bef612
-	int plaintext_len, ciphertext_len, encoded_len, e_tag_len;
bef612
-	unsigned char tag[OIDC_CACHE_TAG_LEN];
bef612
-
bef612
-	/* allocate space for the ciphertext */
bef612
-	plaintext_len = strlen(plaintext) + 1;
bef612
-	ciphertext = apr_pcalloc(r->pool,
bef612
-			(plaintext_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER)));
bef612
-
bef612
-	ciphertext_len = oidc_cache_crypto_encrypt_impl(r,
bef612
-			(unsigned char *) plaintext, plaintext_len,
bef612
-			OIDC_CACHE_CRYPTO_GCM_AAD, sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), key,
bef612
-			OIDC_CACHE_CRYPTO_GCM_IV, sizeof(OIDC_CACHE_CRYPTO_GCM_IV),
bef612
-			ciphertext, tag, sizeof(tag));
bef612
-
bef612
-	/* base64url encode the resulting ciphertext */
bef612
-	encoded_len = oidc_base64url_encode(r, &encoded, (const char *) ciphertext,
bef612
-			ciphertext_len, 1);
bef612
-	if (encoded_len > 0) {
bef612
-		p = encoded;
bef612
-
bef612
-		/* now allocated space for the concatenated base64url encoded ciphertext and tag */
bef612
-		encoded = apr_pcalloc(r->pool,
bef612
-				encoded_len + 1 + OIDC_CACHE_TAG_LEN + 1);
bef612
-		memcpy(encoded, p, encoded_len);
bef612
-		p = encoded + encoded_len;
bef612
-		*p = OIDC_CHAR_DOT;
bef612
-		p++;
bef612
-
bef612
-		/* base64url encode the tag and append it in the buffer */
bef612
-		e_tag_len = oidc_base64url_encode(r, &e_tag, (const char *) tag,
bef612
-				OIDC_CACHE_TAG_LEN, 1);
bef612
-		memcpy(p, e_tag, e_tag_len);
bef612
-		encoded_len += e_tag_len + 1;
bef612
-
bef612
-		/* make sure the result is \0 terminated */
bef612
-		encoded[encoded_len] = '\0';
bef612
-
bef612
-		*result = encoded;
bef612
-	}
bef612
-
bef612
-	return encoded_len;
bef612
+	return rv;
bef612
 }
bef612
 
bef612
 /*
bef612
- * AES GCM decrypt using the static AAD and IV
bef612
+ * AES GCM decrypt using the crypto passphrase as symmetric key
bef612
  */
bef612
-static int oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
bef612
-		unsigned char *key, unsigned char **plaintext) {
bef612
+static apr_byte_t oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
bef612
+		const char *key, char **plaintext) {
bef612
 
bef612
-	int len = -1;
bef612
+	apr_byte_t rv = FALSE;
bef612
+	json_t *json = NULL;
bef612
 
bef612
-	/* grab the base64url-encoded tag after the "." */
bef612
-	char *encoded_tag = strstr(cache_value, ".");
bef612
-	if (encoded_tag == NULL) {
bef612
-		oidc_error(r,
bef612
-				"corrupted cache value: no tag separator found in encrypted value");
bef612
-		return FALSE;
bef612
-	}
bef612
+	rv = oidc_util_jwt_verify(r, (const char*) key, cache_value, &json);
bef612
+	if (rv == FALSE)
bef612
+		goto end;
bef612
 
bef612
-	/* make sure we don't modify the original string since it may be just a pointer into the cache (shm) */
bef612
-	cache_value = apr_pstrmemdup(r->pool, cache_value,
bef612
-			strlen(cache_value) - strlen(encoded_tag));
bef612
-	encoded_tag++;
bef612
+	rv = oidc_json_object_get_string(r->pool, json, OIDC_CACHE_CRYPTO_JSON_KEY, plaintext, NULL);
bef612
 
bef612
-	/* base64url decode the ciphertext */
bef612
-	char *d_bytes = NULL;
bef612
-	int d_len = oidc_base64url_decode(r->pool, &d_bytes, cache_value);
bef612
+	end:
bef612
 
bef612
-	/* base64url decode the tag */
bef612
-	char *t_bytes = NULL;
bef612
-	int t_len = oidc_base64url_decode(r->pool, &t_bytes, encoded_tag);
bef612
+	if (json)
bef612
+		json_decref(json);
bef612
 
bef612
-	/* see if we're still good to go */
bef612
-	if ((d_len > 0) && (t_len > 0)) {
bef612
-
bef612
-		/* allocated space for the plaintext */
bef612
-		*plaintext = apr_pcalloc(r->pool,
bef612
-				(d_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER) - 1));
bef612
-
bef612
-		/* decrypt the ciphertext providing the tag value */
bef612
-
bef612
-		len = oidc_cache_crypto_decrypt_impl(r, (unsigned char *) d_bytes,
bef612
-				d_len, OIDC_CACHE_CRYPTO_GCM_AAD,
bef612
-				sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), (unsigned char *) t_bytes,
bef612
-				t_len, key, OIDC_CACHE_CRYPTO_GCM_IV,
bef612
-				sizeof(OIDC_CACHE_CRYPTO_GCM_IV), *plaintext);
bef612
-
bef612
-		/* check the result and make sure it is \0 terminated */
bef612
-		if (len > -1) {
bef612
-			(*plaintext)[len] = '\0';
bef612
-		} else {
bef612
-			*plaintext = NULL;
bef612
-		}
bef612
-
bef612
-	}
bef612
-
bef612
-	return len;
bef612
-}
bef612
-
bef612
-/*
bef612
- * hash the crypto passhphrase so it has enough key length for AES GCM 256
bef612
- */
bef612
-static unsigned char *oidc_cache_hash_passphrase(request_rec *r,
bef612
-		const char *passphrase) {
bef612
-
bef612
-	unsigned char *key = NULL;
bef612
-	unsigned int key_len = 0;
bef612
-	oidc_jose_error_t err;
bef612
-
bef612
-	if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
bef612
-			(const unsigned char *) passphrase, strlen(passphrase), &key,
bef612
-			&key_len, &err) == FALSE) {
bef612
-		oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
bef612
-		return NULL;
bef612
-	}
bef612
-
bef612
-	return key;
bef612
+	return rv;
bef612
 }
bef612
 
bef612
 /*
bef612
  * hash a cache key and a crypto passphrase so the result is suitable as an randomized cache key
bef612
  */
bef612
-static char *oidc_cache_get_hashed_key(request_rec *r, const char *passphrase,
bef612
-		const char *key) {
bef612
+static char* oidc_cache_get_hashed_key(request_rec *r, const char *passphrase, const char *key) {
bef612
 	char *input = apr_psprintf(r->pool, "%s:%s", passphrase, key);
bef612
 	char *output = NULL;
bef612
-	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
bef612
-			input, &output) == FALSE) {
bef612
-		oidc_error(r,
bef612
-				"oidc_util_hash_string_and_base64url_encode returned an error");
bef612
+	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, input, &output)
bef612
+			== FALSE) {
bef612
+		oidc_error(r, "oidc_util_hash_string_and_base64url_encode returned an error");
bef612
 		return NULL;
bef612
 	}
bef612
 	return output;
bef612
@@ -588,9 +323,7 @@ apr_byte_t oidc_cache_get(request_rec *r, const char *section, const char *key,
bef612
 		goto out;
bef612
 	}
bef612
 
bef612
-	rc = (oidc_cache_crypto_decrypt(r, cache_value,
bef612
-			oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
bef612
-			(unsigned char **) value) > 0);
bef612
+	rc = oidc_cache_crypto_decrypt(r, cache_value, cfg->crypto_passphrase, value);
bef612
 
bef612
 out:
bef612
 	/* log the result */
bef612
@@ -634,9 +367,7 @@ apr_byte_t oidc_cache_set(request_rec *r, const char *section, const char *key,
bef612
 			goto out;
bef612
 
bef612
 		if (value != NULL) {
bef612
-			if (oidc_cache_crypto_encrypt(r, value,
bef612
-					oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
bef612
-					&encoded) <= 0)
bef612
+			if (oidc_cache_crypto_encrypt(r, value, cfg->crypto_passphrase, &encoded) == FALSE)
bef612
 				goto out;
bef612
 			value = encoded;
bef612
 		}
bef612