From: Arran Cudbard-Bell Date: Tue, 27 Feb 2018 15:31:19 +0000 (+0600) Subject: Add rlm_ciper for encrypting/decrypting arbitrary data X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c7a7a26843744cd47b2458edd91e4c6ddaba9bb;p=thirdparty%2Ffreeradius-server.git Add rlm_ciper for encrypting/decrypting arbitrary data Not yet functional because OpenSSL is stupid... --- diff --git a/raddb/mods-available/cipher b/raddb/mods-available/cipher new file mode 100644 index 00000000000..c7b5440e238 --- /dev/null +++ b/raddb/mods-available/cipher @@ -0,0 +1,88 @@ + +cipher { + # + # RSA asymmetrically keyed ciphering + # + # Registers the following expansions: + # - %{_encrypt:...} - Encrypts plaintext using `certificate_file` + # - %{<inst>_decrypt:<ciphertext>...} - Decrypts ciphertext using `private_key_file` + # - %{<inst>_sign:<plaintext>...} - Signs plaintext using `private_key_file` + # - %{<inst>_verify:<signature> <plaintext>...} - Validates a signature using `certificate_file` + # + # Note: <ciphertext> and <signature> are ingested and excreted to in their raw form. + # You should use armouring expansions i.e. %{base64_encode:}, %{base64_decode:} if the values + # are to be passed outside of FreeRADIUS. + # + # Example: + # + # %{base64_encode:%{cipher_encrypt:<plaintext>}} + # %{cipher_decrypt:%{base64_decode:<ciphertext>}} + # + # Supported digest types vary depending on the version OpenSSL was built against. + # + # Reasonably modern >= 1.0.0 versions of OpenSSL should support at least: + # + # - md2 (not recommended) + # - md4 (not recommended) + # - md5 (not recommended) + # - sha1 (widely used but deprecated) + # - sha224 + # - sha256 (the default - strongly recommended) + # - sha384 + # - sha512 + # + # Bleeding edge versions of OpenSSL may also support the sha3 family of digest functions. + # + # Again, the supported versions are determined *entirely* by the version of OpenSSL used, we + # pass the name of the digest off to OpenSSL and it tells *us* whether it's valid/supported + # or not. + # + rsa { + # + # Private key used for decrypting and signing data + # + private_key_password = whatever + private_key_file = ${certdir}/server.pem + + # + # The PEM encoded certificate used for encrypting data + # and verifying signatures. + # + certificate_file = ${certdir}/server.pem + + # + # Parameters for the OAEP RSA padding scheme + # + oaep { +# oaep_digest = "sha256" +# mgf1_digest = "sha256" +# label = "" + } + + # + # Digest used to ingest the plaintext before signing + # or verification. + # +# signature_digest = "sha256" + + # + # The type of padding applied to the plaintext + # + # One of: + # - none (no padding) + # - pkcs + # - oaep + # - x931 (signing only) + # - ssl (v1/v2) + # + # Defaults to pkcs + # +# padding_type = pkcs + } + + # + # Available schemes are: + # - rsa + # + type = rsa +} diff --git a/src/modules/rlm_cipher/README.md b/src/modules/rlm_cipher/README.md new file mode 100644 index 00000000000..be319d1b61a --- /dev/null +++ b/src/modules/rlm_cipher/README.md @@ -0,0 +1,8 @@ +# rlm_example +## Metadata +<dl> + <dt>category</dt><dd>policy</dd> +</dl> + +## Summary +An example module to use as a template when writing new modules. diff --git a/src/modules/rlm_cipher/all.mk b/src/modules/rlm_cipher/all.mk new file mode 100644 index 00000000000..108009addac --- /dev/null +++ b/src/modules/rlm_cipher/all.mk @@ -0,0 +1,22 @@ +####################################################################### +# +# TARGET should be set by autoconf only. Don't touch it. +# +# The SOURCES definition should list ALL source files. +# +# SRC_CFLAGS defines addition C compiler flags. You usually don't +# want to modify this, though. Get it from autoconf. +# +# The TGT_LDLIBS definition should list ALL required libraries. +# +####################################################################### + +TARGETNAME := rlm_cipher + +ifneq "$(OPENSSL_LIBS)" "" +TARGET := $(TARGETNAME).a +endif + +SOURCES := $(TARGETNAME).c + +TGT_PREREQS := $(LIBFREERADIUS_SERVER) diff --git a/src/modules/rlm_cipher/rlm_cipher.c b/src/modules/rlm_cipher/rlm_cipher.c new file mode 100644 index 00000000000..ebf3c6c1af8 --- /dev/null +++ b/src/modules/rlm_cipher/rlm_cipher.c @@ -0,0 +1,1027 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file rlm_cipher.c + * @brief Creates dynamic expansions for encrypting/decrypting data. + * + * @author Arran Cudbard-Bell \<a.cudbardb@freeradius.org\> + * + * @copyright 2018 The FreeRADIUS server project + * @copyright 2018 Network RADIUS \<info@networkradius.com\> + * + */ +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> +#include <freeradius-devel/rad_assert.h> + +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/rsa.h> + +static int digest_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule); +static int cipher_rsa_padding_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule); +static int cipher_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule); + +static int cipher_rsa_private_key_file_load(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule); +static int cipher_rsa_certificate_file_load(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule); + +typedef enum { + RLM_CIPHER_TYPE_INVALID = 0, + RLM_CIPHER_TYPE_RSA = 1, +} cipher_type_t; + +/** The type of padding used + * + */ +const FR_NAME_NUMBER cipher_rsa_padding[] = { + { "none", RSA_NO_PADDING }, + { "pkcs", RSA_PKCS1_PADDING }, /* PKCS 1.5 */ + { "oaep", RSA_PKCS1_OAEP_PADDING }, /* PKCS OAEP padding */ + { "x931", RSA_X931_PADDING }, + { "ssl", RSA_SSLV23_PADDING }, + + { NULL, 0 }, +}; + +const FR_NAME_NUMBER cipher_type[] = { + { "rsa", RLM_CIPHER_TYPE_RSA }, + + { NULL, 0 } +}; + +typedef struct { + EVP_PKEY_CTX *evp_pkey_ctx; //!< Pre-allocated evp_pkey_ctx. + EVP_MD_CTX *evp_md_ctx; //!< Pre-allocated evp_md_ctx for sign and verify. + uint8_t *digest_buff; //!< Pre-allocated digest buffer. +} rlm_cipher_rsa_thread_inst_t; + +/** Configuration for the OAEP padding method + * + */ +typedef struct { + EVP_MD *oaep_digest; //!< Padding digest type. + EVP_MD *mgf1_digest; //!< Masking function digest. + + char const *label; //!< Additional input to the hashing function. +} cipher_rsa_oaep_t; + +/** Configuration for RSA encryption/decryption/signing + * + */ +typedef struct { + char const *private_key_password; //!< Password to decrypt the private key. + char const *random_file; //!< If set, we read 10K of data (or the complete file) + //!< and use it to seed OpenSSL's PRNG. + + EVP_PKEY *private_key_file; //!< Private key file. + EVP_PKEY *certificate_file; //!< Public (certificate) file. + + int padding; //!< Type of padding to apply to the plaintext + ///< or ciphertext before feeding it to RSA crypto + ///< functions. + + EVP_MD *sig_digest; //!< Signature digest type. + + cipher_rsa_oaep_t *oaep; //!< OAEP can use a configurable message digest type + ///< and additional keying labeleter. +} cipher_rsa_t; + +/** Instance configuration + * + */ +typedef struct { + char const *xlat_name; //!< Name of xlat we registered. + cipher_type_t type; //!< Type of encryption to use. + + /** Supported cipher types + * + */ + union { + cipher_rsa_t *rsa; //!< Use RSA encryption (with optional padding). + }; +} rlm_cipher_t; + +/** Configuration for the RSA-PCKS1-OAEP padding scheme + * + */ +static const CONF_PARSER rsa_oaep_config[] = { + { FR_CONF_OFFSET("oaep_digest", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_oaep_t, oaep_digest), .func = digest_type_parse, .dflt = "sha256" }, + { FR_CONF_OFFSET("mgf1_digest", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_oaep_t, mgf1_digest), .func = digest_type_parse, .dflt = "sha256" }, + { FR_CONF_OFFSET("label", FR_TYPE_STRING, cipher_rsa_oaep_t, label) }, + + CONF_PARSER_TERMINATOR +}; + +/** Configuration for the RSA cipher type + * + */ +static const CONF_PARSER rsa_config[] = { + { FR_CONF_OFFSET("private_key_password", FR_TYPE_STRING | FR_TYPE_SECRET, cipher_rsa_t, private_key_password) }, /* Must come before private_key */ + { FR_CONF_OFFSET("private_key_file", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, private_key_file), .func = cipher_rsa_private_key_file_load }, + { FR_CONF_OFFSET("certificate_file", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, certificate_file), .func = cipher_rsa_certificate_file_load }, + + { FR_CONF_OFFSET("random_file", FR_TYPE_STRING, cipher_rsa_t, random_file) }, + + { FR_CONF_OFFSET("signature_digest", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, sig_digest), .func = digest_type_parse, .dflt = "sha256" }, + + { FR_CONF_OFFSET("padding_type", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, padding), .func = cipher_rsa_padding_type_parse, .dflt = "pkcs" }, + { FR_CONF_OFFSET("oaep", FR_TYPE_SUBSECTION, cipher_rsa_t, oaep), + .subcs_size = sizeof(cipher_rsa_oaep_t), .subcs_type = "cipher_rsa_oaep_t", .subcs = (void const *) rsa_oaep_config }, + + CONF_PARSER_TERMINATOR +}; + +/* + * A mapping of configuration file names to internal variables. + */ +static const CONF_PARSER module_config[] = { + { FR_CONF_OFFSET("type", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, rlm_cipher_t, type), .func = cipher_type_parse, .dflt = "rsa" }, + { FR_CONF_OFFSET("rsa", FR_TYPE_SUBSECTION, rlm_cipher_t, rsa), + .subcs_size = sizeof(cipher_rsa_t), .subcs_type = "cipher_rsa_t", .subcs = (void const *) rsa_config }, + + CONF_PARSER_TERMINATOR +}; + +/** Calls EVP_get_digestbyname() to covert the digest type + * + * @label[in] ctx to allocate data in (instance of proto_radius). + * @label[out] out EVP_MD representing the OpenSSL digest type. + * @label[in] ci #CONF_PAIR specifying the name of the digest. + * @label[in] rule unused. + * @return + * - 0 on success. + * - -1 on failure. + */ +static int digest_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) +{ + EVP_MD const *md; + char const *type_str; + + type_str = cf_pair_value(cf_item_to_pair(ci)); + md = EVP_get_digestbyname(type_str); + if (!md) { + cf_log_err(ci, "Invalid digest type \"%s\"", type_str); + return -1; + } + + *((EVP_MD const **)out) = md; + + return 0; +} + +/** Checks if the specified padding type is valid + * + * @label[in] ctx to allocate data in (instance of proto_radius). + * @label[out] out Padding type. + * @label[in] ci #CONF_PAIR specifying the padding type.. + * @label[in] rule unused. + * @return + * - 0 on success. + * - -1 on failure. + */ +static int cipher_rsa_padding_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, + UNUSED CONF_PARSER const *rule) +{ + int type; + char const *type_str; + + type_str = cf_pair_value(cf_item_to_pair(ci)); + type = fr_str2int(cipher_rsa_padding, type_str, -1); + if (type == -1) { + cf_log_err(ci, "Invalid padding type \"%s\"", type_str); + return -1; + } + + *((int *)out) = type; + + return 0; +} + +/** Checks if the specified cipher type is valid + * + * @label[in] ctx to allocate data in (instance of proto_radius). + * @label[out] out Cipher enumeration type. + * @label[in] ci #CONF_PAIR specifying the name of the type module. + * @label[in] rule unused. + * @return + * - 0 on success. + * - -1 on failure. + */ +static int cipher_type_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) +{ + cipher_type_t type; + char const *type_str; + + type_str = cf_pair_value(cf_item_to_pair(ci)); + type = fr_str2int(cipher_type, type_str, RLM_CIPHER_TYPE_INVALID); + switch (type) { + case RLM_CIPHER_TYPE_RSA: + break; + + case RLM_CIPHER_TYPE_INVALID: + cf_log_err(ci, "Invalid cipher type \"%s\"", type_str); + return -1; + } + + *((cipher_type_t *)out) = type; + + return 0; +} + +/** Return the static private key password we have configured + * + * @label[out] buf Where to write the password to. + * @label[in] num The length of buf. + * @label[in] rwflag + * - 0 if password used for decryption. + * - 1 if password used for encryption. + * @label[in] userdata The static password. + * @return + * - 0 on error. + * - >0 on success (the length of the password). + */ +static int _get_private_key_password(char *buf, int size, UNUSED int rwflag, void *u) +{ + char *pass; + size_t len; + + if (!u) { + ERROR("Certificate encrypted but no private_key_password configured"); + return 0; + } + + pass = talloc_get_type_abort(u, char); + len = talloc_array_length(pass); /* Len includes \0 */ + if (len > (size_t)size) { + ERROR("Password too long. Maximum length is %i bytes", size - 1); + return -1; + } + memcpy(buf, pass, len); /* Copy complete password including \0 byte */ + + return len - 1; +} + +/** Talloc destructor for freeing an EVP_PKEY (representing a certificate) + * + * @label[in] pkey to free. + * @return 0 + */ +static int _evp_pkey_free(EVP_PKEY *pkey) +{ + EVP_PKEY_free(pkey); + + return 0; +} + +/** Load and (optionally decrypt) an RSA private key using OpenSSL functions + * + * @label[in] ctx UNUSED. Although the EVP_PKEY struct will be allocated + * with talloc, we need to call the specialised free + * function anyway. + * @label[out] out Where to write the EVP_PKEY * representing the + * certificate we just loaded. + * @label[in] ci Config item containing the certificate path. + * @label[in] rule this callback was attached to. + * @return + * - -1 on failure. + * - 0 on success. + */ +static int cipher_rsa_private_key_file_load(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, + UNUSED CONF_PARSER const *rule) +{ + FILE *fp; + char const *filename; + cipher_rsa_t *rsa_inst = talloc_get_type_abort(ctx, cipher_rsa_t); /* Yeah this is a bit hacky */ + EVP_PKEY *pkey; + void *pass; + + filename = cf_pair_value(cf_item_to_pair(ci)); + + fp = fopen(filename, "r"); + if (!fp) { + cf_log_err(ci, "Failed opening file: %s", fr_syserror(errno)); + return -1; + } + + memcpy(&pass, &rsa_inst->private_key_password, sizeof(pass)); + + pkey = PEM_read_PrivateKey(fp, (EVP_PKEY **)out, _get_private_key_password, pass); + fclose(fp); + + if (!pkey) { + tls_strerror_printf(true, NULL); + cf_log_perr(ci, "Error loading private certificate file \"%s\"", filename); + + return -1; + } + + (void)talloc_steal(ctx, pkey); /* Bind lifetime to config */ + talloc_set_destructor(pkey, _evp_pkey_free); /* Free pkey correctly on chunk free */ + + return 0; +} + +/** Load an RSA public key using OpenSSL functions + * + * @label[in] ctx UNUSED. Although the EVP_PKEY struct will be allocated + * with talloc, we need to call the specialised free + * function anyway. + * @label[out] out Where to write the EVP_PKEY * representing the + * certificate we just loaded. + * @label[in] ci Config item containing the certificate path. + * @label[in] rule this callback was attached to. + * @return + * - -1 on failure. + * - 0 on success. + */ +static int cipher_rsa_certificate_file_load(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, + UNUSED CONF_PARSER const *rule) +{ + FILE *fp; + char const *filename; + EVP_PKEY *pkey; + + filename = cf_pair_value(cf_item_to_pair(ci)); + + fp = fopen(filename, "r"); + if (!fp) { + cf_log_err(ci, "Failed opening file: %s", fr_syserror(errno)); + return -1; + } + + pkey = PEM_read_PUBKEY(fp, (EVP_PKEY **)out, NULL, NULL); + fclose(fp); + + if (!pkey) { + tls_strerror_printf(true, NULL); + cf_log_perr(ci, "Error loading certificate file \"%s\"", filename); + + return -1; + } + + (void)talloc_steal(ctx, pkey); /* Bind lifetime to config */ + talloc_set_destructor(pkey, _evp_pkey_free); /* Free pkey correctly on chunk free */ + + return 0; +} + +static int cipher_rsa_padding_params_set(REQUEST *request, EVP_PKEY_CTX *evp_pkey_ctx, cipher_rsa_t const *rsa_inst) +{ + + if (unlikely(EVP_PKEY_CTX_set_rsa_padding(evp_pkey_ctx, rsa_inst->padding)) <= 0) { + tls_log_error(request, "Failed setting RSA padding type"); + return -1; + } + + switch (rsa_inst->padding) { + case RSA_NO_PADDING: + case RSA_X931_PADDING: + case RSA_SSLV23_PADDING: + case RSA_PKCS1_PADDING: + return 0; + + /* + * Configure OAEP advanced padding options + */ + case RSA_PKCS1_OAEP_PADDING: + if (unlikely(EVP_PKEY_CTX_set_rsa_oaep_md(evp_pkey_ctx, rsa_inst->oaep->oaep_digest) <= 0)) { + tls_log_error(request, "Failed setting OAEP digest"); + return -1; + } + + if (unlikely(EVP_PKEY_CTX_set_rsa_mgf1_md(evp_pkey_ctx, rsa_inst->oaep->mgf1_digest) <= 0)) { + tls_log_error(request, "Failed setting MGF1 digest"); + return -1; + } + + if (rsa_inst->oaep->label) { + char *label; + + memcpy(&label, &rsa_inst->oaep->label, sizeof(label)); + + if (unlikely(EVP_PKEY_CTX_set0_rsa_oaep_label(evp_pkey_ctx, label, + talloc_array_length(label) - 1) <= 0)) { + tls_log_error(request, "Failed setting OAEP padding label"); + return -1; + } + } + return 0; + + default: + rad_assert(0); + return -1; + } +} + +/** Encrypt input data + * + * Arguments are (<plaintext>...). + * + * If multiple arguments are provided they will be concatenated. + */ +static xlat_action_t cipher_rsa_encrypt_xlat(TALLOC_CTX *ctx, fr_cursor_t *out, + REQUEST *request, void const *xlat_inst, void *xlat_thread_inst, + fr_value_box_t **in) +{ + rlm_cipher_t const *inst = talloc_get_type_abort(*((void const * const *)xlat_inst), rlm_cipher_t); + rlm_cipher_rsa_thread_inst_t *xt = talloc_get_type_abort(*((void const * const *)xlat_thread_inst), + rlm_cipher_rsa_thread_inst_t); + + char const *plaintext; + size_t plaintext_len; + + uint8_t *ciphertext; + size_t ciphertext_len; + + fr_value_box_t *vb; + + if (!*in) { + REDEBUG("encrypt requires one or arguments (<plaintext>...)"); + return XLAT_ACTION_FAIL; + } + + if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) { + tls_log_error(request, "Failed concatenating arguments to form plaintext"); + return XLAT_ACTION_FAIL; + } + plaintext = (*in)->vb_strvalue; + plaintext_len = (*in)->vb_length; + + if (unlikely(EVP_PKEY_encrypt_init(xt->evp_pkey_ctx) <= 0)) { + tls_log_error(request, "Failed initialising EVP_PKEY_CTX"); + return XLAT_ACTION_FAIL; + } + + if (unlikely(cipher_rsa_padding_params_set(request, xt->evp_pkey_ctx, inst->rsa) < 0)) return XLAT_ACTION_FAIL; + + /* + * Figure out the buffer we need + */ + if (EVP_PKEY_encrypt(xt->evp_pkey_ctx, NULL, &ciphertext_len, + (unsigned char const *)plaintext, plaintext_len) <= 0) { + tls_log_error(request, "Failed encrypting plaintext"); + return XLAT_ACTION_FAIL; + } + + MEM(ciphertext = talloc_array(ctx, uint8_t, ciphertext_len)); + if (EVP_PKEY_encrypt(xt->evp_pkey_ctx, ciphertext, &ciphertext_len, + (unsigned char const *)plaintext, plaintext_len) <= 0) { + tls_log_error(request, "Failed encrypting plaintext"); + return XLAT_ACTION_FAIL; + } + + if (ciphertext_len != talloc_array_length(ciphertext)) { + uint8_t *n; + + n = talloc_realloc_size(ctx, ciphertext, ciphertext_len); + if (unlikely(!n)) { + REDEBUG("Failed shrinking ciphertext buffer"); + talloc_free(ciphertext); + return XLAT_ACTION_FAIL; + } + talloc_set_type(n, uint8_t); + + ciphertext = n; + } + + MEM(vb = fr_value_box_alloc_null(ctx)); + fr_value_box_memsteal(vb, vb, NULL, ciphertext, false); + fr_cursor_append(out, vb); + + return XLAT_ACTION_DONE; +} + +/** Sign input data + * + * Arguments are (<plaintext>...). + * + * If multiple arguments are provided they will be concatenated. + */ +static xlat_action_t cipher_rsa_sign_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_cursor_t *out, + REQUEST *request, void const *xlat_inst, void *xlat_thread_inst, + fr_value_box_t **in) +{ + rlm_cipher_t const *inst = talloc_get_type_abort(*((void const * const *)xlat_inst), rlm_cipher_t); + rlm_cipher_rsa_thread_inst_t *xt = talloc_get_type_abort(*((void const * const *)xlat_thread_inst), + rlm_cipher_rsa_thread_inst_t); + + char const *msg; + size_t msg_len; + + uint8_t *sig; + size_t sig_len; + + size_t digest_len; + + fr_value_box_t *vb; + + if (!*in) { + REDEBUG("sign requires one or arguments (<plaintext>...)"); + return XLAT_ACTION_FAIL; + } + + if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) { + REDEBUG("Failed concatenating arguments to form plaintext"); + return XLAT_ACTION_FAIL; + } + msg = (*in)->next->vb_strvalue; + msg_len = (*in)->vb_length; + + if (unlikely(EVP_PKEY_sign_init(xt->evp_pkey_ctx) <= 0)) { + tls_log_error(request, "Failed initialising EVP_PKEY_CTX"); + return XLAT_ACTION_FAIL; + } + + if (unlikely(cipher_rsa_padding_params_set(request, xt->evp_pkey_ctx, inst->rsa) < 0)) return XLAT_ACTION_FAIL; + if (unlikely(EVP_PKEY_CTX_set_signature_md(xt->evp_pkey_ctx, inst->rsa->sig_digest)) <= 0) { + tls_log_error(request, "Failed setting signature digest type"); + return XLAT_ACTION_FAIL; + } + + /* + * First produce a digest of the message + */ + if (unlikely(EVP_DigestInit(xt->evp_md_ctx, inst->rsa->sig_digest) <= 0)) { + tls_log_error(request, "Failed initialising message digest"); + return XLAT_ACTION_FAIL; + } + + if (EVP_DigestUpdate(xt->evp_md_ctx, msg, msg_len) <= 0) { + tls_log_error(request, "Failed ingesting message"); + return XLAT_ACTION_FAIL; + } + + if (EVP_DigestFinal(xt->evp_md_ctx, xt->digest_buff, (unsigned int *)&digest_len) <= 0) { + tls_log_error(request, "Failed finalising message digest"); + return XLAT_ACTION_FAIL; + } + + /* + * Then sign the digest + */ + if (EVP_PKEY_sign(ctx, NULL, &sig_len, xt->digest_buff, digest_len) <= 0) { + tls_log_error(request, "Failed signing message digest"); + return XLAT_ACTION_FAIL; + } + + MEM(sig = talloc_array(ctx, uint8_t, sig_len)); + if (EVP_PKEY_sign(ctx, sig, &sig_len, xt->digest_buff, digest_len) <= 0) { + tls_log_error(request, "Failed signing message digest"); + return XLAT_ACTION_FAIL; + } + + /* + * Fixup the output buffer + */ + if (sig_len != talloc_array_length(sig)) { + uint8_t *n; + + n = talloc_realloc_size(ctx, sig, sig_len); + if (unlikely(!n)) { + REDEBUG("Failed shrinking signature buffer"); + talloc_free(sig); + return XLAT_ACTION_FAIL; + } + talloc_set_type(n, uint8_t); + + sig = n; + } + + MEM(vb = fr_value_box_alloc_null(ctx)); + fr_value_box_memsteal(vb, vb, NULL, sig, false); + fr_cursor_append(out, vb); + + return XLAT_ACTION_DONE; +} + +/** Decrypt input data + * + * Arguments are (<ciphertext>...). + * + * If multiple arguments are provided they will be concatenated. + */ +static xlat_action_t cipher_rsa_decrypt_xlat(TALLOC_CTX *ctx, UNUSED fr_cursor_t *out, + REQUEST *request, void const *xlat_inst, void *xlat_thread_inst, + fr_value_box_t **in) +{ + rlm_cipher_t const *inst = talloc_get_type_abort(*((void const * const *)xlat_inst), rlm_cipher_t); + rlm_cipher_rsa_thread_inst_t *xt = talloc_get_type_abort(*((void const * const *)xlat_thread_inst), + rlm_cipher_rsa_thread_inst_t); + + uint8_t const *ciphertext; + size_t ciphertext_len; + + char *plaintext; + size_t plaintext_len; + + fr_value_box_t *vb; + + if (!*in) { + REDEBUG("decrypt requires one or more arguments (<ciphertext>...)"); + return XLAT_ACTION_FAIL; + } + + if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_OCTETS, true) < 0) { + REDEBUG("Failed concatenating arguments to form plaintext"); + return XLAT_ACTION_FAIL; + } + ciphertext = (*in)->vb_octets; + ciphertext_len = (*in)->vb_length; + + if (unlikely(EVP_PKEY_decrypt_init(xt->evp_pkey_ctx) <= 0)) { + tls_log_error(request, "Failed initialising EVP_PKEY_CTX"); + return XLAT_ACTION_FAIL; + } + + if (unlikely(cipher_rsa_padding_params_set(request, xt->evp_pkey_ctx, inst->rsa) < 0)) return XLAT_ACTION_FAIL; + + /* + * Decrypt the plaintext + */ + if (EVP_PKEY_decrypt(ctx, NULL, &plaintext_len, ciphertext, ciphertext_len) <= 0) { + tls_log_error(request, "Failed decrypting ciphertext"); + return XLAT_ACTION_FAIL; + } + + MEM(plaintext = talloc_array(ctx, char, plaintext_len + 1)); + if (EVP_PKEY_decrypt(ctx, (unsigned char *)plaintext, &plaintext_len, ciphertext, ciphertext_len) <= 0) { + tls_log_error(request, "Failed decrypting ciphertext"); + return XLAT_ACTION_FAIL; + } + + /* + * Fixup the output buffer (and ensure it's \0 terminated) + */ + { + char *n; + + n = talloc_realloc_bstr(plaintext, plaintext_len); + if (unlikely(!n)) { + REDEBUG("Failed shrinking plaintext buffer"); + talloc_free(plaintext); + return XLAT_ACTION_FAIL; + } + + plaintext = n; + } + + MEM(vb = fr_value_box_alloc_null(ctx)); + fr_value_box_strsteal(vb, vb, NULL, plaintext, false); + fr_cursor_append(out, vb); + + return XLAT_ACTION_DONE; +} + +/** Verify input data + * + * Arguments are (<signature>, <plaintext>...). + * + * If multiple arguments are provided (after <signature>) they will be concatenated. + */ +static xlat_action_t cipher_rsa_verify_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_cursor_t *out, + REQUEST *request, void const *xlat_inst, void *xlat_thread_inst, + fr_value_box_t **in) +{ + rlm_cipher_t const *inst = talloc_get_type_abort(*((void const * const *)xlat_inst), rlm_cipher_t); + rlm_cipher_rsa_thread_inst_t *xt = talloc_get_type_abort(*((void const * const *)xlat_thread_inst), + rlm_cipher_rsa_thread_inst_t); + + uint8_t const *sig; + size_t sig_len; + + char const *msg; + size_t msg_len; + + size_t digest_len; + + fr_value_box_t *vb; + + if (!*in) { + REDEBUG("verification requires two or more arguments (<signature>, <message>...)"); + return XLAT_ACTION_FAIL; + } + + /* + * Check we have at least two boxed values + */ + if (!(*in)->next) { + REDEBUG("Missing message data arg or message data was (null)"); + return XLAT_ACTION_FAIL; + } + + /* + * Don't auto-cast to octets if the signature + * isn't already in that form. + * It could be hexits or base64 or some other encoding. + */ + if ((*in)->type != FR_TYPE_OCTETS) { + REDEBUG("Signature argument wrong type, expected %s, got %s. " + "Use %%{base64_decode:<text>} or %%{hex_decode:<text>} if signature is armoured", + fr_int2str(dict_attr_types, FR_TYPE_OCTETS, "?Unknown?"), + fr_int2str(dict_attr_types, (*in)->type, "?Unknown?")); + return XLAT_ACTION_FAIL; + } + sig = (*in)->vb_octets; + sig_len = (*in)->vb_length; + + /* + * Concat (...) args to get message data + */ + if (fr_value_box_list_concat(ctx, (*in)->next, &((*in)->next), FR_TYPE_STRING, true) < 0) { + REDEBUG("Failed concatenating arguments to form plaintext"); + return XLAT_ACTION_FAIL; + } + msg = (*in)->next->vb_strvalue; + msg_len = (*in)->next->vb_length; + + /* + * The argument separator also gets rolled into + * the concatenate buffer... We should probably + * figure out a cleaner way of doing this. + */ + if (*msg != ' ') { + REDEBUG("Expected whitespace argument separator"); + return XLAT_ACTION_FAIL; + } + msg++; + msg_len--; + + if (msg_len == 0) { + REDEBUG("Zero length message data"); + return XLAT_ACTION_FAIL; + } + + if (unlikely(EVP_PKEY_verify_init(xt->evp_pkey_ctx) <= 0)) { + tls_log_error(request, "Failed initialising EVP_PKEY_CTX"); + return XLAT_ACTION_FAIL; + } + + if (unlikely(cipher_rsa_padding_params_set(request, xt->evp_pkey_ctx, inst->rsa) < 0)) return XLAT_ACTION_FAIL; + if (unlikely(EVP_PKEY_CTX_set_signature_md(xt->evp_pkey_ctx, inst->rsa->sig_digest)) <= 0) { + tls_log_error(request, "Failed setting signature digest type"); + return XLAT_ACTION_FAIL; + } + + /* + * First produce a digest of the message + */ + if (unlikely(EVP_DigestInit(xt->evp_md_ctx, inst->rsa->sig_digest) <= 0)) { + tls_log_error(request, "Failed initialising message digest"); + return XLAT_ACTION_FAIL; + } + + if (EVP_DigestUpdate(xt->evp_md_ctx, msg, msg_len) <= 0) { + tls_log_error(request, "Failed ingesting message"); + return XLAT_ACTION_FAIL; + } + + if (EVP_DigestFinal(xt->evp_md_ctx, xt->digest_buff, (unsigned int *)&digest_len) <= 0) { + tls_log_error(request, "Failed finalising message digest"); + return XLAT_ACTION_FAIL; + } + + /* + * Now check the signature matches what we expected + */ + switch (EVP_PKEY_verify(ctx, sig, sig_len, xt->digest_buff, digest_len)) { + case 1: /* success (signature valid) */ + MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL, false)); + vb->vb_bool = true; + fr_cursor_append(out, vb); + break; + + case 0: /* failure (signature not valid) */ + MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL, false)); + vb->vb_bool = false; + fr_cursor_append(out, vb); + break; + + default: + tls_log_error(request, "Failed validating signature"); + return XLAT_ACTION_FAIL; + } + + return XLAT_ACTION_DONE; +} + +/** Talloc destructor for freeing an EVP_PKEY_CTX + * + * @label[in] pkey to free. + * @return 0 + */ +static int _evp_pkey_ctx_free(EVP_PKEY_CTX *evp_pkey_ctx) +{ + EVP_PKEY_CTX_free(evp_pkey_ctx); + + return 0; +} + +/** Talloc destructor for freeing an EVP_MD_CTX + * + * @label[in] pkey to free. + * @return 0 + */ +static int _evp_md_ctx_free(EVP_MD_CTX *evp_md_ctx) +{ + EVP_MD_CTX_destroy(evp_md_ctx); + + return 0; +} + +/** Boilerplate to copy the pointer to the main module thread instance into xlat thread instance data + * + */ +static int cipher_xlat_thread_instantiate(UNUSED void *xlat_inst, void *xlat_thread_inst, + UNUSED xlat_exp_t const *exp, void *uctx) +{ + rlm_cipher_t *inst = talloc_get_type_abort(uctx, rlm_cipher_t); + + *((rlm_cipher_rsa_thread_inst_t **)xlat_thread_inst) = + talloc_get_type_abort(module_thread_instance_by_data(inst), rlm_cipher_rsa_thread_inst_t); + + return 0; +} + +/** Boilerplate to copy the pointer to the main module config into the xlat instance data + * + */ +static int cipher_xlat_instantiate(void *xlat_inst, UNUSED xlat_exp_t const *exp, void *uctx) +{ + *((rlm_cipher_t **)xlat_inst) = talloc_get_type_abort(uctx, rlm_cipher_t); + + return 0; +} + + +/** Pre-initialises the EVP_PKEY_CTX necessary for performing RSA encryption/decryption/sign/verify + * + * If reference counting is used for EVP_PKEY structs, should also prevent any mutex contention + * associated with incrementing/decrementing those references. + * + * xlat functions MUST NOT interleave PKEY operations with yields + * + * @return 0. + */ +static int cipher_rsa_thread_instantiate(UNUSED CONF_SECTION const *conf, void *instance, + UNUSED fr_event_list_t *el, void *thread) +{ + rlm_cipher_t const *inst = talloc_get_type_abort(instance, rlm_cipher_t); + rlm_cipher_rsa_thread_inst_t *ti = talloc_get_type_abort(thread, rlm_cipher_rsa_thread_inst_t); + + ti->evp_pkey_ctx = EVP_PKEY_CTX_new(inst->rsa->certificate_file, NULL); + if (!ti->evp_pkey_ctx) { + tls_strerror_printf(true, NULL); + PERROR("%s: Failed allocating EVP_PKEY_CTX", __FUNCTION__); + return -1; + } + + ti->evp_pkey_ctx = talloc_steal(ti, ti->evp_pkey_ctx); /* Bind lifetime to instance */ + talloc_set_destructor(ti->evp_pkey_ctx, _evp_pkey_ctx_free); /* Free ctx correctly on chunk free */ + + ti->evp_md_ctx = EVP_MD_CTX_create(); + if (!ti->evp_md_ctx) { + tls_strerror_printf(true, NULL); + PERROR("%s: Failed allocating EVP_MD_CTX", __FUNCTION__); + return -1; + } + + ti->evp_md_ctx = talloc_steal(ti, ti->evp_md_ctx); /* Bind lifetime to instance */ + talloc_set_destructor(ti->evp_md_ctx, _evp_md_ctx_free); /* Free ctx correctly on chunk free */ + + MEM(ti->digest_buff = talloc_array(ti, uint8_t, EVP_MD_size(inst->rsa->sig_digest))); + + return 0; +} + +static int mod_thread_instantiate(CONF_SECTION const *conf, void *instance, + fr_event_list_t *el, void *thread) +{ + rlm_cipher_t *inst = talloc_get_type_abort(instance, rlm_cipher_t); + + switch (inst->type) { + case RLM_CIPHER_TYPE_RSA: + return cipher_rsa_thread_instantiate(conf, instance, el, thread); + + case RLM_CIPHER_TYPE_INVALID: + rad_assert(0); + } + + return 0; +} + +/* + * Do any per-module initialization that is separate to each + * configured instance of the module. e.g. set up connections + * to external databases, read configuration files, set up + * dictionary entries, etc. + */ +static int mod_bootstrap(void *instance, CONF_SECTION *conf) +{ + rlm_cipher_t *inst = talloc_get_type_abort(instance, rlm_cipher_t); + + inst->xlat_name = cf_section_name2(conf); + if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); + + switch (inst->type) { + case RLM_CIPHER_TYPE_RSA: + if (!inst->rsa) { + cf_log_err(conf, "type = rsa, but no 'rsa { ... }' configuration section provided"); + return -1; + } + + if (!inst->rsa->private_key_file && !inst->rsa->certificate_file) { + cf_log_err(conf, "type = rsa, but neither " + "'private_key_file' nor 'certificate_file' configured"); + return -1; + } + + if (inst->rsa->private_key_file) { + char *decrypt_name; + char *verify_name; + + decrypt_name = talloc_asprintf(inst, "%s_decrypt", inst->xlat_name); + verify_name = talloc_asprintf(inst, "%s_verify", inst->xlat_name); + + xlat_async_register(inst, decrypt_name, cipher_rsa_decrypt_xlat, + cipher_xlat_instantiate, rlm_cipher_t *, NULL, + cipher_xlat_thread_instantiate, rlm_cipher_rsa_thread_inst_t *, + NULL, inst); + + xlat_async_register(inst, verify_name, cipher_rsa_verify_xlat, + cipher_xlat_instantiate, rlm_cipher_t *, NULL, + cipher_xlat_thread_instantiate, rlm_cipher_rsa_thread_inst_t *, + NULL, inst); + + talloc_free(decrypt_name); + talloc_free(verify_name); + } + + if (inst->rsa->certificate_file) { + char *encrypt_name; + char *sign_name; + + encrypt_name = talloc_asprintf(inst, "%s_encrypt", inst->xlat_name); + sign_name = talloc_asprintf(inst, "%s_sign", inst->xlat_name); + + xlat_async_register(inst, encrypt_name, cipher_rsa_encrypt_xlat, + cipher_xlat_instantiate, rlm_cipher_t *, NULL, + cipher_xlat_thread_instantiate, rlm_cipher_rsa_thread_inst_t *, + NULL, inst); + xlat_async_register(inst, sign_name, cipher_rsa_sign_xlat, + cipher_xlat_instantiate, rlm_cipher_t *, NULL, + cipher_xlat_thread_instantiate, rlm_cipher_rsa_thread_inst_t *, + NULL, inst); + + talloc_free(encrypt_name); + talloc_free(sign_name); + } + break; + + /* + * Populated by cipher_type_parse() so if + * the value is unrecognised we've got an issue. + */ + default: + rad_assert(0); + return -1; + }; + + return 0; +} + +/* + * The module name should be the only globally exported symbol. + * That is, everything else should be 'static'. + * + * If the module needs to temporarily modify it's instantiation + * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. + * The server will then take care of ensuring that the module + * is single-threaded. + */ +extern rad_module_t rlm_cipher; +rad_module_t rlm_cipher = { + .magic = RLM_MODULE_INIT, + .name = "cipher", + .type = RLM_TYPE_THREAD_SAFE, + .inst_size = sizeof(rlm_cipher_t), + .config = module_config, + .bootstrap = mod_bootstrap, + .thread_instantiate = mod_thread_instantiate, +}; diff --git a/src/tests/modules/cipher/module.conf b/src/tests/modules/cipher/module.conf new file mode 100644 index 00000000000..46c904833f2 --- /dev/null +++ b/src/tests/modules/cipher/module.conf @@ -0,0 +1,18 @@ +cipher rsa { + rsa { + private_key_password = whatever + private_key_file = $ENV{MODULE_TEST_DIR}/server.key + certificate_file = $ENV{MODULE_TEST_DIR}/server.crt + + oaep { + oaep_digest = "sha256" + mgf1_digest = "sha256" + label = "freeradius" + } + + signature_digest = "sha256" + padding_type = oaep + } + + type = rsa +} diff --git a/src/tests/modules/cipher/rsa_encrypt_decrypt.unlang b/src/tests/modules/cipher/rsa_encrypt_decrypt.unlang new file mode 100644 index 00000000000..5d1b57d5e6c --- /dev/null +++ b/src/tests/modules/cipher/rsa_encrypt_decrypt.unlang @@ -0,0 +1,46 @@ +update request { + &Tmp-String-0 := "Hello world!" +} + +update request { + &Tmp-Octets-0 := "%{cipher_rsa_encrypt:%{Tmp-String-0}}" +} + +if (!&Tmp-Octets-0) { + test_fail +} +else { + test_pass +} + +if (<octets>&Tmp-String-0[0] == &Tmp-Octets-0[0]) { + test_fail +} +else { + test_pass +} + +update request { + &Tmp-String-1 := "%{cipher_rsa_decrypt:%{Tmp-Octets-0}}" +} + +if (&Tmp-String-0 != &Tmp-String-1) { + test_fail +} +else { + test_pass +} + +# +# Padding scheme should ensure ciphertext is not consistent +# +update request { + &Tmp-Octets-1 := "%{cipher_rsa_encrypt:%{Tmp-String-0}}" +} + +if (&Tmp-Octets-0 == &Tmp-Octets-1) { + test_fail +} +else { + test_pass +} diff --git a/src/tests/modules/cipher/server.crt b/src/tests/modules/cipher/server.crt new file mode 100644 index 00000000000..ff5f87afdc6 --- /dev/null +++ b/src/tests/modules/cipher/server.crt @@ -0,0 +1,89 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc/emailAddress=admin@example.org, CN=Example Certificate Authority + Validity + Not Before: Feb 21 10:35:43 2018 GMT + Not After : Apr 22 10:35:43 2018 GMT + Subject: C=FR, ST=Radius, O=Example Inc, CN=Example Server Certificate/emailAddress=admin@example.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c2:fe:ba:f8:2e:3c:0b:a8:a7:fe:1d:14:c2:99: + e8:5b:9e:a2:ec:e1:41:8f:5f:c9:1f:39:5e:ef:29: + d2:66:3c:bf:ab:19:fd:5d:0d:46:8d:d9:77:23:26: + 60:cf:25:30:63:f9:01:01:1e:96:74:8e:e9:31:97: + 52:44:21:ea:7f:e3:bd:8e:b2:cd:da:55:0a:f7:4f: + 7a:82:52:58:be:ed:95:04:a7:ea:ad:81:1b:b5:86: + 30:fe:c7:7f:41:ab:db:61:a9:03:19:79:0a:e9:cc: + 6d:68:02:56:71:50:f2:25:1d:73:8a:9f:ef:9d:2c: + a7:d3:20:95:b3:0a:41:c4:12:0e:df:60:ac:e9:d8: + 64:08:02:95:f8:54:91:18:7e:e2:36:13:84:f6:aa: + cf:0c:c8:64:1c:d8:b8:e4:4e:ee:55:fa:eb:21:80: + 40:f0:28:60:52:ab:8a:6d:e4:23:61:bd:ff:cb:24: + da:c5:ff:0e:92:5c:23:fa:c2:f0:84:2f:7b:a4:d8: + cb:a5:33:a6:b0:45:63:c0:d5:ba:d6:8f:40:a2:3b: + 31:fd:82:12:59:81:7e:66:8d:19:de:0d:f3:16:07: + 86:a6:b2:51:06:b8:84:ca:49:75:fb:99:73:27:77: + c1:53:a6:f6:d2:9c:16:57:4f:e6:1b:a8:27:23:79: + 9f:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Authority Information Access: + OCSP - URI:http://www.example.org/ocsp + + Signature Algorithm: sha256WithRSAEncryption + 16:38:3c:13:4d:0a:d9:d2:29:f5:e7:6b:97:7a:ff:61:fb:6a: + 4f:c6:ad:9c:93:67:16:f9:e4:49:00:92:36:06:80:bb:e4:19: + 29:82:28:8a:ca:fa:11:d4:d6:14:78:45:50:a5:e7:5f:6d:1c: + 42:e4:c4:26:92:27:ea:01:a1:34:b7:43:84:5c:52:78:89:1b: + 6d:0c:f2:ae:92:83:d5:54:82:da:ef:a3:d7:93:f8:58:98:35: + 6d:24:ce:b9:52:bf:16:52:76:6d:f6:66:a5:4c:76:a5:73:d2: + 81:fb:0f:3a:45:5b:9e:5e:24:4e:63:cf:15:38:8d:ad:79:98: + 71:c7:48:e0:c3:fe:a2:86:ed:c1:ac:3e:67:fe:44:45:21:06: + f0:a0:33:3e:94:7c:ca:dd:e1:20:f8:b5:18:0b:53:f9:ae:4b: + c5:0d:63:73:d5:2a:35:bb:3d:3a:03:28:ea:7e:26:35:98:81: + f3:93:9b:81:92:b6:a6:6b:c5:f6:0d:a2:52:54:e5:51:a8:c3: + 18:ed:45:c9:bc:af:21:76:66:21:fb:2d:e4:7b:a0:96:d3:6f: + 62:d3:ff:e3:14:35:85:f9:4b:c2:d3:ea:7a:49:00:3d:f7:bd: + 1c:2f:1a:ba:0c:31:26:65:d7:5a:a7:d7:ce:be:d4:3d:c1:07: + aa:58:c6:1e +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBkjELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRQwEgYDVQQKDAtF +eGFtcGxlIEluYzEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxJjAk +BgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE4MDIyMTEw +MzU0M1oXDTE4MDQyMjEwMzU0M1owezELMAkGA1UEBhMCRlIxDzANBgNVBAgMBlJh +ZGl1czEUMBIGA1UECgwLRXhhbXBsZSBJbmMxIzAhBgNVBAMMGkV4YW1wbGUgU2Vy +dmVyIENlcnRpZmljYXRlMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9y +ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML+uvguPAuop/4dFMKZ +6FueouzhQY9fyR85Xu8p0mY8v6sZ/V0NRo3ZdyMmYM8lMGP5AQEelnSO6TGXUkQh +6n/jvY6yzdpVCvdPeoJSWL7tlQSn6q2BG7WGMP7Hf0Gr22GpAxl5CunMbWgCVnFQ +8iUdc4qf750sp9MglbMKQcQSDt9grOnYZAgClfhUkRh+4jYThPaqzwzIZBzYuORO +7lX66yGAQPAoYFKrim3kI2G9/8sk2sX/DpJcI/rC8IQve6TYy6UzprBFY8DVutaP +QKI7Mf2CElmBfmaNGd4N8xYHhqayUQa4hMpJdfuZcyd3wVOm9tKcFldP5huoJyN5 +nzkCAwEAAaOBoTCBnjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DATBgNVHSUEDDAK +BggrBgEFBQcDATA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUu +Y29tL2V4YW1wbGVfY2EuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYb +aHR0cDovL3d3dy5leGFtcGxlLm9yZy9vY3NwMA0GCSqGSIb3DQEBCwUAA4IBAQAW +ODwTTQrZ0in152uXev9h+2pPxq2ck2cW+eRJAJI2BoC75BkpgiiKyvoR1NYUeEVQ +pedfbRxC5MQmkifqAaE0t0OEXFJ4iRttDPKukoPVVILa76PXk/hYmDVtJM65Ur8W +UnZt9malTHalc9KB+w86RVueXiROY88VOI2teZhxx0jgw/6ihu3BrD5n/kRFIQbw +oDM+lHzK3eEg+LUYC1P5rkvFDWNz1So1uz06AyjqfiY1mIHzk5uBkrama8X2DaJS +VOVRqMMY7UXJvK8hdmYh+y3ke6CW029i0//jFDWF+UvC0+p6SQA9970cLxq6DDEm +Zddap9fOvtQ9wQeqWMYe +-----END CERTIFICATE----- diff --git a/src/tests/modules/cipher/server.key b/src/tests/modules/cipher/server.key new file mode 100644 index 00000000000..deb31ec115d --- /dev/null +++ b/src/tests/modules/cipher/server.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIX+r9DJvjng0CAggA +MB0GCWCGSAFlAwQBKgQQvW/Ms0F6ZLE6m8rlqPUxMQSCBNA3QYVOcz570/x1PQOz +WYg8WEFaxp/eC2+hZndjdLFWWSgOr/I17+RW13zD5rWodlUH+zAt4gHpjmGDh6om +COUDCT6BbZDlV/Fdh9hQ5LP9YXC4nRofxXxr9281rvMaYd1ZBzNRoR2+z0MXW21u +Y+wN9aVPHm0ghlCK28uItCHU0yPuacG9wvVJesATD8j9qy6vaiH0Y/7a8dZWA9IW +BWQeGRXzyUnWXdLZFSs/6JBZm2o+RMja7YCHpGcWORDNvN8i4vYgG8/ZbRJcqCUs +2yegib2qdKAMCuEh8wHGssgoh4WRkeGVInNKRCbHlnBgdCa3dx78ZPnbepgQclhC +opaU7EshE7BUsy22rnjtWypRxpCSoncUCS0bsubqyyRb1A0TpcVQTuQ9XARhydJd +zz9YuvaibytWr5mPXd/nvtv5HFIiT4gmNCV9o+xp44MEly6IYUKOMxzva1ATrNoU +ojUPRPhiicLe+cUqE0Ap1kHM2ddJyWCqvQ1WPvvVMsdBYJBzo+jSXzhFgLCwZ9RU +KvG31E8VDoU7nbGF+5UUBExvEHM0wuGb71U7aDbBXj0rnR1ReUxcdF2DBbYSMubJ +Fq98+6OojMSvP1VzbNQfTvhU2qIXAIbk8vFs+66CMq42I5x+Zva6xZGJsB9PlzXb +fnYqk1YWtipozoDbvcArF0BnVsHj4klmybBmNh+nj4cZAXKAowdVrliE96smCMWl +6sejZNPyWyz2Nr1tiXNzVASXrpldj41Rm9iTuMZ1x3UihkJ9HnzsQ7NPXXK9q2KV +icHnHdTo88XR4fudxHBmxSjIExrDx3+PTWwWbkPawxs1ekLp5ECuARLPwwD20/RQ +tGIGWX2Ez0+nROIlYIZradYXx7BmxIuQftZ+uRQXSsK1p1VROmg1v7nEpI1BXrJW +6Zw1bJmtos2yo3guDtmtyvP3wjZDJkrFSZwVvYrZ0EuDtMuuWxk/3L3jLoUm7omt +tHcF7wBQrsEPD8eER9gpU7vKZkenjWateoUKJMSkqp1IKqVpP7BuaZuhZKE3P31y +22P5LDsubZNsud4iUZDVFr7zl3ERHhflPJdjT1rXRkAjM0937SVaxmW36wXacaUP +QHkdei9zvPbsLdwAJqwbEJWRs+2aQ3qxYtjtlSPQqbEVgMA84++gqU/XvtJv+ao0 +AFoKq3AE/LB5Hvsswh7ZpQBORoKoZwPY1i/vfmxDR6hXlHYhFZoz2Gra8PCiBqud +zrrp8gbKH0S0aTOceo/2NmJhhaBHFqgV14IpeJnVsWTfwtUmkItkHM2/s87+fF9Q +XfNRK78MoAXQVsgeU40WcgbPhDSg7/H87Ms52TQTfNvDi+H3WPRja9V0GhV7KyYG +Kszg6b8a+DWYy/UzAyKM7O1kqw15wUbops9rWdQ1Clqpccaa8rFu7plsywHUcb0x +msU3EspnkemLN6VKf1S3EXM/AEmb2rGsgWo4x7Qyadsri9FWBjMhHj5zDf/y4RdK +givdegKG1i+MCiWwCwDViwAEFeMBKC6shZF5yygZdjVFKahoVVat6V9ZYU/p5Xmi +xq4R6a5iWfeTk0K9wKtZcjwB/GY5p2q7O4tmHY5EomALO1rN8lzlY0ZfKef3oMak +TazRKf9MQYyZhX7dMQHF/P1zsA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/tests/modules/cipher/server.pem b/src/tests/modules/cipher/server.pem new file mode 100644 index 00000000000..9ee0eb8e7e2 --- /dev/null +++ b/src/tests/modules/cipher/server.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBkjELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRQwEgYDVQQKDAtF +eGFtcGxlIEluYzEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxJjAk +BgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE4MDIyMTEw +MzU0M1oXDTE4MDQyMjEwMzU0M1owezELMAkGA1UEBhMCRlIxDzANBgNVBAgMBlJh +ZGl1czEUMBIGA1UECgwLRXhhbXBsZSBJbmMxIzAhBgNVBAMMGkV4YW1wbGUgU2Vy +dmVyIENlcnRpZmljYXRlMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9y +ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML+uvguPAuop/4dFMKZ +6FueouzhQY9fyR85Xu8p0mY8v6sZ/V0NRo3ZdyMmYM8lMGP5AQEelnSO6TGXUkQh +6n/jvY6yzdpVCvdPeoJSWL7tlQSn6q2BG7WGMP7Hf0Gr22GpAxl5CunMbWgCVnFQ +8iUdc4qf750sp9MglbMKQcQSDt9grOnYZAgClfhUkRh+4jYThPaqzwzIZBzYuORO +7lX66yGAQPAoYFKrim3kI2G9/8sk2sX/DpJcI/rC8IQve6TYy6UzprBFY8DVutaP +QKI7Mf2CElmBfmaNGd4N8xYHhqayUQa4hMpJdfuZcyd3wVOm9tKcFldP5huoJyN5 +nzkCAwEAAaOBoTCBnjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DATBgNVHSUEDDAK +BggrBgEFBQcDATA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUu +Y29tL2V4YW1wbGVfY2EuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYb +aHR0cDovL3d3dy5leGFtcGxlLm9yZy9vY3NwMA0GCSqGSIb3DQEBCwUAA4IBAQAW +ODwTTQrZ0in152uXev9h+2pPxq2ck2cW+eRJAJI2BoC75BkpgiiKyvoR1NYUeEVQ +pedfbRxC5MQmkifqAaE0t0OEXFJ4iRttDPKukoPVVILa76PXk/hYmDVtJM65Ur8W +UnZt9malTHalc9KB+w86RVueXiROY88VOI2teZhxx0jgw/6ihu3BrD5n/kRFIQbw +oDM+lHzK3eEg+LUYC1P5rkvFDWNz1So1uz06AyjqfiY1mIHzk5uBkrama8X2DaJS +VOVRqMMY7UXJvK8hdmYh+y3ke6CW029i0//jFDWF+UvC0+p6SQA9970cLxq6DDEm +Zddap9fOvtQ9wQeqWMYe +-----END CERTIFICATE-----