...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and * packet encryption, packet authentication, and
* packet compression. * packet compression.
* *
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "crypto_mbedtls.h" #include "crypto_mbedtls.h"
#endif #endif
#include "basic.h" #include "basic.h"
#include "buffer.h"
/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */ /* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
#define OPENVPN_AEAD_TAG_LENGTH 16 #define OPENVPN_AEAD_TAG_LENGTH 16
...@@ -50,7 +51,7 @@ ...@@ -50,7 +51,7 @@
typedef enum { typedef enum {
MD_SHA1, MD_SHA1,
MD_SHA256 MD_SHA256
} hash_algo_type ; } hash_algo_type;
/** Struct used in cipher name translation table */ /** Struct used in cipher name translation table */
typedef struct { typedef struct {
...@@ -77,6 +78,21 @@ void crypto_clear_error(void); ...@@ -77,6 +78,21 @@ void crypto_clear_error(void);
*/ */
void crypto_init_lib_engine(const char *engine_name); void crypto_init_lib_engine(const char *engine_name);
/**
* Load the given (OpenSSL) providers
* @param provider name of providers to load
* @return reference to the loaded provider
*/
provider_t *crypto_load_provider(const char *provider);
/**
* Unloads the given (OpenSSL) provider
* @param provname name of the provider to unload
* @param provider pointer to the provider to unload
*/
void crypto_unload_provider(const char *provname, provider_t *provider);
#ifdef DMALLOC #ifdef DMALLOC
/* /*
* OpenSSL memory debugging. If dmalloc debugging is enabled, tell * OpenSSL memory debugging. If dmalloc debugging is enabled, tell
...@@ -87,24 +103,40 @@ void crypto_init_dmalloc(void); ...@@ -87,24 +103,40 @@ void crypto_init_dmalloc(void);
#endif /* DMALLOC */ #endif /* DMALLOC */
/**
* Translate a data channel cipher name from the OpenVPN config file
* 'language' to the crypto library specific name.
*/
const char *translate_cipher_name_from_openvpn(const char *cipher_name);
/**
* Translate a data channel cipher name from the crypto library specific name
* to the OpenVPN config file 'language'.
*/
const char *translate_cipher_name_from_openvpn(const char *cipher_name);
void show_available_ciphers(void); void show_available_ciphers(void);
void show_available_digests(void); void show_available_digests(void);
void show_available_engines(void); void show_available_engines(void);
/**
* Encode binary data as PEM.
*
* @param name The name to use in the PEM header/footer.
* @param dst Destination buffer for PEM-encoded data. Must be a valid
* pointer to an uninitialized buffer structure. Iff this
* function returns true, the buffer will contain memory
* allocated through the supplied gc.
* @param src Source buffer.
* @param gc The garbage collector to use when allocating memory for dst.
*
* @return true iff PEM encode succeeded.
*/
bool crypto_pem_encode(const char *name, struct buffer *dst,
const struct buffer *src, struct gc_arena *gc);
/**
* Decode a PEM buffer to binary data.
*
* @param name The name expected in the PEM header/footer.
* @param dst Destination buffer for decoded data.
* @param src Source buffer (PEM data).
*
* @return true iff PEM decode succeeded.
*/
bool crypto_pem_decode(const char *name, struct buffer *dst,
const struct buffer *src);
/* /*
* *
* Random number functions, used in cases where we want * Random number functions, used in cases where we want
...@@ -125,43 +157,6 @@ void show_available_engines(void); ...@@ -125,43 +157,6 @@ void show_available_engines(void);
*/ */
int rand_bytes(uint8_t *output, int len); int rand_bytes(uint8_t *output, int len);
/*
*
* Key functions, allow manipulation of keys.
*
*/
/**
* Return number of DES cblocks (1 cblock = length of a single-DES key) for the
* current key type or 0 if not a DES cipher.
*
* @param kt Type of key
*
* @return Number of DES cblocks that the key consists of, or 0.
*/
int key_des_num_cblocks(const cipher_kt_t *kt);
/*
* Check the given DES key. Checks the given key's length, weakness and parity.
*
* @param key Key to check
* @param key_len Length of the key, in bytes
* @param ndc Number of DES cblocks that the key is made up of.
*
* @return \c true if the key is valid, \c false otherwise.
*/
bool key_des_check(uint8_t *key, int key_len, int ndc);
/*
* Fix the given DES key, setting its parity to odd.
*
* @param key Key to check
* @param key_len Length of the key, in bytes
* @param ndc Number of DES cblocks that the key is made up of.
*/
void key_des_fixup(uint8_t *key, int key_len, int ndc);
/** /**
* Encrypt the given block, using DES ECB mode * Encrypt the given block, using DES ECB mode
* *
...@@ -193,104 +188,136 @@ void cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], ...@@ -193,104 +188,136 @@ void cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
#define MAX_CIPHER_KEY_LENGTH 64 #define MAX_CIPHER_KEY_LENGTH 64
/** /**
* Return cipher parameters, based on the given cipher name. The * Returns if the cipher is valid, based on the given cipher name and provides a
* contents of these parameters are library-specific, and can be used to * reason if invalid.
* initialise encryption/decryption.
* *
* @param ciphername Name of the cipher to retrieve parameters for (e.g. * @param ciphername Name of the cipher to check for validity (e.g.
* \c AES-128-CBC). * \c AES-128-CBC). Will be translated to the library name
* from the openvpn config name if needed.
* @param reason Pointer where a static string indicating the reason
* for rejecting the cipher should be stored. It is set to
* NULL if the cipher is valid.
* *
* @return A statically allocated structure containing parameters * @return if the cipher is valid
* for the given cipher, or NULL if no matching parameters
* were found.
*/ */
const cipher_kt_t *cipher_kt_get(const char *ciphername); bool cipher_valid_reason(const char *ciphername, const char **reason);
/** /**
* Retrieve a string describing the cipher (e.g. \c AES-128-CBC). * Returns if the cipher is valid, based on the given cipher name.
*
* @param ciphername Name of the cipher to check for validity (e.g.
* \c AES-128-CBC). Will be translated to the library name
* from the openvpn config name if needed.
* *
* @param cipher_kt Static cipher parameters * @return if the cipher is valid
*/
static inline bool
cipher_valid(const char *ciphername)
{
const char *reason;
return cipher_valid_reason(ciphername, &reason);
}
/**
* Checks if the cipher is defined and is not the null (none) cipher
*
* @param ciphername Name of the cipher to check if it is defined, may not
* be NULL
* @return The cipher is defined and not the null (none) cipher
*/
static inline bool
cipher_defined(const char *ciphername)
{
ASSERT(ciphername);
return strcmp(ciphername, "none") != 0;
}
/**
* Retrieve a normalised string describing the cipher (e.g. \c AES-128-CBC).
* The returned name is normalised to the OpenVPN config name in case the
* name differs from the name used by the crypto library.
*
* Returns [null-cipher] in case the ciphername is none. NULL if the cipher
* is not valid.
*
* @param ciphername Name of the cipher
* *
* @return a statically allocated string describing the cipher. * @return a statically allocated string describing the cipher.
*/ */
const char *cipher_kt_name(const cipher_kt_t *cipher_kt); const char *cipher_kt_name(const char *ciphername);
/** /**
* Returns the size of keys used by the cipher, in bytes. If the cipher has a * Returns the size of keys used by the cipher, in bytes. If the cipher has a
* variable key size, return the default key size. * variable key size, return the default key size.
* *
* @param cipher_kt Static cipher parameters * @param ciphername Cipher name to lookup
* *
* @return (Default) size of keys used by the cipher, in bytes. * @return (Default) size of keys used by the cipher, in bytes.
*/ */
int cipher_kt_key_size(const cipher_kt_t *cipher_kt); int cipher_kt_key_size(const char *ciphername);
/** /**
* Returns the size of the IV used by the cipher, in bytes, or 0 if no IV is * Returns the size of the IV used by the cipher, in bytes, or 0 if no IV is
* used. * used.
* *
* @param cipher_kt Static cipher parameters * @param ciphername cipher name to lookup
* *
* @return Size of the IV, in bytes, or 0 if the cipher does not * @return Size of the IV, in bytes, or 0 if the cipher does not
* use an IV. * use an IV.
*/ */
int cipher_kt_iv_size(const cipher_kt_t *cipher_kt); int cipher_kt_iv_size(const char *ciphername);
/** /**
* Returns the block size of the cipher, in bytes. * Returns the block size of the cipher, in bytes.
* *
* @param cipher_kt Static cipher parameters * @param ciphername cipher name
* *
* @return Block size, in bytes. * @return Block size, in bytes.
*/ */
int cipher_kt_block_size(const cipher_kt_t *cipher_kt); int cipher_kt_block_size(const char *ciphername);
/** /**
* Returns the MAC tag size of the cipher, in bytes. * Returns the MAC tag size of the cipher, in bytes.
* *
* @param ctx Static cipher parameters. * @param ciphername Name of the cipher
* *
* @return Tag size in bytes, or 0 if the tag size could not be * @return Tag size in bytes, or 0 if the tag size could not be
* determined. * determined.
*/ */
int cipher_kt_tag_size(const cipher_kt_t *cipher_kt); int cipher_kt_tag_size(const char *ciphername);
/** /**
* Returns the mode that the cipher runs in. * Returns true if we consider this cipher to be insecure.
*
* @param cipher_kt Static cipher parameters. May not be NULL.
*
* @return Cipher mode, either \c OPENVPN_MODE_CBC, \c
* OPENVPN_MODE_OFB or \c OPENVPN_MODE_CFB
*/ */
int cipher_kt_mode(const cipher_kt_t *cipher_kt); bool cipher_kt_insecure(const char *ciphername);
/** /**
* Check if the supplied cipher is a supported CBC mode cipher. * Check if the supplied cipher is a supported CBC mode cipher.
* *
* @param cipher Static cipher parameters. * @param ciphername cipher name
* *
* @return true iff the cipher is a CBC mode cipher. * @return true iff the cipher is a CBC mode cipher.
*/ */
bool cipher_kt_mode_cbc(const cipher_kt_t *cipher); bool cipher_kt_mode_cbc(const char *ciphername);
/** /**
* Check if the supplied cipher is a supported OFB or CFB mode cipher. * Check if the supplied cipher is a supported OFB or CFB mode cipher.
* *
* @param cipher Static cipher parameters. * @param ciphername cipher name
* *
* @return true iff the cipher is a OFB or CFB mode cipher. * @return true iff the cipher is a OFB or CFB mode cipher.
*/ */
bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher); bool cipher_kt_mode_ofb_cfb(const char *ciphername);
/** /**
* Check if the supplied cipher is a supported AEAD mode cipher. * Check if the supplied cipher is a supported AEAD mode cipher.
* *
* @param cipher Static cipher parameters. * @param ciphername name of the cipher
* *
* @return true iff the cipher is a AEAD mode cipher. * @return true iff the cipher is a AEAD mode cipher.
*/ */
bool cipher_kt_mode_aead(const cipher_kt_t *cipher); bool cipher_kt_mode_aead(const char *ciphername);
/** /**
...@@ -318,13 +345,12 @@ void cipher_ctx_free(cipher_ctx_t *ctx); ...@@ -318,13 +345,12 @@ void cipher_ctx_free(cipher_ctx_t *ctx);
* *
* @param ctx Cipher context. May not be NULL * @param ctx Cipher context. May not be NULL
* @param key Buffer containing the key to use * @param key Buffer containing the key to use
* @param key_len Length of the key, in bytes * @param ciphername Ciphername of the cipher to use
* @param kt Static cipher parameters to use
* @param enc Whether to encrypt or decrypt (either * @param enc Whether to encrypt or decrypt (either
* \c MBEDTLS_OP_ENCRYPT or \c MBEDTLS_OP_DECRYPT). * \c MBEDTLS_OP_ENCRYPT or \c MBEDTLS_OP_DECRYPT).
*/ */
void cipher_ctx_init(cipher_ctx_t *ctx, const uint8_t *key, int key_len, void cipher_ctx_init(cipher_ctx_t *ctx, const uint8_t *key,
const cipher_kt_t *kt, int enc); const char *cipername, int enc);
/** /**
* Returns the size of the IV used by the cipher, in bytes, or 0 if no IV is * Returns the size of the IV used by the cipher, in bytes, or 0 if no IV is
...@@ -333,7 +359,7 @@ void cipher_ctx_init(cipher_ctx_t *ctx, const uint8_t *key, int key_len, ...@@ -333,7 +359,7 @@ void cipher_ctx_init(cipher_ctx_t *ctx, const uint8_t *key, int key_len,
* @param ctx The cipher's context * @param ctx The cipher's context
* *
* @return Size of the IV, in bytes, or \c 0 if the cipher does not * @return Size of the IV, in bytes, or \c 0 if the cipher does not
* use an IV or ctx was NULL. * use an IV.
*/ */
int cipher_ctx_iv_length(const cipher_ctx_t *ctx); int cipher_ctx_iv_length(const cipher_ctx_t *ctx);
...@@ -366,14 +392,31 @@ int cipher_ctx_block_size(const cipher_ctx_t *ctx); ...@@ -366,14 +392,31 @@ int cipher_ctx_block_size(const cipher_ctx_t *ctx);
int cipher_ctx_mode(const cipher_ctx_t *ctx); int cipher_ctx_mode(const cipher_ctx_t *ctx);
/** /**
* Returns the static cipher parameters for this context. * Check if the supplied cipher is a supported CBC mode cipher.
*
* @param ctx Cipher's context. May not be NULL.
*
* @return true iff the cipher is a CBC mode cipher.
*/
bool cipher_ctx_mode_cbc(const cipher_ctx_t *ctx);
/**
* Check if the supplied cipher is a supported OFB or CFB mode cipher.
*
* @param ctx Cipher's context. May not be NULL.
*
* @return true iff the cipher is a OFB or CFB mode cipher.
*/
bool cipher_ctx_mode_ofb_cfb(const cipher_ctx_t *ctx);
/**
* Check if the supplied cipher is a supported AEAD mode cipher.
* *
* @param ctx Cipher's context. * @param ctx Cipher's context. May not be NULL.
* *
* @return Static cipher parameters for the supplied context, or * @return true iff the cipher is a AEAD mode cipher.
* NULL if unable to determine cipher parameters.
*/ */
const cipher_kt_t *cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx); bool cipher_ctx_mode_aead(const cipher_ctx_t *ctx);
/** /**
* Resets the given cipher context, setting the IV to the specified value. * Resets the given cipher context, setting the IV to the specified value.
...@@ -384,7 +427,7 @@ const cipher_kt_t *cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx); ...@@ -384,7 +427,7 @@ const cipher_kt_t *cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx);
* *
* @return \c 0 on failure, \c 1 on success. * @return \c 0 on failure, \c 1 on success.
*/ */
int cipher_ctx_reset(cipher_ctx_t *ctx, uint8_t *iv_buf); int cipher_ctx_reset(cipher_ctx_t *ctx, const uint8_t *iv_buf);
/** /**
* Updates the given cipher context, providing additional data (AD) for * Updates the given cipher context, providing additional data (AD) for
...@@ -463,36 +506,45 @@ int cipher_ctx_final_check_tag(cipher_ctx_t *ctx, uint8_t *dst, int *dst_len, ...@@ -463,36 +506,45 @@ int cipher_ctx_final_check_tag(cipher_ctx_t *ctx, uint8_t *dst, int *dst_len,
#define MAX_HMAC_KEY_LENGTH 64 #define MAX_HMAC_KEY_LENGTH 64
/** /**
* Return message digest parameters, based on the given digest name. The * Checks if the cipher is defined and is not the null (none) cipher
* contents of these parameters are library-specific, and can be used to *
* initialise HMAC or message digest operations. * @param mdname Name of the digest
* @return
*/
static inline bool
md_defined(const char *mdname)
{
return strcmp(mdname, "none") != 0;
}
/**
* Return if a message digest parameters is valid given the name of the digest.
* *
* @param digest Name of the digest to retrieve parameters for (e.g. * @param digest Name of the digest to verify, e.g. \c MD5).
* \c MD5).
* *
* @return A statically allocated structure containing parameters * @return Whether a digest of the given name is available
* for the given message digest.
*/ */
const md_kt_t *md_kt_get(const char *digest); bool md_valid(const char *digest);
/** /**
* Retrieve a string describing the digest digest (e.g. \c SHA1). * Retrieve a string describing the digest digest (e.g. \c SHA1).
* *
* @param kt Static message digest parameters * @param mdname Message digest name
* *
* @return Statically allocated string describing the message * @return Statically allocated string describing the message
* digest. * digest.
*/ */
const char *md_kt_name(const md_kt_t *kt); const char *md_kt_name(const char *mdname);
/** /**
* Returns the size of the message digest, in bytes. * Returns the size of the message digest, in bytes.
* *
* @param kt Static message digest parameters * @param mdname Message digest name
* *
* @return Message digest size, in bytes, or 0 if ctx was NULL. * @return Message digest size, in bytes, or 0 if ctx was NULL.
*/ */
int md_kt_size(const md_kt_t *kt); unsigned char md_kt_size(const char *mdname);
/* /*
...@@ -501,17 +553,17 @@ int md_kt_size(const md_kt_t *kt); ...@@ -501,17 +553,17 @@ int md_kt_size(const md_kt_t *kt);
* *
*/ */
/* /**
* Calculates the message digest for the given buffer. * Calculates the message digest for the given buffer.
* *
* @param kt Static message digest parameters * @param mdname message digest name
* @param src Buffer to digest. May not be NULL. * @param src Buffer to digest. May not be NULL.
* @param src_len The length of the incoming buffer. * @param src_len The length of the incoming buffer.
* @param dst Buffer to write the message digest to. May not be NULL. * @param dst Buffer to write the message digest to. May not be NULL.
* *
* @return \c 1 on success, \c 0 on failure * @return \c 1 on success, \c 0 on failure
*/ */
int md_full(const md_kt_t *kt, const uint8_t *src, int src_len, uint8_t *dst); int md_full(const char *mdname, const uint8_t *src, int src_len, uint8_t *dst);
/* /*
* Allocate a new message digest context * Allocate a new message digest context
...@@ -527,13 +579,13 @@ md_ctx_t *md_ctx_new(void); ...@@ -527,13 +579,13 @@ md_ctx_t *md_ctx_new(void);
*/ */
void md_ctx_free(md_ctx_t *ctx); void md_ctx_free(md_ctx_t *ctx);
/* /**
* Initialises the given message digest context. * Initialises the given message digest context.
* *
* @param ctx Message digest context * @param ctx Message digest context
* @param kt Static message digest parameters * @param mdname Message digest name
*/ */
void md_ctx_init(md_ctx_t *ctx, const md_kt_t *kt); void md_ctx_init(md_ctx_t *ctx, const char *mdname);
/* /*
* Free the given message digest context. * Free the given message digest context.
...@@ -593,14 +645,13 @@ void hmac_ctx_free(hmac_ctx_t *ctx); ...@@ -593,14 +645,13 @@ void hmac_ctx_free(hmac_ctx_t *ctx);
* Initialises the given HMAC context, using the given digest * Initialises the given HMAC context, using the given digest
* and key. * and key.
* *
* @param ctx HMAC context to intialise * @param ctx HMAC context to initialise
* @param key The key to use for the HMAC * @param key The key to use for the HMAC
* @param key_len The key length to use * @param mdname message digest name
* @param kt Static message digest parameters
* *
*/ */
void hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, int key_length, void hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, const char *mdname);
const md_kt_t *kt);
/* /*
* Free the given HMAC context. * Free the given HMAC context.
...@@ -616,7 +667,7 @@ void hmac_ctx_cleanup(hmac_ctx_t *ctx); ...@@ -616,7 +667,7 @@ void hmac_ctx_cleanup(hmac_ctx_t *ctx);
* *
* @return Size of the HMAC, or \0 if ctx is NULL. * @return Size of the HMAC, or \0 if ctx is NULL.
*/ */
int hmac_ctx_size(const hmac_ctx_t *ctx); int hmac_ctx_size(hmac_ctx_t *ctx);
/* /*
* Resets the given HMAC context, preserving the associated key information * Resets the given HMAC context, preserving the associated key information
...@@ -662,4 +713,21 @@ const char *translate_cipher_name_from_openvpn(const char *cipher_name); ...@@ -662,4 +713,21 @@ const char *translate_cipher_name_from_openvpn(const char *cipher_name);
*/ */
const char *translate_cipher_name_to_openvpn(const char *cipher_name); const char *translate_cipher_name_to_openvpn(const char *cipher_name);
/**
* Calculates the TLS 1.0-1.1 PRF function. For the exact specification of the
* function definition see the TLS RFCs like RFC 4346.
*
* @param seed seed to use
* @param seed_len length of the seed
* @param secret secret to use
* @param secret_len length of the secret
* @param output output destination
* @param output_len length of output/number of bytes to generate
*
* @return true if successful, false on any error
*/
bool ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
int secret_len, uint8_t *output, int output_len);
#endif /* CRYPTO_BACKEND_H_ */ #endif /* CRYPTO_BACKEND_H_ */
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and * packet encryption, packet authentication, and
* packet compression. * packet compression.
* *
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
...@@ -34,23 +34,26 @@ ...@@ -34,23 +34,26 @@
#include "syshead.h" #include "syshead.h"
#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) #if defined(ENABLE_CRYPTO_MBEDTLS)
#include "errlevel.h" #include "errlevel.h"
#include "basic.h" #include "basic.h"
#include "buffer.h" #include "buffer.h"
#include "crypto.h"
#include "integer.h" #include "integer.h"
#include "crypto_backend.h" #include "crypto_backend.h"
#include "otime.h" #include "otime.h"
#include "misc.h" #include "misc.h"
#include <mbedtls/base64.h>
#include <mbedtls/des.h> #include <mbedtls/des.h>
#include <mbedtls/error.h> #include <mbedtls/error.h>
#include <mbedtls/md5.h> #include <mbedtls/md5.h>
#include <mbedtls/cipher.h> #include <mbedtls/cipher.h>
#include <mbedtls/havege.h> #include <mbedtls/pem.h>
#include <mbedtls/entropy.h> #include <mbedtls/entropy.h>
#include <mbedtls/ssl.h>
/* /*
...@@ -66,6 +69,21 @@ crypto_init_lib_engine(const char *engine_name) ...@@ -66,6 +69,21 @@ crypto_init_lib_engine(const char *engine_name)
"available"); "available");
} }
provider_t *
crypto_load_provider(const char *provider)
{
if (provider)
{
msg(M_WARN, "Note: mbed TLS provider functionality is not available");
}
return NULL;
}
void
crypto_unload_provider(const char *provname, provider_t *provider)
{
}
/* /*
* *
* Functions related to the core crypto library * Functions related to the core crypto library
...@@ -138,26 +156,6 @@ const cipher_name_pair cipher_name_translation_table[] = { ...@@ -138,26 +156,6 @@ const cipher_name_pair cipher_name_translation_table[] = {
const size_t cipher_name_translation_table_count = const size_t cipher_name_translation_table_count =
sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table); sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table);
static void
print_cipher(const cipher_kt_t *info)
{
if (info && (cipher_kt_mode_cbc(info)
#ifdef HAVE_AEAD_CIPHER_MODES
|| cipher_kt_mode_aead(info)
#endif
))
{
const char *ssl_only = cipher_kt_mode_cbc(info) ?
"" : ", TLS client/server mode only";
const char *var_key_size = info->flags & MBEDTLS_CIPHER_VARIABLE_KEY_LEN ?
" by default" : "";
printf("%s (%d bit key%s, %d bit block%s)\n",
cipher_kt_name(info), cipher_kt_key_size(info) * 8, var_key_size,
cipher_kt_block_size(info) * 8, ssl_only);
}
}
void void
show_available_ciphers(void) show_available_ciphers(void)
{ {
...@@ -166,16 +164,18 @@ show_available_ciphers(void) ...@@ -166,16 +164,18 @@ show_available_ciphers(void)
#ifndef ENABLE_SMALL #ifndef ENABLE_SMALL
printf("The following ciphers and cipher modes are available for use\n" printf("The following ciphers and cipher modes are available for use\n"
"with " PACKAGE_NAME ". Each cipher shown below may be used as a\n" "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n"
"parameter to the --cipher option. Using a CBC or GCM mode is\n" "parameter to the --data-ciphers (or --cipher) option. Using a\n"
"recommended. In static key mode only CBC mode is allowed.\n\n"); "GCM or CBC mode is recommended. In static key mode only CBC\n"
"mode is allowed.\n\n");
#endif #endif
while (*ciphers != 0) while (*ciphers != 0)
{ {
const cipher_kt_t *info = mbedtls_cipher_info_from_type(*ciphers); const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type(*ciphers);
if (info && cipher_kt_block_size(info) >= 128/8) if (info && !cipher_kt_insecure(info->name)
&& (cipher_kt_mode_aead(info->name) || cipher_kt_mode_cbc(info->name)))
{ {
print_cipher(info); print_cipher(info->name);
} }
ciphers++; ciphers++;
} }
...@@ -185,10 +185,11 @@ show_available_ciphers(void) ...@@ -185,10 +185,11 @@ show_available_ciphers(void)
ciphers = mbedtls_cipher_list(); ciphers = mbedtls_cipher_list();
while (*ciphers != 0) while (*ciphers != 0)
{ {
const cipher_kt_t *info = mbedtls_cipher_info_from_type(*ciphers); const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type(*ciphers);
if (info && cipher_kt_block_size(info) < 128/8) if (info && cipher_kt_insecure(info->name)
&& (cipher_kt_mode_aead(info->name) || cipher_kt_mode_cbc(info->name)))
{ {
print_cipher(info); print_cipher(info->name);
} }
ciphers++; ciphers++;
} }
...@@ -229,6 +230,84 @@ show_available_engines(void) ...@@ -229,6 +230,84 @@ show_available_engines(void)
"available\n"); "available\n");
} }
bool
crypto_pem_encode(const char *name, struct buffer *dst,
const struct buffer *src, struct gc_arena *gc)
{
/* 1000 chars is the PEM line length limit (+1 for tailing NUL) */
char header[1000+1] = { 0 };
char footer[1000+1] = { 0 };
if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----\n", name))
{
return false;
}
if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----\n", name))
{
return false;
}
size_t out_len = 0;
if (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL !=
mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src),
NULL, 0, &out_len))
{
return false;
}
/* We set the size buf to out_len-1 to NOT include the 0 byte that
* mbedtls_pem_write_buffer in its length calculation */
*dst = alloc_buf_gc(out_len, gc);
if (!mbed_ok(mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src),
BPTR(dst), BCAP(dst), &out_len))
|| !buf_inc_len(dst, out_len-1))
{
CLEAR(*dst);
return false;
}
return true;
}
bool
crypto_pem_decode(const char *name, struct buffer *dst,
const struct buffer *src)
{
/* 1000 chars is the PEM line length limit (+1 for tailing NUL) */
char header[1000+1] = { 0 };
char footer[1000+1] = { 0 };
if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----", name))
{
return false;
}
if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----", name))
{
return false;
}
/* mbed TLS requires the src to be null-terminated */
/* allocate a new buffer to avoid modifying the src buffer */
struct gc_arena gc = gc_new();
struct buffer input = alloc_buf_gc(BLEN(src) + 1, &gc);
buf_copy(&input, src);
buf_null_terminate(&input);
size_t use_len = 0;
mbedtls_pem_context ctx = { 0 };
bool ret = mbed_ok(mbedtls_pem_read_buffer(&ctx, header, footer, BPTR(&input),
NULL, 0, &use_len));
if (ret && !buf_write(dst, ctx.buf, ctx.buflen))
{
ret = false;
msg(M_WARN, "PEM decode error: destination buffer too small");
}
mbedtls_pem_free(&ctx);
gc_free(&gc);
return ret;
}
/* /*
* *
* Random number functions, used in cases where we want * Random number functions, used in cases where we want
...@@ -310,104 +389,33 @@ rand_bytes(uint8_t *output, int len) ...@@ -310,104 +389,33 @@ rand_bytes(uint8_t *output, int len)
/* /*
* *
* Key functions, allow manipulation of keys. * Generic cipher key type functions
* *
*/ */
static const mbedtls_cipher_info_t *
cipher_get(const char *ciphername)
int
key_des_num_cblocks(const mbedtls_cipher_info_t *kt)
{
int ret = 0;
if (kt->type == MBEDTLS_CIPHER_DES_CBC)
{
ret = 1;
}
if (kt->type == MBEDTLS_CIPHER_DES_EDE_CBC)
{
ret = 2;
}
if (kt->type == MBEDTLS_CIPHER_DES_EDE3_CBC)
{
ret = 3;
}
dmsg(D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret);
return ret;
}
bool
key_des_check(uint8_t *key, int key_len, int ndc)
{ {
int i; ASSERT(ciphername);
struct buffer b;
buf_set_read(&b, key, key_len);
for (i = 0; i < ndc; ++i)
{
unsigned char *key = buf_read_alloc(&b, MBEDTLS_DES_KEY_SIZE);
if (!key)
{
msg(D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: insufficient key material");
goto err;
}
if (0 != mbedtls_des_key_check_weak(key))
{
msg(D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: weak key detected");
goto err;
}
if (0 != mbedtls_des_key_check_key_parity(key))
{
msg(D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: bad parity detected");
goto err;
}
}
return true;
err:
return false;
}
void const mbedtls_cipher_info_t *cipher = NULL;
key_des_fixup(uint8_t *key, int key_len, int ndc)
{
int i;
struct buffer b;
buf_set_read(&b, key, key_len); ciphername = translate_cipher_name_from_openvpn(ciphername);
for (i = 0; i < ndc; ++i) cipher = mbedtls_cipher_info_from_string(ciphername);
{ return cipher;
unsigned char *key = buf_read_alloc(&b, MBEDTLS_DES_KEY_SIZE);
if (!key)
{
msg(D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material");
return;
}
mbedtls_des_key_set_parity(key);
}
} }
/* bool
* cipher_valid_reason(const char *ciphername, const char **reason)
* Generic cipher key type functions
*
*/
const mbedtls_cipher_info_t *
cipher_kt_get(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher = NULL; ASSERT(reason);
ASSERT(ciphername);
cipher = mbedtls_cipher_info_from_string(ciphername); const mbedtls_cipher_info_t *cipher = cipher_get(ciphername);
if (NULL == cipher) if (NULL == cipher)
{ {
msg(D_LOW, "Cipher algorithm '%s' not found", ciphername); msg(D_LOW, "Cipher algorithm '%s' not found", ciphername);
return NULL; *reason = "disabled because unknown";
return false;
} }
if (cipher->key_bitlen/8 > MAX_CIPHER_KEY_LENGTH) if (cipher->key_bitlen/8 > MAX_CIPHER_KEY_LENGTH)
...@@ -415,15 +423,18 @@ cipher_kt_get(const char *ciphername) ...@@ -415,15 +423,18 @@ cipher_kt_get(const char *ciphername)
msg(D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) " msg(D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) "
"which is larger than " PACKAGE_NAME "'s current maximum key size " "which is larger than " PACKAGE_NAME "'s current maximum key size "
"(%d bytes)", ciphername, cipher->key_bitlen/8, MAX_CIPHER_KEY_LENGTH); "(%d bytes)", ciphername, cipher->key_bitlen/8, MAX_CIPHER_KEY_LENGTH);
return NULL; *reason = "disabled due to key size too large";
return false;
} }
return cipher; *reason = NULL;
return true;
} }
const char * const char *
cipher_kt_name(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_name(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername);
if (NULL == cipher_kt) if (NULL == cipher_kt)
{ {
return "[null-cipher]"; return "[null-cipher]";
...@@ -433,8 +444,10 @@ cipher_kt_name(const mbedtls_cipher_info_t *cipher_kt) ...@@ -433,8 +444,10 @@ cipher_kt_name(const mbedtls_cipher_info_t *cipher_kt)
} }
int int
cipher_kt_key_size(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_key_size(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername);
if (NULL == cipher_kt) if (NULL == cipher_kt)
{ {
return 0; return 0;
...@@ -444,8 +457,10 @@ cipher_kt_key_size(const mbedtls_cipher_info_t *cipher_kt) ...@@ -444,8 +457,10 @@ cipher_kt_key_size(const mbedtls_cipher_info_t *cipher_kt)
} }
int int
cipher_kt_iv_size(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_iv_size(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername);
if (NULL == cipher_kt) if (NULL == cipher_kt)
{ {
return 0; return 0;
...@@ -454,8 +469,9 @@ cipher_kt_iv_size(const mbedtls_cipher_info_t *cipher_kt) ...@@ -454,8 +469,9 @@ cipher_kt_iv_size(const mbedtls_cipher_info_t *cipher_kt)
} }
int int
cipher_kt_block_size(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_block_size(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername);
if (NULL == cipher_kt) if (NULL == cipher_kt)
{ {
return 0; return 0;
...@@ -464,18 +480,32 @@ cipher_kt_block_size(const mbedtls_cipher_info_t *cipher_kt) ...@@ -464,18 +480,32 @@ cipher_kt_block_size(const mbedtls_cipher_info_t *cipher_kt)
} }
int int
cipher_kt_tag_size(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_tag_size(const char *ciphername)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES if (cipher_kt_mode_aead(ciphername))
if (cipher_kt && cipher_kt_mode_aead(cipher_kt))
{ {
return OPENVPN_AEAD_TAG_LENGTH; return OPENVPN_AEAD_TAG_LENGTH;
} }
#endif
return 0; return 0;
} }
int bool
cipher_kt_insecure(const char *ciphername)
{
const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername);
if (!cipher_kt)
{
return true;
}
return !(cipher_kt_block_size(ciphername) >= 128 / 8
#ifdef MBEDTLS_CHACHAPOLY_C
|| cipher_kt->type == MBEDTLS_CIPHER_CHACHA20_POLY1305
#endif
);
}
static int
cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt) cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt)
{ {
ASSERT(NULL != cipher_kt); ASSERT(NULL != cipher_kt);
...@@ -483,22 +513,29 @@ cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt) ...@@ -483,22 +513,29 @@ cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt)
} }
bool bool
cipher_kt_mode_cbc(const cipher_kt_t *cipher) cipher_kt_mode_cbc(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher = cipher_get(ciphername);
return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC;
} }
bool bool
cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) cipher_kt_mode_ofb_cfb(const char *ciphername)
{ {
const mbedtls_cipher_info_t *cipher = cipher_get(ciphername);
return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB
|| cipher_kt_mode(cipher) == OPENVPN_MODE_CFB); || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB);
} }
bool bool
cipher_kt_mode_aead(const cipher_kt_t *cipher) cipher_kt_mode_aead(const char *ciphername)
{ {
return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_GCM; const mbedtls_cipher_info_t *cipher = cipher_get(ciphername);
return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM
#ifdef MBEDTLS_CHACHAPOLY_C
|| cipher_kt_mode(cipher) == MBEDTLS_MODE_CHACHAPOLY
#endif
);
} }
...@@ -524,13 +561,17 @@ cipher_ctx_free(mbedtls_cipher_context_t *ctx) ...@@ -524,13 +561,17 @@ cipher_ctx_free(mbedtls_cipher_context_t *ctx)
} }
void void
cipher_ctx_init(mbedtls_cipher_context_t *ctx, const uint8_t *key, int key_len, cipher_ctx_init(mbedtls_cipher_context_t *ctx, const uint8_t *key,
const mbedtls_cipher_info_t *kt, const mbedtls_operation_t operation) const char *ciphername, const mbedtls_operation_t operation)
{ {
ASSERT(NULL != kt && NULL != ctx); ASSERT(NULL != ciphername && NULL != ctx);
CLEAR(*ctx); CLEAR(*ctx);
const mbedtls_cipher_info_t *kt = cipher_get(ciphername);
int key_len = kt->key_bitlen/8;
ASSERT(kt);
if (!mbed_ok(mbedtls_cipher_setup(ctx, kt))) if (!mbed_ok(mbedtls_cipher_setup(ctx, kt)))
{ {
msg(M_FATAL, "mbed TLS cipher context init #1"); msg(M_FATAL, "mbed TLS cipher context init #1");
...@@ -554,7 +595,6 @@ cipher_ctx_iv_length(const mbedtls_cipher_context_t *ctx) ...@@ -554,7 +595,6 @@ cipher_ctx_iv_length(const mbedtls_cipher_context_t *ctx)
int int
cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len) cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES
if (tag_len > SIZE_MAX) if (tag_len > SIZE_MAX)
{ {
return 0; return 0;
...@@ -566,9 +606,6 @@ cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len) ...@@ -566,9 +606,6 @@ cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len)
} }
return 1; return 1;
#else /* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT(0);
#endif /* HAVE_AEAD_CIPHER_MODES */
} }
int int
...@@ -585,14 +622,32 @@ cipher_ctx_mode(const mbedtls_cipher_context_t *ctx) ...@@ -585,14 +622,32 @@ cipher_ctx_mode(const mbedtls_cipher_context_t *ctx)
return cipher_kt_mode(ctx->cipher_info); return cipher_kt_mode(ctx->cipher_info);
} }
const cipher_kt_t * bool
cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx) cipher_ctx_mode_cbc(const cipher_ctx_t *ctx)
{
return ctx && cipher_ctx_mode(ctx) == OPENVPN_MODE_CBC;
}
bool
cipher_ctx_mode_ofb_cfb(const cipher_ctx_t *ctx)
{ {
return ctx ? ctx->cipher_info : NULL; return ctx && (cipher_ctx_mode(ctx) == OPENVPN_MODE_OFB
|| cipher_ctx_mode(ctx) == OPENVPN_MODE_CFB);
}
bool
cipher_ctx_mode_aead(const cipher_ctx_t *ctx)
{
return ctx && (cipher_ctx_mode(ctx) == OPENVPN_MODE_GCM
#ifdef MBEDTLS_CHACHAPOLY_C
|| cipher_ctx_mode(ctx) == MBEDTLS_MODE_CHACHAPOLY
#endif
);
} }
int int
cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf) cipher_ctx_reset(mbedtls_cipher_context_t *ctx, const uint8_t *iv_buf)
{ {
if (!mbed_ok(mbedtls_cipher_reset(ctx))) if (!mbed_ok(mbedtls_cipher_reset(ctx)))
{ {
...@@ -610,7 +665,6 @@ cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf) ...@@ -610,7 +665,6 @@ cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf)
int int
cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len) cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES
if (src_len > SIZE_MAX) if (src_len > SIZE_MAX)
{ {
return 0; return 0;
...@@ -622,9 +676,6 @@ cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len) ...@@ -622,9 +676,6 @@ cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len)
} }
return 1; return 1;
#else /* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT(0);
#endif /* HAVE_AEAD_CIPHER_MODES */
} }
int int
...@@ -663,7 +714,6 @@ int ...@@ -663,7 +714,6 @@ int
cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst, cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst,
int *dst_len, uint8_t *tag, size_t tag_len) int *dst_len, uint8_t *tag, size_t tag_len)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES
size_t olen = 0; size_t olen = 0;
if (MBEDTLS_DECRYPT != ctx->operation) if (MBEDTLS_DECRYPT != ctx->operation)
...@@ -695,15 +745,12 @@ cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst, ...@@ -695,15 +745,12 @@ cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst,
} }
return 1; return 1;
#else /* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT(0);
#endif /* HAVE_AEAD_CIPHER_MODES */
} }
void void
cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
unsigned char *src, unsigned char src[DES_KEY_LENGTH],
unsigned char *dst) unsigned char dst[DES_KEY_LENGTH])
{ {
mbedtls_des_context ctx; mbedtls_des_context ctx;
...@@ -720,8 +767,8 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], ...@@ -720,8 +767,8 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
*/ */
const mbedtls_md_info_t * static const mbedtls_md_info_t *
md_kt_get(const char *digest) md_get(const char *digest)
{ {
const mbedtls_md_info_t *md = NULL; const mbedtls_md_info_t *md = NULL;
ASSERT(digest); ASSERT(digest);
...@@ -741,19 +788,28 @@ md_kt_get(const char *digest) ...@@ -741,19 +788,28 @@ md_kt_get(const char *digest)
return md; return md;
} }
bool
md_valid(const char *digest)
{
const mbedtls_md_info_t *md = mbedtls_md_info_from_string(digest);
return md != NULL;
}
const char * const char *
md_kt_name(const mbedtls_md_info_t *kt) md_kt_name(const char *mdname)
{ {
if (NULL == kt) if (!strcmp("none", mdname))
{ {
return "[null-digest]"; return "[null-digest]";
} }
const mbedtls_md_info_t *kt = md_get(mdname);
return mbedtls_md_get_name(kt); return mbedtls_md_get_name(kt);
} }
int unsigned char
md_kt_size(const mbedtls_md_info_t *kt) md_kt_size(const char *mdname)
{ {
const mbedtls_md_info_t *kt = md_get(mdname);
if (NULL == kt) if (NULL == kt)
{ {
return 0; return 0;
...@@ -768,8 +824,9 @@ md_kt_size(const mbedtls_md_info_t *kt) ...@@ -768,8 +824,9 @@ md_kt_size(const mbedtls_md_info_t *kt)
*/ */
int int
md_full(const md_kt_t *kt, const uint8_t *src, int src_len, uint8_t *dst) md_full(const char *mdname, const uint8_t *src, int src_len, uint8_t *dst)
{ {
const mbedtls_md_info_t *kt = md_get(mdname);
return 0 == mbedtls_md(kt, src, src_len, dst); return 0 == mbedtls_md(kt, src, src_len, dst);
} }
...@@ -781,14 +838,16 @@ md_ctx_new(void) ...@@ -781,14 +838,16 @@ md_ctx_new(void)
return ctx; return ctx;
} }
void md_ctx_free(mbedtls_md_context_t *ctx) void
md_ctx_free(mbedtls_md_context_t *ctx)
{ {
free(ctx); free(ctx);
} }
void void
md_ctx_init(mbedtls_md_context_t *ctx, const mbedtls_md_info_t *kt) md_ctx_init(mbedtls_md_context_t *ctx, const char *mdname)
{ {
const mbedtls_md_info_t *kt = md_get(mdname);
ASSERT(NULL != ctx && NULL != kt); ASSERT(NULL != ctx && NULL != kt);
mbedtls_md_init(ctx); mbedtls_md_init(ctx);
...@@ -852,12 +911,13 @@ hmac_ctx_free(mbedtls_md_context_t *ctx) ...@@ -852,12 +911,13 @@ hmac_ctx_free(mbedtls_md_context_t *ctx)
} }
void void
hmac_ctx_init(mbedtls_md_context_t *ctx, const uint8_t *key, int key_len, hmac_ctx_init(mbedtls_md_context_t *ctx, const uint8_t *key, const char *mdname)
const mbedtls_md_info_t *kt)
{ {
const mbedtls_md_info_t *kt = md_get(mdname);
ASSERT(NULL != kt && NULL != ctx); ASSERT(NULL != kt && NULL != ctx);
mbedtls_md_init(ctx); mbedtls_md_init(ctx);
int key_len = mbedtls_md_get_size(kt);
ASSERT(0 == mbedtls_md_setup(ctx, kt, 1)); ASSERT(0 == mbedtls_md_setup(ctx, kt, 1));
ASSERT(0 == mbedtls_md_hmac_starts(ctx, key, key_len)); ASSERT(0 == mbedtls_md_hmac_starts(ctx, key, key_len));
...@@ -872,7 +932,7 @@ hmac_ctx_cleanup(mbedtls_md_context_t *ctx) ...@@ -872,7 +932,7 @@ hmac_ctx_cleanup(mbedtls_md_context_t *ctx)
} }
int int
hmac_ctx_size(const mbedtls_md_context_t *ctx) hmac_ctx_size(mbedtls_md_context_t *ctx)
{ {
if (NULL == ctx) if (NULL == ctx)
{ {
...@@ -899,4 +959,163 @@ hmac_ctx_final(mbedtls_md_context_t *ctx, uint8_t *dst) ...@@ -899,4 +959,163 @@ hmac_ctx_final(mbedtls_md_context_t *ctx, uint8_t *dst)
ASSERT(0 == mbedtls_md_hmac_finish(ctx, dst)); ASSERT(0 == mbedtls_md_hmac_finish(ctx, dst));
} }
#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_MBEDTLS */ int
memcmp_constant_time(const void *a, const void *b, size_t size)
{
/* mbed TLS has a no const time memcmp function that it exposes
* via its APIs like OpenSSL does with CRYPTO_memcmp
* Adapt the function that mbedtls itself uses in
* mbedtls_safer_memcmp as it considers that to be safe */
volatile const unsigned char *A = (volatile const unsigned char *) a;
volatile const unsigned char *B = (volatile const unsigned char *) b;
volatile unsigned char diff = 0;
for (size_t i = 0; i < size; i++)
{
unsigned char x = A[i], y = B[i];
diff |= x ^ y;
}
return diff;
}
/* mbedtls-2.18.0 or newer */
#ifdef HAVE_MBEDTLS_SSL_TLS_PRF
bool
ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
int secret_len, uint8_t *output, int output_len)
{
return mbed_ok(mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1, secret,
secret_len, "", seed, seed_len, output,
output_len));
}
#else /* ifdef HAVE_MBEDTLS_SSL_TLS_PRF */
/*
* Generate the hash required by for the \c tls1_PRF function.
*
* @param md_kt Message digest to use
* @param sec Secret to base the hash on
* @param sec_len Length of the secret
* @param seed Seed to hash
* @param seed_len Length of the seed
* @param out Output buffer
* @param olen Length of the output buffer
*/
static void
tls1_P_hash(const mbedtls_md_info_t *md_kt, const uint8_t *sec, int sec_len,
const uint8_t *seed, int seed_len, uint8_t *out, int olen)
{
struct gc_arena gc = gc_new();
uint8_t A1[MAX_HMAC_KEY_LENGTH];
#ifdef ENABLE_DEBUG
/* used by the D_SHOW_KEY_SOURCE, guarded with ENABLE_DEBUG to avoid unused
* variables warnings if compiled with --enable-small */
const int olen_orig = olen;
const uint8_t *out_orig = out;
#endif
hmac_ctx_t *ctx = hmac_ctx_new();
hmac_ctx_t *ctx_tmp = hmac_ctx_new();
dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc));
dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc));
int chunk = mbedtls_md_get_size(md_kt);
unsigned int A1_len = mbedtls_md_get_size(md_kt);
/* This is the only place where we init an HMAC with a key that is not
* equal to its size, therefore we init the hmac ctx manually here */
mbedtls_md_init(ctx);
ASSERT(0 == mbedtls_md_setup(ctx, md_kt, 1));
ASSERT(0 == mbedtls_md_hmac_starts(ctx, sec, sec_len));
mbedtls_md_init(ctx_tmp);
ASSERT(0 == mbedtls_md_setup(ctx_tmp, md_kt, 1));
ASSERT(0 == mbedtls_md_hmac_starts(ctx_tmp, sec, sec_len));
hmac_ctx_update(ctx,seed,seed_len);
hmac_ctx_final(ctx, A1);
for (;; )
{
hmac_ctx_reset(ctx);
hmac_ctx_reset(ctx_tmp);
hmac_ctx_update(ctx, A1, A1_len);
hmac_ctx_update(ctx_tmp, A1, A1_len);
hmac_ctx_update(ctx, seed, seed_len);
if (olen > chunk)
{
hmac_ctx_final(ctx, out);
out += chunk;
olen -= chunk;
hmac_ctx_final(ctx_tmp, A1); /* calc the next A1 value */
}
else /* last one */
{
hmac_ctx_final(ctx, A1);
memcpy(out, A1, olen);
break;
}
}
hmac_ctx_cleanup(ctx);
hmac_ctx_free(ctx);
hmac_ctx_cleanup(ctx_tmp);
hmac_ctx_free(ctx_tmp);
secure_memzero(A1, sizeof(A1));
dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex(out_orig, olen_orig, 0, &gc));
gc_free(&gc);
}
/*
* Use the TLS PRF function for generating data channel keys.
* This code is based on the OpenSSL library.
*
* TLS generates keys as such:
*
* master_secret[48] = PRF(pre_master_secret[48], "master secret",
* ClientHello.random[32] + ServerHello.random[32])
*
* key_block[] = PRF(SecurityParameters.master_secret[48],
* "key expansion",
* SecurityParameters.server_random[32] +
* SecurityParameters.client_random[32]);
*
* Notes:
*
* (1) key_block contains a full set of 4 keys.
* (2) The pre-master secret is generated by the client.
*/
bool
ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec,
int slen, uint8_t *out1, int olen)
{
struct gc_arena gc = gc_new();
const md_kt_t *md5 = md_get("MD5");
const md_kt_t *sha1 = md_get("SHA1");
uint8_t *out2 = (uint8_t *)gc_malloc(olen, false, &gc);
int len = slen/2;
const uint8_t *S1 = sec;
const uint8_t *S2 = &(sec[len]);
len += (slen&1); /* add for odd, make longer */
tls1_P_hash(md5, S1, len, label, label_len, out1, olen);
tls1_P_hash(sha1, S2, len, label, label_len, out2, olen);
for (int i = 0; i<olen; i++)
{
out1[i] ^= out2[i];
}
secure_memzero(out2, olen);
dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc));
gc_free(&gc);
return true;
}
#endif /* ifdef HAVE_MBEDTLS_SSL_TLS_PRF */
#endif /* ENABLE_CRYPTO_MBEDTLS */
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and * packet encryption, packet authentication, and
* packet compression. * packet compression.
* *
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
...@@ -33,9 +33,6 @@ ...@@ -33,9 +33,6 @@
#include <mbedtls/md.h> #include <mbedtls/md.h>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
/** Generic cipher key type %context. */
typedef mbedtls_cipher_info_t cipher_kt_t;
/** Generic message digest key type %context. */ /** Generic message digest key type %context. */
typedef mbedtls_md_info_t md_kt_t; typedef mbedtls_md_info_t md_kt_t;
...@@ -48,6 +45,9 @@ typedef mbedtls_md_context_t md_ctx_t; ...@@ -48,6 +45,9 @@ typedef mbedtls_md_context_t md_ctx_t;
/** Generic HMAC %context. */ /** Generic HMAC %context. */
typedef mbedtls_md_context_t hmac_ctx_t; typedef mbedtls_md_context_t hmac_ctx_t;
/* Use a dummy type for the provider */
typedef void provider_t;
/** Maximum length of an IV */ /** Maximum length of an IV */
#define OPENVPN_MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH #define OPENVPN_MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH
...@@ -146,5 +146,4 @@ mbed_log_func_line_lite(unsigned int flags, int errval, ...@@ -146,5 +146,4 @@ mbed_log_func_line_lite(unsigned int flags, int errval,
#define mbed_ok(errval) \ #define mbed_ok(errval) \
mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__) mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__)
#endif /* CRYPTO_MBEDTLS_H_ */ #endif /* CRYPTO_MBEDTLS_H_ */
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and * packet encryption, packet authentication, and
* packet compression. * packet compression.
* *
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include "syshead.h" #include "syshead.h"
#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) #if defined(ENABLE_CRYPTO_OPENSSL)
#include "basic.h" #include "basic.h"
#include "buffer.h" #include "buffer.h"
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "crypto_backend.h" #include "crypto_backend.h"
#include "openssl_compat.h" #include "openssl_compat.h"
#include <openssl/conf.h>
#include <openssl/des.h> #include <openssl/des.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/evp.h> #include <openssl/evp.h>
...@@ -50,6 +51,22 @@ ...@@ -50,6 +51,22 @@
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
#include <openssl/kdf.h>
#endif
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/provider.h>
#endif
#if defined(_WIN32) && defined(OPENSSL_NO_EC)
#error Windows build with OPENSSL_NO_EC: disabling EC key is not supported.
#endif
#ifdef _MSC_VER
/* mute ossl3 deprecation warnings treated as errors in msvc */
#pragma warning(disable: 4996)
#endif
/* /*
* Check for key size creepage. * Check for key size creepage.
*/ */
...@@ -63,6 +80,7 @@ ...@@ -63,6 +80,7 @@
#endif #endif
#if HAVE_OPENSSL_ENGINE #if HAVE_OPENSSL_ENGINE
#include <openssl/ui.h>
#include <openssl/engine.h> #include <openssl/engine.h>
static bool engine_initialized = false; /* GLOBAL */ static bool engine_initialized = false; /* GLOBAL */
...@@ -139,6 +157,34 @@ crypto_init_lib_engine(const char *engine_name) ...@@ -139,6 +157,34 @@ crypto_init_lib_engine(const char *engine_name)
#endif #endif
} }
provider_t *
crypto_load_provider(const char *provider)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* Load providers into the default (NULL) library context */
OSSL_PROVIDER *prov = OSSL_PROVIDER_load(NULL, provider);
if (!prov)
{
crypto_msg(M_FATAL, "failed to load provider '%s'", provider);
}
return prov;
#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
msg(M_WARN, "Note: OpenSSL provider functionality is not available");
return NULL;
#endif
}
void
crypto_unload_provider(const char *provname, provider_t *provider)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!OSSL_PROVIDER_unload(provider))
{
crypto_msg(M_FATAL, "failed to unload provider '%s'", provname);
}
#endif
}
/* /*
* *
* Functions related to the core crypto library * Functions related to the core crypto library
...@@ -148,6 +194,11 @@ crypto_init_lib_engine(const char *engine_name) ...@@ -148,6 +194,11 @@ crypto_init_lib_engine(const char *engine_name)
void void
crypto_init_lib(void) crypto_init_lib(void)
{ {
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
#else
OPENSSL_config(NULL);
#endif
/* /*
* If you build the OpenSSL library and OpenVPN with * If you build the OpenSSL library and OpenVPN with
* CRYPTO_MDEBUG, you will get a listing of OpenSSL * CRYPTO_MDEBUG, you will get a listing of OpenSSL
...@@ -188,7 +239,7 @@ crypto_clear_error(void) ...@@ -188,7 +239,7 @@ crypto_clear_error(void)
void void
crypto_print_openssl_errors(const unsigned int flags) crypto_print_openssl_errors(const unsigned int flags)
{ {
size_t err = 0; unsigned long err = 0;
while ((err = ERR_get_error())) while ((err = ERR_get_error()))
{ {
...@@ -202,12 +253,12 @@ crypto_print_openssl_errors(const unsigned int flags) ...@@ -202,12 +253,12 @@ crypto_print_openssl_errors(const unsigned int flags)
else if (ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL) else if (ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL)
{ {
msg(D_CRYPT_ERRORS, "TLS error: Unsupported protocol. This typically " msg(D_CRYPT_ERRORS, "TLS error: Unsupported protocol. This typically "
"indicates that client and server have no common TLS version enabled. " "indicates that client and server have no common TLS version enabled. "
"This can be caused by mismatched tls-version-min and tls-version-max " "This can be caused by mismatched tls-version-min and tls-version-max "
"options on client and server. " "options on client and server. "
"If your OpenVPN client is between v2.3.6 and v2.3.2 try adding " "If your OpenVPN client is between v2.3.6 and v2.3.2 try adding "
"tls-version-min 1.0 to the client configuration to use TLS 1.0+ " "tls-version-min 1.0 to the client configuration to use TLS 1.0+ "
"instead of TLS 1.0 only"); "instead of TLS 1.0 only");
} }
msg(flags, "OpenSSL: %s", ERR_error_string(err, NULL)); msg(flags, "OpenSSL: %s", ERR_error_string(err, NULL));
} }
...@@ -254,6 +305,7 @@ const cipher_name_pair cipher_name_translation_table[] = { ...@@ -254,6 +305,7 @@ const cipher_name_pair cipher_name_translation_table[] = {
{ "AES-128-GCM", "id-aes128-GCM" }, { "AES-128-GCM", "id-aes128-GCM" },
{ "AES-192-GCM", "id-aes192-GCM" }, { "AES-192-GCM", "id-aes192-GCM" },
{ "AES-256-GCM", "id-aes256-GCM" }, { "AES-256-GCM", "id-aes256-GCM" },
{ "CHACHA20-POLY1305", "ChaCha20-Poly1305" },
}; };
const size_t cipher_name_translation_table_count = const size_t cipher_name_translation_table_count =
sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table); sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table);
...@@ -265,110 +317,123 @@ cipher_name_cmp(const void *a, const void *b) ...@@ -265,110 +317,123 @@ cipher_name_cmp(const void *a, const void *b)
const EVP_CIPHER *const *cipher_a = a; const EVP_CIPHER *const *cipher_a = a;
const EVP_CIPHER *const *cipher_b = b; const EVP_CIPHER *const *cipher_b = b;
const char *cipher_name_a = return strcmp(EVP_CIPHER_get0_name(*cipher_a), EVP_CIPHER_get0_name(*cipher_b));
translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_a));
const char *cipher_name_b =
translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_b));
return strcmp(cipher_name_a, cipher_name_b);
} }
struct collect_ciphers {
/* If we ever exceed this, we must be more selective */
const EVP_CIPHER *list[1000];
size_t num;
};
static void static void
print_cipher(const EVP_CIPHER *cipher) collect_ciphers(EVP_CIPHER *cipher, void *list)
{ {
const char *var_key_size = if (!cipher)
(EVP_CIPHER_flags(cipher) & EVP_CIPH_VARIABLE_LENGTH) ? {
" by default" : ""; return;
const char *ssl_only = cipher_kt_mode_cbc(cipher) ? }
"" : ", TLS client/server mode only"; struct collect_ciphers *cipher_list = list;
if (cipher_list->num == SIZE(cipher_list->list))
{
msg(M_WARN, "WARNING: Too many ciphers, not showing all");
return;
}
const char *ciphername = EVP_CIPHER_get0_name(cipher);
printf("%s (%d bit key%s, %d bit block%s)\n", if (ciphername && (cipher_kt_mode_cbc(ciphername)
translate_cipher_name_to_openvpn(EVP_CIPHER_name(cipher)), #ifdef ENABLE_OFB_CFB_MODE
EVP_CIPHER_key_length(cipher) * 8, var_key_size, || cipher_kt_mode_ofb_cfb(ciphername)
cipher_kt_block_size(cipher) * 8, ssl_only); #endif
|| cipher_kt_mode_aead(ciphername)
))
{
cipher_list->list[cipher_list->num++] = cipher;
}
} }
void void
show_available_ciphers(void) show_available_ciphers(void)
{ {
int nid; struct collect_ciphers cipher_list = { 0 };
size_t i;
/* If we ever exceed this, we must be more selective */
const EVP_CIPHER *cipher_list[1000];
size_t num_ciphers = 0;
#ifndef ENABLE_SMALL #ifndef ENABLE_SMALL
printf("The following ciphers and cipher modes are available for use\n" printf("The following ciphers and cipher modes are available for use\n"
"with " PACKAGE_NAME ". Each cipher shown below may be use as a\n" "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n"
"parameter to the --cipher option. The default key size is\n" "parameter to the --data-ciphers (or --cipher) option. In static \n"
"shown as well as whether or not it can be changed with the\n" "key mode only CBC mode is allowed.\n");
"--keysize directive. Using a CBC or GCM mode is recommended.\n" printf("See also openssl list -cipher-algorithms\n\n");
"In static key mode only CBC mode is allowed.\n\n");
#endif #endif
for (nid = 0; nid < 10000; ++nid) #if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_CIPHER_do_all_provided(NULL, collect_ciphers, &cipher_list);
#else
for (int nid = 0; nid < 10000; ++nid)
{ {
const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid); const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid);
if (cipher && (cipher_kt_mode_cbc(cipher) /* We cast the const away so we can keep the function prototype
#ifdef ENABLE_OFB_CFB_MODE * compatible with EVP_CIPHER_do_all_provided */
|| cipher_kt_mode_ofb_cfb(cipher) collect_ciphers((EVP_CIPHER *) cipher, &cipher_list);
#endif
#ifdef HAVE_AEAD_CIPHER_MODES
|| cipher_kt_mode_aead(cipher)
#endif
))
{
cipher_list[num_ciphers++] = cipher;
}
if (num_ciphers == (sizeof(cipher_list)/sizeof(*cipher_list)))
{
msg(M_WARN, "WARNING: Too many ciphers, not showing all");
break;
}
} }
#endif
qsort(cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp); /* cast to non-const to prevent warning */
qsort((EVP_CIPHER *)cipher_list.list, cipher_list.num, sizeof(*cipher_list.list), cipher_name_cmp);
for (i = 0; i < num_ciphers; i++) { for (size_t i = 0; i < cipher_list.num; i++)
if (cipher_kt_block_size(cipher_list[i]) >= 128/8) {
if (!cipher_kt_insecure(EVP_CIPHER_get0_name(cipher_list.list[i])))
{ {
print_cipher(cipher_list[i]); print_cipher(EVP_CIPHER_get0_name(cipher_list.list[i]));
} }
} }
printf("\nThe following ciphers have a block size of less than 128 bits, \n" printf("\nThe following ciphers have a block size of less than 128 bits, \n"
"and are therefore deprecated. Do not use unless you have to.\n\n"); "and are therefore deprecated. Do not use unless you have to.\n\n");
for (i = 0; i < num_ciphers; i++) { for (int i = 0; i < cipher_list.num; i++)
if (cipher_kt_block_size(cipher_list[i]) < 128/8) {
if (cipher_kt_insecure(EVP_CIPHER_get0_name(cipher_list.list[i])))
{ {
print_cipher(cipher_list[i]); print_cipher(EVP_CIPHER_get0_name(cipher_list.list[i]));
} }
} }
printf("\n"); printf("\n");
} }
void void
show_available_digests(void) print_digest(EVP_MD *digest, void *unused)
{ {
int nid; printf("%s %d bit digest size\n", EVP_MD_get0_name(digest),
EVP_MD_size(digest) * 8);
}
void
show_available_digests(void)
{
#ifndef ENABLE_SMALL #ifndef ENABLE_SMALL
printf("The following message digests are available for use with\n" printf("The following message digests are available for use with\n"
PACKAGE_NAME ". A message digest is used in conjunction with\n" PACKAGE_NAME ". A message digest is used in conjunction with\n"
"the HMAC function, to authenticate received packets.\n" "the HMAC function, to authenticate received packets.\n"
"You can specify a message digest as parameter to\n" "You can specify a message digest as parameter to\n"
"the --auth option.\n\n"); "the --auth option.\n");
printf("See also openssl list -digest-algorithms\n\n");
#endif #endif
for (nid = 0; nid < 10000; ++nid) #if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD_do_all_provided(NULL, print_digest, NULL);
#else
for (int nid = 0; nid < 10000; ++nid)
{ {
const EVP_MD *digest = EVP_get_digestbynid(nid); const EVP_MD *digest = EVP_get_digestbynid(nid);
if (digest) if (digest)
{ {
printf("%s %d bit digest size\n", /* We cast the const away so we can keep the function prototype
OBJ_nid2sn(nid), EVP_MD_size(digest) * 8); * compatible with EVP_MD_do_all_provided */
print_digest((EVP_MD *)digest, NULL);
} }
} }
#endif
printf("\n"); printf("\n");
} }
...@@ -396,110 +461,107 @@ show_available_engines(void) ...@@ -396,110 +461,107 @@ show_available_engines(void)
#endif #endif
} }
/*
*
* Random number functions, used in cases where we want
* reasonably strong cryptographic random number generation
* without depleting our entropy pool. Used for random
* IV values and a number of other miscellaneous tasks.
*
*/
int bool
rand_bytes(uint8_t *output, int len) crypto_pem_encode(const char *name, struct buffer *dst,
const struct buffer *src, struct gc_arena *gc)
{ {
if (unlikely(1 != RAND_bytes(output, len))) bool ret = false;
BIO *bio = BIO_new(BIO_s_mem());
if (!bio || !PEM_write_bio(bio, name, "", BPTR(src), BLEN(src)))
{ {
crypto_msg(D_CRYPT_ERRORS, "RAND_bytes() failed"); ret = false;
return 0; goto cleanup;
} }
return 1;
}
/* BUF_MEM *bptr;
* BIO_get_mem_ptr(bio, &bptr);
* Key functions, allow manipulation of keys.
*
*/
*dst = alloc_buf_gc(bptr->length, gc);
ASSERT(buf_write(dst, bptr->data, bptr->length));
int ret = true;
key_des_num_cblocks(const EVP_CIPHER *kt) cleanup:
{ if (!BIO_free(bio))
int ret = 0;
const char *name = OBJ_nid2sn(EVP_CIPHER_nid(kt));
if (name)
{ {
if (!strncmp(name, "DES-", 4)) ret = false;
{
ret = EVP_CIPHER_key_length(kt) / sizeof(DES_cblock);
}
else if (!strncmp(name, "DESX-", 5))
{
ret = 1;
}
} }
dmsg(D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret);
return ret; return ret;
} }
bool bool
key_des_check(uint8_t *key, int key_len, int ndc) crypto_pem_decode(const char *name, struct buffer *dst,
const struct buffer *src)
{ {
int i; bool ret = false;
struct buffer b;
buf_set_read(&b, key, key_len); BIO *bio = BIO_new_mem_buf((char *)BPTR(src), BLEN(src));
if (!bio)
{
crypto_msg(M_FATAL, "Cannot open memory BIO for PEM decode");
}
for (i = 0; i < ndc; ++i) char *name_read = NULL;
char *header_read = NULL;
uint8_t *data_read = NULL;
long data_read_len = 0;
if (!PEM_read_bio(bio, &name_read, &header_read, &data_read,
&data_read_len))
{ {
DES_cblock *dc = (DES_cblock *) buf_read_alloc(&b, sizeof(DES_cblock)); dmsg(D_CRYPT_ERRORS, "%s: PEM decode failed", __func__);
if (!dc) goto cleanup;
{
crypto_msg(D_CRYPT_ERRORS,
"CRYPTO INFO: check_key_DES: insufficient key material");
goto err;
}
if (DES_is_weak_key(dc))
{
crypto_msg(D_CRYPT_ERRORS,
"CRYPTO INFO: check_key_DES: weak key detected");
goto err;
}
if (!DES_check_key_parity(dc))
{
crypto_msg(D_CRYPT_ERRORS,
"CRYPTO INFO: check_key_DES: bad parity detected");
goto err;
}
} }
return true;
err: if (strcmp(name, name_read))
ERR_clear_error(); {
return false; dmsg(D_CRYPT_ERRORS,
} "%s: unexpected PEM name (got '%s', expected '%s')",
__func__, name_read, name);
goto cleanup;
}
void uint8_t *dst_data = buf_write_alloc(dst, data_read_len);
key_des_fixup(uint8_t *key, int key_len, int ndc) if (!dst_data)
{ {
int i; dmsg(D_CRYPT_ERRORS, "%s: dst too small (%i, needs %li)", __func__,
struct buffer b; BCAP(dst), data_read_len);
goto cleanup;
}
memcpy(dst_data, data_read, data_read_len);
buf_set_read(&b, key, key_len); ret = true;
for (i = 0; i < ndc; ++i) cleanup:
OPENSSL_free(name_read);
OPENSSL_free(header_read);
OPENSSL_free(data_read);
if (!BIO_free(bio))
{ {
DES_cblock *dc = (DES_cblock *) buf_read_alloc(&b, sizeof(DES_cblock)); ret = false;
if (!dc)
{
msg(D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material");
ERR_clear_error();
return;
}
DES_set_odd_parity(dc);
} }
return ret;
} }
/*
*
* Random number functions, used in cases where we want
* reasonably strong cryptographic random number generation
* without depleting our entropy pool. Used for random
* IV values and a number of other miscellaneous tasks.
*
*/
int
rand_bytes(uint8_t *output, int len)
{
if (unlikely(1 != RAND_bytes(output, len)))
{
crypto_msg(D_CRYPT_ERRORS, "RAND_bytes() failed");
return 0;
}
return 1;
}
/* /*
* *
...@@ -507,59 +569,96 @@ key_des_fixup(uint8_t *key, int key_len, int ndc) ...@@ -507,59 +569,96 @@ key_des_fixup(uint8_t *key, int key_len, int ndc)
* *
*/ */
static evp_cipher_type *
const EVP_CIPHER * cipher_get(const char *ciphername)
cipher_kt_get(const char *ciphername)
{ {
const EVP_CIPHER *cipher = NULL;
ASSERT(ciphername); ASSERT(ciphername);
cipher = EVP_get_cipherbyname(ciphername); ciphername = translate_cipher_name_from_openvpn(ciphername);
return EVP_CIPHER_fetch(NULL, ciphername, NULL);
}
if (NULL == cipher) bool
cipher_valid_reason(const char *ciphername, const char **reason)
{
bool ret = false;
evp_cipher_type *cipher = cipher_get(ciphername);
if (!cipher)
{ {
crypto_msg(D_LOW, "Cipher algorithm '%s' not found", ciphername); crypto_msg(D_LOW, "Cipher algorithm '%s' not found", ciphername);
return NULL; *reason = "disabled because unknown";
goto out;
} }
#ifdef OPENSSL_FIPS
/* Rhel 8/CentOS 8 have a patched OpenSSL version that return a cipher
* here that is actually not usable if in FIPS mode */
if (FIPS_mode() && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_FIPS))
{
msg(D_LOW, "Cipher algorithm '%s' is known by OpenSSL library but "
"currently disabled by running in FIPS mode.", ciphername);
*reason = "disabled by FIPS mode";
goto out;
}
#endif
if (EVP_CIPHER_key_length(cipher) > MAX_CIPHER_KEY_LENGTH) if (EVP_CIPHER_key_length(cipher) > MAX_CIPHER_KEY_LENGTH)
{ {
msg(D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) " msg(D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) "
"which is larger than " PACKAGE_NAME "'s current maximum key size " "which is larger than " PACKAGE_NAME "'s current maximum key size "
"(%d bytes)", ciphername, EVP_CIPHER_key_length(cipher), "(%d bytes)", ciphername, EVP_CIPHER_key_length(cipher),
MAX_CIPHER_KEY_LENGTH); MAX_CIPHER_KEY_LENGTH);
return NULL; *reason = "disabled due to key size too large";
goto out;
} }
return cipher; ret = true;
*reason = NULL;
out:
EVP_CIPHER_free(cipher);
return ret;
} }
const char * const char *
cipher_kt_name(const EVP_CIPHER *cipher_kt) cipher_kt_name(const char *ciphername)
{ {
if (NULL == cipher_kt) ASSERT(ciphername);
if (strcmp("none", ciphername) == 0)
{ {
return "[null-cipher]"; return "[null-cipher]";
} }
return EVP_CIPHER_name(cipher_kt);
evp_cipher_type *cipher_kt = cipher_get(ciphername);
if (!cipher_kt)
{
return NULL;
}
const char *name = EVP_CIPHER_name(cipher_kt);
EVP_CIPHER_free(cipher_kt);
return translate_cipher_name_to_openvpn(name);
} }
int int
cipher_kt_key_size(const EVP_CIPHER *cipher_kt) cipher_kt_key_size(const char *ciphername)
{ {
return EVP_CIPHER_key_length(cipher_kt); evp_cipher_type *cipher = cipher_get(ciphername);
int size = EVP_CIPHER_key_length(cipher);
EVP_CIPHER_free(cipher);
return size;
} }
int int
cipher_kt_iv_size(const EVP_CIPHER *cipher_kt) cipher_kt_iv_size(const char *ciphername)
{ {
return EVP_CIPHER_iv_length(cipher_kt); evp_cipher_type *cipher = cipher_get(ciphername);
int ivsize = EVP_CIPHER_iv_length(cipher);
EVP_CIPHER_free(cipher);
return ivsize;
} }
int int
cipher_kt_block_size(const EVP_CIPHER *cipher) cipher_kt_block_size(const char *ciphername)
{ {
/* /*
* OpenSSL reports OFB/CFB/GCM cipher block sizes as '1 byte'. To work * OpenSSL reports OFB/CFB/GCM cipher block sizes as '1 byte'. To work
...@@ -570,11 +669,16 @@ cipher_kt_block_size(const EVP_CIPHER *cipher) ...@@ -570,11 +669,16 @@ cipher_kt_block_size(const EVP_CIPHER *cipher)
char *name = NULL; char *name = NULL;
char *mode_str = NULL; char *mode_str = NULL;
const char *orig_name = NULL; const char *orig_name = NULL;
const EVP_CIPHER *cbc_cipher = NULL; evp_cipher_type *cbc_cipher = NULL;
evp_cipher_type *cipher = cipher_get(ciphername);
if (!cipher)
{
return 0;
}
int block_size = EVP_CIPHER_block_size(cipher); int block_size = EVP_CIPHER_block_size(cipher);
orig_name = cipher_kt_name(cipher); orig_name = EVP_CIPHER_name(cipher);
if (!orig_name) if (!orig_name)
{ {
goto cleanup; goto cleanup;
...@@ -589,21 +693,23 @@ cipher_kt_block_size(const EVP_CIPHER *cipher) ...@@ -589,21 +693,23 @@ cipher_kt_block_size(const EVP_CIPHER *cipher)
strcpy(mode_str, "-CBC"); strcpy(mode_str, "-CBC");
cbc_cipher = EVP_get_cipherbyname(translate_cipher_name_from_openvpn(name)); cbc_cipher = EVP_CIPHER_fetch(NULL, translate_cipher_name_from_openvpn(name), NULL);
if (cbc_cipher) if (cbc_cipher)
{ {
block_size = EVP_CIPHER_block_size(cbc_cipher); block_size = EVP_CIPHER_block_size(cbc_cipher);
} }
cleanup: cleanup:
EVP_CIPHER_free(cbc_cipher);
EVP_CIPHER_free(cipher);
free(name); free(name);
return block_size; return block_size;
} }
int int
cipher_kt_tag_size(const EVP_CIPHER *cipher_kt) cipher_kt_tag_size(const char *ciphername)
{ {
if (cipher_kt_mode_aead(cipher_kt)) if (cipher_kt_mode_aead(ciphername))
{ {
return OPENVPN_AEAD_TAG_LENGTH; return OPENVPN_AEAD_TAG_LENGTH;
} }
...@@ -613,6 +719,29 @@ cipher_kt_tag_size(const EVP_CIPHER *cipher_kt) ...@@ -613,6 +719,29 @@ cipher_kt_tag_size(const EVP_CIPHER *cipher_kt)
} }
} }
bool
cipher_kt_insecure(const char *ciphername)
{
if (cipher_kt_block_size(ciphername) >= 128 / 8)
{
return false;
}
#ifdef NID_chacha20_poly1305
evp_cipher_type *cipher = cipher_get(ciphername);
if (cipher)
{
bool ischachapoly = (EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305);
EVP_CIPHER_free(cipher);
if (ischachapoly)
{
return false;
}
}
#endif
return true;
}
int int
cipher_kt_mode(const EVP_CIPHER *cipher_kt) cipher_kt_mode(const EVP_CIPHER *cipher_kt)
{ {
...@@ -621,36 +750,56 @@ cipher_kt_mode(const EVP_CIPHER *cipher_kt) ...@@ -621,36 +750,56 @@ cipher_kt_mode(const EVP_CIPHER *cipher_kt)
} }
bool bool
cipher_kt_mode_cbc(const cipher_kt_t *cipher) cipher_kt_mode_cbc(const char *ciphername)
{ {
return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC evp_cipher_type *cipher = cipher_get(ciphername);
#ifdef EVP_CIPH_FLAG_AEAD_CIPHER
/* Exclude AEAD cipher modes, they require a different API */ bool ret = cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_CBC
&& !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) /* Exclude AEAD cipher modes, they require a different API */
#ifdef EVP_CIPH_FLAG_CTS
&& !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_CTS)
#endif #endif
; && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER));
EVP_CIPHER_free(cipher);
return ret;
} }
bool bool
cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) cipher_kt_mode_ofb_cfb(const char *ciphername)
{ {
return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB evp_cipher_type *cipher = cipher_get(ciphername);
|| cipher_kt_mode(cipher) == OPENVPN_MODE_CFB) bool ofb_cfb = cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB
#ifdef EVP_CIPH_FLAG_AEAD_CIPHER || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB)
/* Exclude AEAD cipher modes, they require a different API */ /* Exclude AEAD cipher modes, they require a different API */
&& !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER);
#endif EVP_CIPHER_free(cipher);
; return ofb_cfb;
} }
bool bool
cipher_kt_mode_aead(const cipher_kt_t *cipher) cipher_kt_mode_aead(const char *ciphername)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES bool isaead = false;
return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM);
#else evp_cipher_type *cipher = cipher_get(ciphername);
return false; if (cipher)
{
if (EVP_CIPHER_mode(cipher) == OPENVPN_MODE_GCM)
{
isaead = true;
}
#ifdef NID_chacha20_poly1305
if (EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305)
{
isaead = true;
}
#endif #endif
}
EVP_CIPHER_free(cipher);
return isaead;
} }
/* /*
...@@ -674,29 +823,25 @@ cipher_ctx_free(EVP_CIPHER_CTX *ctx) ...@@ -674,29 +823,25 @@ cipher_ctx_free(EVP_CIPHER_CTX *ctx)
} }
void void
cipher_ctx_init(EVP_CIPHER_CTX *ctx, const uint8_t *key, int key_len, cipher_ctx_init(EVP_CIPHER_CTX *ctx, const uint8_t *key,
const EVP_CIPHER *kt, int enc) const char *ciphername, int enc)
{ {
ASSERT(NULL != kt && NULL != ctx); ASSERT(NULL != ciphername && NULL != ctx);
evp_cipher_type *kt = cipher_get(ciphername);
EVP_CIPHER_CTX_reset(ctx); EVP_CIPHER_CTX_reset(ctx);
if (!EVP_CipherInit(ctx, kt, NULL, NULL, enc)) if (!EVP_CipherInit(ctx, kt, NULL, NULL, enc))
{ {
crypto_msg(M_FATAL, "EVP cipher init #1"); crypto_msg(M_FATAL, "EVP cipher init #1");
} }
#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
if (!EVP_CIPHER_CTX_set_key_length(ctx, key_len))
{
crypto_msg(M_FATAL, "EVP set key size");
}
#endif
if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, enc)) if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, enc))
{ {
crypto_msg(M_FATAL, "EVP cipher init #2"); crypto_msg(M_FATAL, "EVP cipher init #2");
} }
EVP_CIPHER_free(kt);
/* make sure we used a big enough key */ /* make sure we used a big enough key */
ASSERT(EVP_CIPHER_CTX_key_length(ctx) <= key_len); ASSERT(EVP_CIPHER_CTX_key_length(ctx) <= EVP_CIPHER_key_length(kt));
} }
int int
...@@ -708,11 +853,7 @@ cipher_ctx_iv_length(const EVP_CIPHER_CTX *ctx) ...@@ -708,11 +853,7 @@ cipher_ctx_iv_length(const EVP_CIPHER_CTX *ctx)
int int
cipher_ctx_get_tag(EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size) cipher_ctx_get_tag(EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES return EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_size, tag_buf);
return EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag_buf);
#else
ASSERT(0);
#endif
} }
int int
...@@ -727,15 +868,66 @@ cipher_ctx_mode(const EVP_CIPHER_CTX *ctx) ...@@ -727,15 +868,66 @@ cipher_ctx_mode(const EVP_CIPHER_CTX *ctx)
return EVP_CIPHER_CTX_mode(ctx); return EVP_CIPHER_CTX_mode(ctx);
} }
const cipher_kt_t *
cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx) bool
cipher_ctx_mode_cbc(const cipher_ctx_t *ctx)
{
if (!ctx)
{
return false;
}
int flags = EVP_CIPHER_CTX_flags(ctx);
int mode = EVP_CIPHER_CTX_mode(ctx);
return mode == EVP_CIPH_CBC_MODE
/* Exclude AEAD cipher modes, they require a different API */
#ifdef EVP_CIPH_FLAG_CTS
&& !(flags & EVP_CIPH_FLAG_CTS)
#endif
&& !(flags & EVP_CIPH_FLAG_AEAD_CIPHER);
}
bool
cipher_ctx_mode_ofb_cfb(const cipher_ctx_t *ctx)
{ {
return ctx ? EVP_CIPHER_CTX_cipher(ctx) : NULL; if (!ctx)
{
return false;
}
int mode = EVP_CIPHER_CTX_get_mode(ctx);
return (mode == EVP_CIPH_OFB_MODE || mode == EVP_CIPH_CFB_MODE)
/* Exclude AEAD cipher modes, they require a different API */
&& !(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER);
}
bool
cipher_ctx_mode_aead(const cipher_ctx_t *ctx)
{
if (ctx)
{
int flags = EVP_CIPHER_CTX_flags(ctx);
if (flags & EVP_CIPH_FLAG_AEAD_CIPHER)
{
return true;
}
#if defined(NID_chacha20_poly1305) && OPENSSL_VERSION_NUMBER < 0x30000000L
if (EVP_CIPHER_CTX_nid(ctx) == NID_chacha20_poly1305)
{
return true;
}
#endif
}
return false;
} }
int int
cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) cipher_ctx_reset(EVP_CIPHER_CTX *ctx, const uint8_t *iv_buf)
{ {
return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv_buf, -1); return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv_buf, -1);
} }
...@@ -743,16 +935,12 @@ cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) ...@@ -743,16 +935,12 @@ cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf)
int int
cipher_ctx_update_ad(EVP_CIPHER_CTX *ctx, const uint8_t *src, int src_len) cipher_ctx_update_ad(EVP_CIPHER_CTX *ctx, const uint8_t *src, int src_len)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES
int len; int len;
if (!EVP_CipherUpdate(ctx, NULL, &len, src, src_len)) if (!EVP_CipherUpdate(ctx, NULL, &len, src, src_len))
{ {
crypto_msg(M_FATAL, "%s: EVP_CipherUpdate() failed", __func__); crypto_msg(M_FATAL, "%s: EVP_CipherUpdate() failed", __func__);
} }
return 1; return 1;
#else /* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT(0);
#endif
} }
int int
...@@ -776,28 +964,58 @@ int ...@@ -776,28 +964,58 @@ int
cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len,
uint8_t *tag, size_t tag_len) uint8_t *tag, size_t tag_len)
{ {
#ifdef HAVE_AEAD_CIPHER_MODES
ASSERT(tag_len < SIZE_MAX); ASSERT(tag_len < SIZE_MAX);
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag))
{ {
return 0; return 0;
} }
return cipher_ctx_final(ctx, dst, dst_len); return cipher_ctx_final(ctx, dst, dst_len);
#else /* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT(0);
#endif
} }
void void
cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
unsigned char *src, unsigned char src[DES_KEY_LENGTH],
unsigned char *dst) unsigned char dst[DES_KEY_LENGTH])
{ {
DES_key_schedule sched; /* We are using 3DES here with three times the same key to cheat
* and emulate DES as 3DES is better supported than DES */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
crypto_msg(M_FATAL, "%s: EVP_CIPHER_CTX_new() failed", __func__);
}
unsigned char key3[DES_KEY_LENGTH*3];
for (int i = 0; i < 3; i++)
{
memcpy(key3 + (i * DES_KEY_LENGTH), key, DES_KEY_LENGTH);
}
if (!EVP_EncryptInit_ex(ctx, EVP_des_ede3_ecb(), NULL, key3, NULL))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptInit_ex() failed", __func__);
}
int len;
/* The EVP_EncryptFinal method will write to the dst+len pointer even
* though there is nothing to encrypt anymore, provide space for that to
* not overflow the stack */
unsigned char dst2[DES_KEY_LENGTH * 2];
if (!EVP_EncryptUpdate(ctx, dst2, &len, src, DES_KEY_LENGTH))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptUpdate() failed", __func__);
}
if (!EVP_EncryptFinal(ctx, dst2 + len, &len))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptFinal() failed", __func__);
}
memcpy(dst, dst2, DES_KEY_LENGTH);
DES_set_key_unchecked((DES_cblock *)key, &sched); EVP_CIPHER_CTX_free(ctx);
DES_ecb_encrypt((DES_cblock *)src, (DES_cblock *)dst, &sched, DES_ENCRYPT);
} }
/* /*
...@@ -807,12 +1025,12 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], ...@@ -807,12 +1025,12 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
*/ */
const EVP_MD * static evp_md_type *
md_kt_get(const char *digest) md_get(const char *digest)
{ {
const EVP_MD *md = NULL; evp_md_type *md = NULL;
ASSERT(digest); ASSERT(digest);
md = EVP_get_digestbyname(digest); md = EVP_MD_fetch(NULL, digest, NULL);
if (!md) if (!md)
{ {
crypto_msg(M_FATAL, "Message hash algorithm '%s' not found", digest); crypto_msg(M_FATAL, "Message hash algorithm '%s' not found", digest);
...@@ -827,20 +1045,40 @@ md_kt_get(const char *digest) ...@@ -827,20 +1045,40 @@ md_kt_get(const char *digest)
return md; return md;
} }
bool
md_valid(const char *digest)
{
evp_md_type *md = EVP_MD_fetch(NULL, digest, NULL);
bool valid = (md != NULL);
EVP_MD_free(md);
return valid;
}
const char * const char *
md_kt_name(const EVP_MD *kt) md_kt_name(const char *mdname)
{ {
if (NULL == kt) if (!strcmp("none", mdname))
{ {
return "[null-digest]"; return "[null-digest]";
} }
return EVP_MD_name(kt); evp_md_type *kt = md_get(mdname);
const char *name = EVP_MD_get0_name(kt);
EVP_MD_free(kt);
return name;
} }
int unsigned char
md_kt_size(const EVP_MD *kt) md_kt_size(const char *mdname)
{ {
return EVP_MD_size(kt); if (!strcmp("none", mdname))
{
return 0;
}
evp_md_type *kt = md_get(mdname);
unsigned char size = (unsigned char)EVP_MD_size(kt);
EVP_MD_free(kt);
return size;
} }
...@@ -851,11 +1089,14 @@ md_kt_size(const EVP_MD *kt) ...@@ -851,11 +1089,14 @@ md_kt_size(const EVP_MD *kt)
*/ */
int int
md_full(const EVP_MD *kt, const uint8_t *src, int src_len, uint8_t *dst) md_full(const char *mdname, const uint8_t *src, int src_len, uint8_t *dst)
{ {
unsigned int in_md_len = 0; unsigned int in_md_len = 0;
evp_md_type *kt = md_get(mdname);
return EVP_Digest(src, src_len, dst, &in_md_len, kt, NULL); int ret = EVP_Digest(src, src_len, dst, &in_md_len, kt, NULL);
EVP_MD_free(kt);
return ret;
} }
EVP_MD_CTX * EVP_MD_CTX *
...@@ -866,18 +1107,24 @@ md_ctx_new(void) ...@@ -866,18 +1107,24 @@ md_ctx_new(void)
return ctx; return ctx;
} }
void md_ctx_free(EVP_MD_CTX *ctx) void
md_ctx_free(EVP_MD_CTX *ctx)
{ {
EVP_MD_CTX_free(ctx); EVP_MD_CTX_free(ctx);
} }
void void
md_ctx_init(EVP_MD_CTX *ctx, const EVP_MD *kt) md_ctx_init(EVP_MD_CTX *ctx, const char *mdname)
{ {
evp_md_type *kt = md_get(mdname);
ASSERT(NULL != ctx && NULL != kt); ASSERT(NULL != ctx && NULL != kt);
EVP_MD_CTX_init(ctx); EVP_MD_CTX_init(ctx);
EVP_DigestInit(ctx, kt); if (!EVP_DigestInit(ctx, kt))
{
crypto_msg(M_FATAL, "EVP_DigestInit failed");
}
EVP_MD_free(kt);
} }
void void
...@@ -912,7 +1159,7 @@ md_ctx_final(EVP_MD_CTX *ctx, uint8_t *dst) ...@@ -912,7 +1159,7 @@ md_ctx_final(EVP_MD_CTX *ctx, uint8_t *dst)
* Generic HMAC functions * Generic HMAC functions
* *
*/ */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
HMAC_CTX * HMAC_CTX *
hmac_ctx_new(void) hmac_ctx_new(void)
{ {
...@@ -928,13 +1175,17 @@ hmac_ctx_free(HMAC_CTX *ctx) ...@@ -928,13 +1175,17 @@ hmac_ctx_free(HMAC_CTX *ctx)
} }
void void
hmac_ctx_init(HMAC_CTX *ctx, const uint8_t *key, int key_len, hmac_ctx_init(HMAC_CTX *ctx, const uint8_t *key, const char *mdname)
const EVP_MD *kt)
{ {
evp_md_type *kt = md_get(mdname);
ASSERT(NULL != kt && NULL != ctx); ASSERT(NULL != kt && NULL != ctx);
int key_len = EVP_MD_size(kt);
HMAC_CTX_reset(ctx); HMAC_CTX_reset(ctx);
HMAC_Init_ex(ctx, key, key_len, kt, NULL); if (!HMAC_Init_ex(ctx, key, key_len, kt, NULL))
{
crypto_msg(M_FATAL, "HMAC_Init_ex failed");
}
/* make sure we used a big enough key */ /* make sure we used a big enough key */
ASSERT(HMAC_size(ctx) <= key_len); ASSERT(HMAC_size(ctx) <= key_len);
...@@ -947,7 +1198,7 @@ hmac_ctx_cleanup(HMAC_CTX *ctx) ...@@ -947,7 +1198,7 @@ hmac_ctx_cleanup(HMAC_CTX *ctx)
} }
int int
hmac_ctx_size(const HMAC_CTX *ctx) hmac_ctx_size(HMAC_CTX *ctx)
{ {
return HMAC_size(ctx); return HMAC_size(ctx);
} }
...@@ -955,7 +1206,10 @@ hmac_ctx_size(const HMAC_CTX *ctx) ...@@ -955,7 +1206,10 @@ hmac_ctx_size(const HMAC_CTX *ctx)
void void
hmac_ctx_reset(HMAC_CTX *ctx) hmac_ctx_reset(HMAC_CTX *ctx)
{ {
HMAC_Init_ex(ctx, NULL, 0, NULL, NULL); if (!HMAC_Init_ex(ctx, NULL, 0, NULL, NULL))
{
crypto_msg(M_FATAL, "HMAC_Init_ex failed");
}
} }
void void
...@@ -971,5 +1225,390 @@ hmac_ctx_final(HMAC_CTX *ctx, uint8_t *dst) ...@@ -971,5 +1225,390 @@ hmac_ctx_final(HMAC_CTX *ctx, uint8_t *dst)
HMAC_Final(ctx, dst, &in_hmac_len); HMAC_Final(ctx, dst, &in_hmac_len);
} }
#else /* if OPENSSL_VERSION_NUMBER < 0x30000000L */
hmac_ctx_t *
hmac_ctx_new(void)
{
hmac_ctx_t *ctx;
ALLOC_OBJ_CLEAR(ctx, hmac_ctx_t);
EVP_MAC *hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
ctx->ctx = EVP_MAC_CTX_new(hmac);
check_malloc_return(ctx->ctx);
EVP_MAC_free(hmac);
return ctx;
}
#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_OPENSSL */ void
hmac_ctx_free(hmac_ctx_t *ctx)
{
EVP_MAC_CTX_free(ctx->ctx);
secure_memzero(ctx, sizeof(hmac_ctx_t));
free(ctx);
}
void
hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, const char *mdname)
{
evp_md_type *kt = md_get(mdname);
ASSERT(NULL != kt && NULL != ctx && ctx->ctx != NULL);
/* We need to make a copy of the key since the OSSL parameters
* only reference it */
memcpy(ctx->key, key, EVP_MD_size(kt));
/* Lookup/setting of parameters in OpenSSL 3.0 are string based
*
* The OSSL_PARAM_construct_utf8_string needs a non const str but this
* only used for lookup so we cast (as OpenSSL also does internally)
* the constness away here.
*/
ctx->params[0] = OSSL_PARAM_construct_utf8_string("digest",
(char *) EVP_MD_get0_name(kt), 0);
ctx->params[1] = OSSL_PARAM_construct_octet_string("key",
ctx->key, EVP_MD_size(kt));
ctx->params[2] = OSSL_PARAM_construct_end();
if (!EVP_MAC_init(ctx->ctx, NULL, 0, ctx->params))
{
crypto_msg(M_FATAL, "EVP_MAC_init failed");
}
EVP_MD_free(kt);
}
void
hmac_ctx_cleanup(hmac_ctx_t *ctx)
{
EVP_MAC_init(ctx->ctx, NULL, 0, NULL);
}
int
hmac_ctx_size(hmac_ctx_t *ctx)
{
return (int)EVP_MAC_CTX_get_mac_size(ctx->ctx);
}
void
hmac_ctx_reset(hmac_ctx_t *ctx)
{
/* The OpenSSL MAC API lacks a reset method and passing NULL as params
* does not reset it either, so use the params array to reinitialise it the
* same way as before */
if (!EVP_MAC_init(ctx->ctx, NULL, 0, ctx->params))
{
crypto_msg(M_FATAL, "EVP_MAC_init failed");
}
}
void
hmac_ctx_update(hmac_ctx_t *ctx, const uint8_t *src, int src_len)
{
EVP_MAC_update(ctx->ctx, src, src_len);
}
void
hmac_ctx_final(hmac_ctx_t *ctx, uint8_t *dst)
{
/* The calling code always gives us a buffer that has the size of our
* algorithm */
size_t in_hmac_len = EVP_MAC_CTX_get_mac_size(ctx->ctx);
EVP_MAC_final(ctx->ctx, dst, &in_hmac_len, in_hmac_len);
}
#endif /* if OPENSSL_VERSION_NUMBER < 0x30000000L */
int
memcmp_constant_time(const void *a, const void *b, size_t size)
{
return CRYPTO_memcmp(a, b, size);
}
#if HAVE_OPENSSL_ENGINE
static int
ui_reader(UI *ui, UI_STRING *uis)
{
SSL_CTX *ctx = UI_get0_user_data(ui);
if (UI_get_string_type(uis) == UIT_PROMPT)
{
pem_password_cb *cb = SSL_CTX_get_default_passwd_cb(ctx);
void *d = SSL_CTX_get_default_passwd_cb_userdata(ctx);
char password[64];
cb(password, sizeof(password), 0, d);
UI_set_result(ui, uis, password);
return 1;
}
return 0;
}
#endif
EVP_PKEY *
engine_load_key(const char *file, SSL_CTX *ctx)
{
#if HAVE_OPENSSL_ENGINE
UI_METHOD *ui;
EVP_PKEY *pkey;
if (!engine_persist)
{
return NULL;
}
/* this will print out the error from BIO_read */
crypto_msg(M_INFO, "PEM_read_bio failed, now trying engine method to load private key");
ui = UI_create_method("openvpn");
if (!ui)
{
crypto_msg(M_FATAL, "Engine UI creation failed");
return NULL;
}
UI_method_set_reader(ui, ui_reader);
ENGINE_init(engine_persist);
pkey = ENGINE_load_private_key(engine_persist, file, ui, ctx);
ENGINE_finish(engine_persist);
if (!pkey)
{
crypto_msg(M_FATAL, "Engine could not load key file");
}
UI_destroy_method(ui);
return pkey;
#else /* if HAVE_OPENSSL_ENGINE */
return NULL;
#endif /* if HAVE_OPENSSL_ENGINE */
}
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
bool
ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
int secret_len, uint8_t *output, int output_len)
{
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL);
if (!pctx)
{
return false;
}
bool ret = false;
if (!EVP_PKEY_derive_init(pctx))
{
goto out;
}
if (!EVP_PKEY_CTX_set_tls1_prf_md(pctx, EVP_md5_sha1()))
{
goto out;
}
if (!EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, secret, secret_len))
{
goto out;
}
if (!EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed, seed_len))
{
goto out;
}
size_t out_len = output_len;
if (!EVP_PKEY_derive(pctx, output, &out_len))
{
goto out;
}
if (out_len != output_len)
{
goto out;
}
ret = true;
out:
EVP_PKEY_CTX_free(pctx);
return ret;
}
#else /* if OPENSSL_VERSION_NUMBER >= 0x10100000L */
/*
* Generate the hash required by for the \c tls1_PRF function.
*
* We cannot use our normal hmac_* function as they do not work
* in a FIPS environment (no MD5 allowed, which we need). Instead
* we need to directly use the EVP_MD_* API with the special
* EVP_MD_CTX_FLAG_NON_FIPS_ALLOW flag.
*
* The function below is adapted from OpenSSL 1.0.2t
*
* @param md_kt Message digest to use
* @param sec Secret to base the hash on
* @param sec_len Length of the secret
* @param seed Seed to hash
* @param seed_len Length of the seed
* @param out Output buffer
* @param olen Length of the output buffer
*/
static
bool
tls1_P_hash(const EVP_MD *md, const unsigned char *sec,
int sec_len, const void *seed, int seed_len,
unsigned char *out, int olen)
{
int chunk;
size_t j;
EVP_MD_CTX ctx, ctx_tmp, ctx_init;
EVP_PKEY *mac_key;
unsigned char A1[EVP_MAX_MD_SIZE];
size_t A1_len = EVP_MAX_MD_SIZE;
int ret = false;
chunk = EVP_MD_size(md);
OPENSSL_assert(chunk >= 0);
EVP_MD_CTX_init(&ctx);
EVP_MD_CTX_init(&ctx_tmp);
EVP_MD_CTX_init(&ctx_init);
EVP_MD_CTX_set_flags(&ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len);
if (!mac_key)
{
goto err;
}
if (!EVP_DigestSignInit(&ctx_init, NULL, md, NULL, mac_key))
{
goto err;
}
if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init))
{
goto err;
}
if (!EVP_DigestSignUpdate(&ctx, seed, seed_len))
{
goto err;
}
if (!EVP_DigestSignFinal(&ctx, A1, &A1_len))
{
goto err;
}
for (;; )
{
/* Reinit mac contexts */
if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init))
{
goto err;
}
if (!EVP_DigestSignUpdate(&ctx, A1, A1_len))
{
goto err;
}
if (olen > chunk && !EVP_MD_CTX_copy_ex(&ctx_tmp, &ctx))
{
goto err;
}
if (!EVP_DigestSignUpdate(&ctx, seed, seed_len))
{
goto err;
}
if (olen > chunk)
{
j = olen;
if (!EVP_DigestSignFinal(&ctx, out, &j))
{
goto err;
}
out += j;
olen -= j;
/* calc the next A1 value */
if (!EVP_DigestSignFinal(&ctx_tmp, A1, &A1_len))
{
goto err;
}
}
else
{
A1_len = EVP_MAX_MD_SIZE;
/* last one */
if (!EVP_DigestSignFinal(&ctx, A1, &A1_len))
{
goto err;
}
memcpy(out, A1, olen);
break;
}
}
ret = true;
err:
EVP_PKEY_free(mac_key);
EVP_MD_CTX_cleanup(&ctx);
EVP_MD_CTX_cleanup(&ctx_tmp);
EVP_MD_CTX_cleanup(&ctx_init);
OPENSSL_cleanse(A1, sizeof(A1));
return ret;
}
/*
* Use the TLS PRF function for generating data channel keys.
* This code is based on the OpenSSL library.
*
* TLS generates keys as such:
*
* master_secret[48] = PRF(pre_master_secret[48], "master secret",
* ClientHello.random[32] + ServerHello.random[32])
*
* key_block[] = PRF(SecurityParameters.master_secret[48],
* "key expansion",
* SecurityParameters.server_random[32] +
* SecurityParameters.client_random[32]);
*
* Notes:
*
* (1) key_block contains a full set of 4 keys.
* (2) The pre-master secret is generated by the client.
*/
bool
ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec,
int slen, uint8_t *out1, int olen)
{
bool ret = true;
struct gc_arena gc = gc_new();
/* For some reason our md_get("MD5") fails otherwise in the unit test */
const EVP_MD *md5 = EVP_md5();
const EVP_MD *sha1 = EVP_sha1();
uint8_t *out2 = (uint8_t *)gc_malloc(olen, false, &gc);
int len = slen/2;
const uint8_t *S1 = sec;
const uint8_t *S2 = &(sec[len]);
len += (slen&1); /* add for odd, make longer */
if (!tls1_P_hash(md5, S1, len, label, label_len, out1, olen))
{
ret = false;
goto done;
}
if (!tls1_P_hash(sha1, S2, len, label, label_len, out2, olen))
{
ret = false;
goto done;
}
for (int i = 0; i < olen; i++)
{
out1[i] ^= out2[i];
}
secure_memzero(out2, olen);
dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc));
done:
gc_free(&gc);
return ret;
}
#endif /* if OPENSSL_VERSION_NUMBER >= 0x10100000L */
#endif /* ENABLE_CRYPTO_OPENSSL */
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and * packet encryption, packet authentication, and
* packet compression. * packet compression.
* *
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
...@@ -33,12 +33,9 @@ ...@@ -33,12 +33,9 @@
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <openssl/md5.h> #include <openssl/md5.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/** Generic cipher key type %context. */ #include <openssl/provider.h>
typedef EVP_CIPHER cipher_kt_t; #endif
/** Generic message digest key type %context. */
typedef EVP_MD md_kt_t;
/** Generic cipher %context. */ /** Generic cipher %context. */
typedef EVP_CIPHER_CTX cipher_ctx_t; typedef EVP_CIPHER_CTX cipher_ctx_t;
...@@ -47,8 +44,32 @@ typedef EVP_CIPHER_CTX cipher_ctx_t; ...@@ -47,8 +44,32 @@ typedef EVP_CIPHER_CTX cipher_ctx_t;
typedef EVP_MD_CTX md_ctx_t; typedef EVP_MD_CTX md_ctx_t;
/** Generic HMAC %context. */ /** Generic HMAC %context. */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
typedef HMAC_CTX hmac_ctx_t; typedef HMAC_CTX hmac_ctx_t;
/* Use a dummy type for the provider */
typedef void provider_t;
#else
typedef struct {
OSSL_PARAM params[3];
uint8_t key[EVP_MAX_KEY_LENGTH];
EVP_MAC_CTX *ctx;
} hmac_ctx_t;
typedef OSSL_PROVIDER provider_t;
#endif
/* In OpenSSL 3.0 the method that returns EVP_CIPHER, the cipher needs to be
* freed afterwards, thus needing a non-const type. In constrast OpenSSL 1.1.1
* and lower returns a const type, needing a const type */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
typedef const EVP_CIPHER evp_cipher_type;
typedef const EVP_MD evp_md_type;
#else
typedef EVP_CIPHER evp_cipher_type;
typedef EVP_MD evp_md_type;
#endif
/** Maximum length of an IV */ /** Maximum length of an IV */
#define OPENVPN_MAX_IV_LENGTH EVP_MAX_IV_LENGTH #define OPENVPN_MAX_IV_LENGTH EVP_MAX_IV_LENGTH
...@@ -61,13 +82,9 @@ typedef HMAC_CTX hmac_ctx_t; ...@@ -61,13 +82,9 @@ typedef HMAC_CTX hmac_ctx_t;
/** Cipher is in CFB mode */ /** Cipher is in CFB mode */
#define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE #define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE
#ifdef HAVE_AEAD_CIPHER_MODES
/** Cipher is in GCM mode */ /** Cipher is in GCM mode */
#define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE #define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE
#endif /* HAVE_AEAD_CIPHER_MODES */
/** Cipher should encrypt */ /** Cipher should encrypt */
#define OPENVPN_OP_ENCRYPT 1 #define OPENVPN_OP_ENCRYPT 1
...@@ -101,5 +118,16 @@ void crypto_print_openssl_errors(const unsigned int flags); ...@@ -101,5 +118,16 @@ void crypto_print_openssl_errors(const unsigned int flags);
msg((flags), __VA_ARGS__); \ msg((flags), __VA_ARGS__); \
} while (false) } while (false)
/**
* Load a key file from an engine
*
* @param file The engine file to load
* @param ui The UI method for the password prompt
* @param data The data to pass to the UI method
*
* @return The private key if successful or NULL if not
*/
EVP_PKEY *
engine_load_key(const char *file, SSL_CTX *ctx);
#endif /* CRYPTO_OPENSSL_H_ */ #endif /* CRYPTO_OPENSSL_H_ */
/* /*
* Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com> * Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>
* Copyright (c) 2018 Selva Nair <selva.nair@gmail.com>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modifi- * Redistribution and use in source and binary forms, with or without modifi-
...@@ -51,103 +52,31 @@ ...@@ -51,103 +52,31 @@
#include "buffer.h" #include "buffer.h"
#include "openssl_compat.h" #include "openssl_compat.h"
#include "win32.h" #include "win32.h"
#include "xkey_common.h"
/* MinGW w32api 3.17 is still incomplete when it comes to CryptoAPI while #ifndef HAVE_XKEY_PROVIDER
* MinGW32-w64 defines all macros used. This is a hack around that problem. /* index for storing external data in EC_KEY: < 0 means uninitialized */
*/ static int ec_data_idx = -1;
#ifndef CERT_SYSTEM_STORE_LOCATION_SHIFT
#define CERT_SYSTEM_STORE_LOCATION_SHIFT 16
#endif
#ifndef CERT_SYSTEM_STORE_CURRENT_USER_ID
#define CERT_SYSTEM_STORE_CURRENT_USER_ID 1
#endif
#ifndef CERT_SYSTEM_STORE_CURRENT_USER
#define CERT_SYSTEM_STORE_CURRENT_USER (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT)
#endif
#ifndef CERT_STORE_READONLY_FLAG
#define CERT_STORE_READONLY_FLAG 0x00008000
#endif
#ifndef CERT_STORE_OPEN_EXISTING_FLAG
#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
#endif
/* Size of an SSL signature: MD5+SHA1 */
#define SSL_SIG_LENGTH 36
/* try to funnel any Windows/CryptoAPI error messages to OpenSSL ERR_... */
#define ERR_LIB_CRYPTOAPI (ERR_LIB_USER + 69) /* 69 is just a number... */
#define CRYPTOAPIerr(f) err_put_ms_error(GetLastError(), (f), __FILE__, __LINE__)
#define CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE 100
#define CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE 101
#define CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY 102
#define CRYPTOAPI_F_CRYPT_CREATE_HASH 103
#define CRYPTOAPI_F_CRYPT_GET_HASH_PARAM 104
#define CRYPTOAPI_F_CRYPT_SET_HASH_PARAM 105
#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106
#define CRYPTOAPI_F_LOAD_LIBRARY 107
#define CRYPTOAPI_F_GET_PROC_ADDRESS 108
#define CRYPTOAPI_F_NCRYPT_SIGN_HASH 109
static ERR_STRING_DATA CRYPTOAPI_str_functs[] = {
{ ERR_PACK(ERR_LIB_CRYPTOAPI, 0, 0), "microsoft cryptoapi"},
{ ERR_PACK(0, CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE, 0), "CertOpenSystemStore" },
{ ERR_PACK(0, CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE, 0), "CertFindCertificateInStore" },
{ ERR_PACK(0, CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY, 0), "CryptAcquireCertificatePrivateKey" },
{ ERR_PACK(0, CRYPTOAPI_F_CRYPT_CREATE_HASH, 0), "CryptCreateHash" },
{ ERR_PACK(0, CRYPTOAPI_F_CRYPT_GET_HASH_PARAM, 0), "CryptGetHashParam" },
{ ERR_PACK(0, CRYPTOAPI_F_CRYPT_SET_HASH_PARAM, 0), "CryptSetHashParam" },
{ ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" },
{ ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" },
{ ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" },
{ ERR_PACK(0, CRYPTOAPI_F_NCRYPT_SIGN_HASH, 0), "NCryptSignHash" },
{ 0, NULL }
};
/* Global EVP_PKEY_METHOD used to override the sign operation */ /* Global EVP_PKEY_METHOD used to override the sign operation */
static EVP_PKEY_METHOD *pmethod; static EVP_PKEY_METHOD *pmethod;
static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx); static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx);
static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig,
size_t *siglen, const unsigned char *tbs, size_t tbslen); size_t *siglen, const unsigned char *tbs, size_t tbslen);
#else /* ifndef HAVE_XKEY_PROVIDER */
static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign;
#endif /* HAVE_XKEY_PROVIDER */
typedef struct _CAPI_DATA { typedef struct _CAPI_DATA {
const CERT_CONTEXT *cert_context; const CERT_CONTEXT *cert_context;
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov; HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
EVP_PKEY *pubkey;
DWORD key_spec; DWORD key_spec;
BOOL free_crypt_prov; BOOL free_crypt_prov;
int ref_count;
} CAPI_DATA; } CAPI_DATA;
/** /*
* Translate OpenSSL padding type to CNG padding type
* Returns 0 for unknown/unsupported padding.
*/
static DWORD
cng_padding_type(int padding)
{
DWORD pad = 0;
switch (padding)
{
case RSA_NO_PADDING:
pad = BCRYPT_PAD_NONE;
break;
case RSA_PKCS1_PADDING:
pad = BCRYPT_PAD_PKCS1;
break;
case RSA_PKCS1_PSS_PADDING:
pad = BCRYPT_PAD_PSS;
break;
default:
msg(M_WARN|M_INFO, "cryptoapicert: unknown OpenSSL padding type %d.",
padding);
}
return pad;
}
/**
* Translate OpenSSL hash OID to CNG algorithm name. Returns * Translate OpenSSL hash OID to CNG algorithm name. Returns
* "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1 * "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1
* mixed hash used in TLS 1.1 and earlier. * mixed hash used in TLS 1.1 and earlier.
...@@ -190,116 +119,69 @@ cng_hash_algo(int md_type) ...@@ -190,116 +119,69 @@ cng_hash_algo(int md_type)
return alg; return alg;
} }
static char *
ms_error_text(DWORD ms_err)
{
LPVOID lpMsgBuf = NULL;
char *rv = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ms_err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
(LPTSTR) &lpMsgBuf, 0, NULL);
if (lpMsgBuf)
{
char *p;
rv = string_alloc(lpMsgBuf, NULL);
LocalFree(lpMsgBuf);
/* trim to the left */
if (rv)
{
for (p = rv + strlen(rv) - 1; p >= rv; p--) {
if (isspace(*p))
{
*p = '\0';
}
else
{
break;
}
}
}
}
return rv;
}
static void static void
err_put_ms_error(DWORD ms_err, int func, const char *file, int line) CAPI_DATA_free(CAPI_DATA *cd)
{ {
static int init = 0; if (!cd || cd->ref_count-- > 0)
#define ERR_MAP_SZ 16
static struct {
int err;
DWORD ms_err; /* I don't think we get more than 16 *different* errors */
} err_map[ERR_MAP_SZ]; /* in here, before we give up the whole thing... */
int i;
if (ms_err == 0)
{ {
/* 0 is not an error */
return; return;
} }
if (!init) if (cd->free_crypt_prov && cd->crypt_prov)
{ {
ERR_load_strings(ERR_LIB_CRYPTOAPI, CRYPTOAPI_str_functs); if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
memset(&err_map, 0, sizeof(err_map));
init++;
}
/* since MS error codes are 32 bit, and the ones in the ERR_... system is
* only 12, we must have a mapping table between them. */
for (i = 0; i < ERR_MAP_SZ; i++) {
if (err_map[i].ms_err == ms_err)
{ {
ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line); NCryptFreeObject(cd->crypt_prov);
break;
} }
else if (err_map[i].ms_err == 0) else
{ {
/* end of table, add new entry */ CryptReleaseContext(cd->crypt_prov, 0);
ERR_STRING_DATA *esd = calloc(2, sizeof(*esd));
if (esd == NULL)
{
break;
}
err_map[i].ms_err = ms_err;
err_map[i].err = esd->error = i + 100;
esd->string = ms_error_text(ms_err);
check_malloc_return(esd->string);
ERR_load_strings(ERR_LIB_CRYPTOAPI, esd);
ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
break;
} }
} }
if (cd->cert_context)
{
CertFreeCertificateContext(cd->cert_context);
}
EVP_PKEY_free(cd->pubkey); /* passing NULL is okay */
free(cd);
} }
/* encrypt */ #ifndef HAVE_XKEY_PROVIDER
static int
rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) /* Translate OpenSSL padding type to CNG padding type
* Returns 0 for unknown/unsupported padding.
*/
static DWORD
cng_padding_type(int padding)
{ {
/* I haven't been able to trigger this one, but I want to know if it happens... */ DWORD pad = 0;
assert(0);
return 0; switch (padding)
} {
case RSA_NO_PADDING:
break;
/* verify arbitrary data */ case RSA_PKCS1_PADDING:
static int pad = BCRYPT_PAD_PKCS1;
rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) break;
{
/* I haven't been able to trigger this one, but I want to know if it happens... */
assert(0);
return 0; case RSA_PKCS1_PSS_PADDING:
pad = BCRYPT_PAD_PSS;
break;
default:
msg(M_WARN|M_INFO, "cryptoapicert: unknown OpenSSL padding type %d.",
padding);
}
return pad;
} }
/** /**
* Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT * Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT
* key handle in cd->crypt_prov. On return the signature is in 'to'. Returns * key handle in cd->crypt_prov. On return the signature is in 'to'. Returns
* the length of the signature or 0 on error. * the length of the signature or 0 on error.
* Only RSA is supported and padding should be BCRYPT_PAD_PKCS1 or * This is used only for RSA and padding should be BCRYPT_PAD_PKCS1 or
* BCRYPT_PAD_PSS. * BCRYPT_PAD_PSS.
* If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added * If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added
* to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used * to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used
...@@ -333,14 +215,14 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char ...@@ -333,14 +215,14 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char
} }
else else
{ {
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); msg(M_NONFATAL, "Error in cryptoapicert: Unknown padding type");
return 0; return 0;
} }
if (status != ERROR_SUCCESS) if (status != ERROR_SUCCESS)
{ {
SetLastError(status); SetLastError(status);
CRYPTOAPIerr(CRYPTOAPI_F_NCRYPT_SIGN_HASH); msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: NCryptSignHash failed");
len = 0; len = 0;
} }
...@@ -348,180 +230,212 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char ...@@ -348,180 +230,212 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char
return len; return len;
} }
/* sign arbitrary data */ /* called at RSA_free */
static int static int
rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) rsa_finish(RSA *rsa)
{ {
CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(RSA_get_method(rsa)); const RSA_METHOD *rsa_meth = RSA_get_method(rsa);
HCRYPTHASH hash; CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(rsa_meth);
DWORD hash_size, len, i;
unsigned char *buf;
if (cd == NULL) if (cd == NULL)
{ {
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
return 0; return 0;
} }
CAPI_DATA_free(cd);
RSA_meth_free((RSA_METHOD *) rsa_meth);
return 1;
}
if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) static EC_KEY_METHOD *ec_method = NULL;
{
return priv_enc_CNG(cd, NULL, from, flen, to, RSA_size(rsa),
cng_padding_type(padding), 0);
}
if (padding != RSA_PKCS1_PADDING) /** EC_KEY_METHOD callback: called when the key is freed */
{ static void
/* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ ec_finish(EC_KEY *ec)
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); {
return 0; EC_KEY_METHOD_free(ec_method);
} ec_method = NULL;
CAPI_DATA *cd = EC_KEY_get_ex_data(ec, ec_data_idx);
CAPI_DATA_free(cd);
EC_KEY_set_ex_data(ec, ec_data_idx, NULL);
}
/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would /** EC_KEY_METHOD callback sign_setup(): we do nothing here */
* be way to straightforward for M$, I guess... So we have to do it this static int
* tricky way instead, by creating a "Hash", and load the already-made hash ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp)
* from 'from' into it. */ {
/* For now, we only support NID_md5_sha1 */ return 1;
if (flen != SSL_SIG_LENGTH) }
{ #endif /* HAVE_XKEY_PROVIDER */
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
return 0; /**
} * Helper to convert ECDSA signature returned by NCryptSignHash
if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) * to an ECDSA_SIG structure.
{ * On entry 'buf[]' of length len contains r and s concatenated.
CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH); * Returns a newly allocated ECDSA_SIG or NULL (on error).
return 0; */
} static ECDSA_SIG *
len = sizeof(hash_size); ecdsa_bin2sig(unsigned char *buf, int len)
if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) {
ECDSA_SIG *ecsig = NULL;
DWORD rlen = len/2;
BIGNUM *r = BN_bin2bn(buf, rlen, NULL);
BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL);
if (!r || !s)
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM); goto err;
CryptDestroyHash(hash);
return 0;
} }
if ((int) hash_size != flen) ecsig = ECDSA_SIG_new(); /* in openssl 1.1 this does not allocate r, s */
if (!ecsig)
{ {
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); goto err;
CryptDestroyHash(hash);
return 0;
} }
if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM); ECDSA_SIG_free(ecsig);
CryptDestroyHash(hash); goto err;
return 0;
} }
return ecsig;
err:
BN_free(r); /* it is ok to free NULL BN */
BN_free(s);
return NULL;
}
len = RSA_size(rsa); #ifndef HAVE_XKEY_PROVIDER
buf = malloc(len);
if (buf == NULL) /** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */
{ static ECDSA_SIG *
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); ecdsa_sign_sig(const unsigned char *dgst, int dgstlen,
CryptDestroyHash(hash); const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec)
return 0; {
} ECDSA_SIG *ecsig = NULL;
if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len)) CAPI_DATA *cd = (CAPI_DATA *)EC_KEY_get_ex_data(ec, ec_data_idx);
ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC);
NCRYPT_KEY_HANDLE hkey = cd->crypt_prov;
BYTE buf[512]; /* large enough buffer for signature to avoid malloc */
DWORD len = _countof(buf);
msg(D_LOW, "Cryptoapi: signing hash using EC key: data size = %d", dgstlen);
DWORD status = NCryptSignHash(hkey, NULL, (BYTE *)dgst, dgstlen, (BYTE *)buf, len, &len, 0);
if (status != ERROR_SUCCESS)
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH); SetLastError(status);
CryptDestroyHash(hash); msg(M_NONFATAL|M_ERRNO, "Error in cryptoapticert: NCryptSignHash failed");
free(buf);
return 0;
} }
/* and now, we have to reverse the byte-order in the result from CryptSignHash()... */ else
for (i = 0; i < len; i++)
{ {
to[i] = buf[len - i - 1]; /* NCryptSignHash returns r, s concatenated in buf[] */
ecsig = ecdsa_bin2sig(buf, len);
} }
free(buf); return ecsig;
CryptDestroyHash(hash);
return len;
} }
/** /** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */
* Sign the hash in |m| and return the signature in |sig|.
* Returns 1 on success, 0 on error.
* NCryptSignHash() is used to sign and it is instructed to add the
* the PKCS #1 DigestInfo header to |m| unless the hash algorithm is
* the MD5/SHA1 combination used in TLS 1.1 and earlier versions.
* OpenSSL exercises this callback only when padding is PKCS1 v1.5.
*/
static int static int
rsa_sign_CNG(int type, const unsigned char *m, unsigned int m_len, ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig,
unsigned char *sig, unsigned int *siglen, const RSA *rsa) unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec)
{ {
CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(RSA_get_method(rsa)); ECDSA_SIG *s;
const wchar_t *alg = NULL;
int padding = RSA_PKCS1_PADDING;
*siglen = 0; *siglen = 0;
if (cd == NULL) s = ecdsa_sign_sig(dgst, dgstlen, NULL, NULL, ec);
if (s == NULL)
{ {
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
return 0; return 0;
} }
alg = cng_hash_algo(type); /* convert internal signature structure 's' to DER encoded byte array in sig */
if (alg && wcscmp(alg, L"UNKNOWN") == 0) int len = i2d_ECDSA_SIG(s, NULL);
if (len > ECDSA_size(ec))
{ {
RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE); ECDSA_SIG_free(s);
msg(M_NONFATAL,"Error in cryptoapicert: DER encoded ECDSA signature is too long (%d bytes)", len);
return 0; return 0;
} }
*siglen = i2d_ECDSA_SIG(s, &sig);
ECDSA_SIG_free(s);
*siglen = priv_enc_CNG(cd, alg, m, (int)m_len, sig, RSA_size(rsa), return 1;
cng_padding_type(padding), 0);
return (*siglen == 0) ? 0 : 1;
}
/* decrypt */
static int
rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
{
/* I haven't been able to trigger this one, but I want to know if it happens... */
assert(0);
return 0;
} }
/* called at RSA_new */
static int static int
init(RSA *rsa) ssl_ctx_set_eckey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
{ {
EC_KEY *ec = NULL;
EVP_PKEY *privkey = NULL;
return 0; /* create a method struct with default callbacks filled in */
} ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
if (!ec_method)
{
goto err;
}
/* called at RSA_free */ /* We only need to set finish among init methods, and sign methods */
static int EC_KEY_METHOD_set_init(ec_method, NULL, ec_finish, NULL, NULL, NULL, NULL);
finish(RSA *rsa) EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig);
{
const RSA_METHOD *rsa_meth = RSA_get_method(rsa);
CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(rsa_meth);
if (cd == NULL) ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey));
if (!ec)
{ {
return 0; goto err;
} }
if (cd->crypt_prov && cd->free_crypt_prov) if (!EC_KEY_set_method(ec, ec_method))
{ {
if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) goto err;
{ }
NCryptFreeObject(cd->crypt_prov);
} /* get an index to store cd as external data */
else if (ec_data_idx < 0)
{
ec_data_idx = EC_KEY_get_ex_new_index(0, "cryptapicert ec key", NULL, NULL, NULL);
if (ec_data_idx < 0)
{ {
CryptReleaseContext(cd->crypt_prov, 0); goto err;
} }
} }
if (cd->cert_context) EC_KEY_set_ex_data(ec, ec_data_idx, cd);
/* cd assigned to ec as ex_data, increase its refcount */
cd->ref_count++;
privkey = EVP_PKEY_new();
if (!EVP_PKEY_assign_EC_KEY(privkey, ec))
{ {
CertFreeCertificateContext(cd->cert_context); EC_KEY_free(ec);
goto err;
} }
free(cd); /* from here on ec will get freed with privkey */
RSA_meth_free((RSA_METHOD*) rsa_meth);
if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
{
goto err;
}
EVP_PKEY_free(privkey); /* this will dn_ref or free ec as well */
return 1; return 1;
err:
if (privkey)
{
EVP_PKEY_free(privkey);
}
else if (ec)
{
EC_KEY_free(ec);
}
if (ec_method) /* do always set ec_method = NULL after freeing it */
{
EC_KEY_METHOD_free(ec_method);
ec_method = NULL;
}
return 0;
} }
#endif /* !HAVE_XKEY_PROVIDER */
static const CERT_CONTEXT * static const CERT_CONTEXT *
find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
{ {
...@@ -570,7 +484,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) ...@@ -570,7 +484,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
} }
if (!*++p) /* unexpected end of string */ if (!*++p) /* unexpected end of string */
{ {
msg(M_WARN, "WARNING: cryptoapicert: error parsing <THUMB:%s>.", cert_prop); msg(M_WARN|M_INFO, "WARNING: cryptoapicert: error parsing <THUMB:%s>.", cert_prop);
goto out; goto out;
} }
if (*p >= '0' && *p <= '9') if (*p >= '0' && *p <= '9')
...@@ -595,11 +509,11 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) ...@@ -595,11 +509,11 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
} }
else else
{ {
msg(M_WARN, "WARNING: cryptoapicert: unsupported certificate specification <%s>", cert_prop); msg(M_NONFATAL, "Error in cryptoapicert: unsupported certificate specification <%s>", cert_prop);
goto out; goto out;
} }
while(true) while (true)
{ {
int validity = 1; int validity = 1;
/* this frees previous rv, if not NULL */ /* this frees previous rv, if not NULL */
...@@ -613,7 +527,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) ...@@ -613,7 +527,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
{ {
break; break;
} }
msg(M_WARN, "WARNING: cryptoapicert: ignoring certificate in store %s.", msg(M_WARN|M_INFO, "WARNING: cryptoapicert: ignoring certificate in store %s.",
validity < 0 ? "not yet valid" : "that has expired"); validity < 0 ? "not yet valid" : "that has expired");
} }
...@@ -622,7 +536,7 @@ out: ...@@ -622,7 +536,7 @@ out:
return rv; return rv;
} }
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) #ifndef HAVE_XKEY_PROVIDER
static const CAPI_DATA * static const CAPI_DATA *
retrieve_capi_data(EVP_PKEY *pkey) retrieve_capi_data(EVP_PKEY *pkey)
...@@ -643,6 +557,8 @@ retrieve_capi_data(EVP_PKEY *pkey) ...@@ -643,6 +557,8 @@ retrieve_capi_data(EVP_PKEY *pkey)
static int static int
pkey_rsa_sign_init(EVP_PKEY_CTX *ctx) pkey_rsa_sign_init(EVP_PKEY_CTX *ctx)
{ {
msg(D_LOW, "cryptoapicert: enter pkey_rsa_sign_init");
EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
if (pkey && retrieve_capi_data(pkey)) if (pkey && retrieve_capi_data(pkey))
...@@ -660,7 +576,7 @@ pkey_rsa_sign_init(EVP_PKEY_CTX *ctx) ...@@ -660,7 +576,7 @@ pkey_rsa_sign_init(EVP_PKEY_CTX *ctx)
* Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs| * Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs|
* and save the the signature in |sig| and its size in |*siglen|. * and save the the signature in |sig| and its size in |*siglen|.
* If |sig| is NULL the required buffer size is returned in |*siglen|. * If |sig| is NULL the required buffer size is returned in |*siglen|.
* Returns 1 on success, 0 or a negative integer on error. * Returns value is 1 on success, 0 or a negative integer on error.
*/ */
static int static int
pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
...@@ -671,9 +587,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, ...@@ -671,9 +587,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
EVP_MD *md = NULL; EVP_MD *md = NULL;
const wchar_t *alg = NULL; const wchar_t *alg = NULL;
int padding; int padding = 0;
int hashlen; int hashlen = 0;
int saltlen; int saltlen = 0;
pkey = EVP_PKEY_CTX_get0_pkey(ctx); pkey = EVP_PKEY_CTX_get0_pkey(ctx);
if (pkey) if (pkey)
...@@ -693,7 +609,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, ...@@ -693,7 +609,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
} }
else /* This should not happen */ else /* This should not happen */
{ {
msg(M_FATAL, "cryptopaicert: Unknown key and no default sign operation to fallback on"); msg(M_FATAL, "Error in cryptoapicert: Unknown key and no default sign operation to fallback on");
return -1; return -1;
} }
} }
...@@ -714,20 +630,19 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, ...@@ -714,20 +630,19 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
*/ */
if (alg && wcscmp(alg, L"UNKNOWN") == 0) if (alg && wcscmp(alg, L"UNKNOWN") == 0)
{ {
RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE); msg(M_NONFATAL, "Error in cryptoapicert: Unknown hash algorithm <%d>", EVP_MD_type(md));
return -1; return -1;
} }
} }
else else
{ {
msg(M_NONFATAL, "cryptoapicert: could not determine the signature digest algorithm"); msg(M_NONFATAL, "Error in cryptoapicert: could not determine the signature digest algorithm");
RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
return -1; return -1;
} }
if (tbslen != (size_t)hashlen) if (tbslen != (size_t)hashlen)
{ {
RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_INVALID_DIGEST_LENGTH); msg(M_NONFATAL, "Error in cryptoapicert: data size does not match hash");
return -1; return -1;
} }
...@@ -745,9 +660,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, ...@@ -745,9 +660,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
if (!EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) if (!EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md)
|| EVP_MD_type(mgf1md) != EVP_MD_type(md)) || EVP_MD_type(mgf1md) != EVP_MD_type(md))
{ {
msg(M_NONFATAL, "cryptoapicert: Unknown MGF1 digest type or does" msg(M_NONFATAL, "Error in cryptoapicert: Unknown MGF1 digest type or does"
" not match the signature digest type."); " not match the signature digest type.");
RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_UNSUPPORTED_MASK_PARAMETER); return -1;
} }
if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen)) if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen))
...@@ -778,32 +693,257 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, ...@@ -778,32 +693,257 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
if (saltlen < 0) if (saltlen < 0)
{ {
RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); msg(M_NONFATAL, "Error in cryptoapicert: invalid salt length (%d). Digest too large for keysize?", saltlen);
return -1; return -1;
} }
msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen); msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen);
} }
*siglen = priv_enc_CNG(cd, alg, tbs, (int)tbslen, sig, *siglen, msg(D_LOW, "cryptoapicert: calling priv_enc_CNG with alg = %ls", alg);
*siglen = priv_enc_CNG(cd, alg, tbs, (int)tbslen, sig, (int)*siglen,
cng_padding_type(padding), (DWORD)saltlen); cng_padding_type(padding), (DWORD)saltlen);
return (*siglen == 0) ? 0 : 1; return (*siglen == 0) ? 0 : 1;
} }
#endif /* OPENSSL_VERSION >= 1.1.0 */ static int
ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
{
RSA *rsa = NULL;
RSA_METHOD *my_rsa_method = NULL;
EVP_PKEY *privkey = NULL;
int ret = 0;
my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method",
RSA_METHOD_FLAG_NO_CHECK);
check_malloc_return(my_rsa_method);
RSA_meth_set_finish(my_rsa_method, rsa_finish); /* we use this callback to cleanup CAPI_DATA */
RSA_meth_set0_app_data(my_rsa_method, cd);
/* pmethod is global -- initialize only if NULL */
if (!pmethod)
{
pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
if (!pmethod)
{
msg(M_NONFATAL, "Error in cryptoapicert: failed to create EVP_PKEY_METHOD");
return 0;
}
const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA);
EVP_PKEY_meth_copy(pmethod, default_pmethod);
/* We want to override only sign_init() and sign() */
EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign);
EVP_PKEY_meth_add0(pmethod);
/* Keep a copy of the default sign and sign_init methods */
EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
&default_pkey_sign);
}
rsa = EVP_PKEY_get1_RSA(pkey);
RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
if (!RSA_set_method(rsa, my_rsa_method))
{
goto cleanup;
}
my_rsa_method = NULL; /* we do not want to free it in cleanup */
cd->ref_count++; /* with method, cd gets assigned to the key as well */
privkey = EVP_PKEY_new();
if (!EVP_PKEY_assign_RSA(privkey, rsa))
{
goto cleanup;
}
rsa = NULL; /* privkey has taken ownership */
if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey))
{
goto cleanup;
}
ret = 1;
cleanup:
if (rsa)
{
RSA_free(rsa);
}
if (my_rsa_method)
{
RSA_meth_free(my_rsa_method);
}
if (privkey)
{
EVP_PKEY_free(privkey);
}
return ret;
}
#else /* HAVE_XKEY_PROVIDER */
/** Sign hash in tbs using EC key in cd and NCryptSignHash */
static int
xkey_cng_ec_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
size_t tbslen)
{
BYTE buf[1024]; /* large enough for EC keys upto 1024 bits */
DWORD len = _countof(buf);
msg(D_LOW, "Signing using NCryptSignHash with EC key");
DWORD status = NCryptSignHash(cd->crypt_prov, NULL, (BYTE *)tbs, tbslen, buf, len, &len, 0);
if (status != ERROR_SUCCESS)
{
SetLastError(status);
msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: ECDSA signature using CNG failed.");
return 0;
}
/* NCryptSignHash returns r|s -- convert to OpenSSL's ECDSA_SIG */
ECDSA_SIG *ecsig = ecdsa_bin2sig(buf, len);
if (!ecsig)
{
msg(M_NONFATAL, "Error in cryptopicert: Failed to convert ECDSA signature");
return 0;
}
/* convert internal signature structure 's' to DER encoded byte array in sig */
if (i2d_ECDSA_SIG(ecsig, NULL) > EVP_PKEY_size(cd->pubkey))
{
ECDSA_SIG_free(ecsig);
msg(M_NONFATAL, "Error in cryptoapicert: DER encoded ECDSA signature is too long");
return 0;
}
*siglen = i2d_ECDSA_SIG(ecsig, &sig);
ECDSA_SIG_free(ecsig);
return (*siglen > 0);
}
/** Sign hash in tbs using RSA key in cd and NCryptSignHash */
static int
xkey_cng_rsa_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
size_t tbslen, XKEY_SIGALG sigalg)
{
dmsg(D_LOW, "In xkey_cng_rsa_sign");
ASSERT(cd);
ASSERT(sig);
ASSERT(tbs);
DWORD status = ERROR_SUCCESS;
DWORD len = 0;
const wchar_t *hashalg = cng_hash_algo(OBJ_sn2nid(sigalg.mdname));
if (hashalg && wcscmp(hashalg, L"UNKNOWN") == 0)
{
msg(M_NONFATAL, "Error in cryptoapicert: Unknown hash name <%s>", sigalg.mdname);
return 0;
}
if (!strcmp(sigalg.padmode, "pkcs1"))
{
msg(D_LOW, "Signing using NCryptSignHash with PKCS1 padding: hashalg <%s>", sigalg.mdname);
BCRYPT_PKCS1_PADDING_INFO padinfo = {hashalg};
status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD)tbslen,
sig, (DWORD)*siglen, &len, BCRYPT_PAD_PKCS1);
}
else if (!strcmp(sigalg.padmode, "pss"))
{
int saltlen = tbslen; /* digest size by default */
if (!strcmp(sigalg.saltlen, "max"))
{
saltlen = xkey_max_saltlen(EVP_PKEY_bits(cd->pubkey), tbslen);
if (saltlen < 0)
{
msg(M_NONFATAL, "Error in cryptoapicert: invalid salt length (%d)", saltlen);
return 0;
}
}
msg(D_LOW, "Signing using NCryptSignHash with PSS padding: hashalg <%s>, saltlen <%d>",
sigalg.mdname, saltlen);
BCRYPT_PSS_PADDING_INFO padinfo = {hashalg, (DWORD) saltlen}; /* cast is safe as saltlen >= 0 */
status = NCryptSignHash(cd->crypt_prov, &padinfo, (BYTE *)tbs, (DWORD) tbslen,
sig, (DWORD)*siglen, &len, BCRYPT_PAD_PSS);
}
else
{
msg(M_NONFATAL, "Error in cryptoapicert: Unsupported padding mode <%s>", sigalg.padmode);
return 0;
}
if (status != ERROR_SUCCESS)
{
SetLastError(status);
msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: RSA signature using CNG failed.");
return 0;
}
*siglen = len;
return (*siglen > 0);
}
/** Dispatch sign op to xkey_cng_<rsa/ec>_sign */
static int
xkey_cng_sign(void *handle, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
size_t tbslen, XKEY_SIGALG sigalg)
{
dmsg(D_LOW, "In xkey_cng_sign");
CAPI_DATA *cd = handle;
ASSERT(cd);
ASSERT(sig);
ASSERT(tbs);
unsigned char mdbuf[EVP_MAX_MD_SIZE];
size_t buflen = _countof(mdbuf);
/* compute digest if required */
if (!strcmp(sigalg.op, "DigestSign"))
{
if (!xkey_digest(tbs, tbslen, mdbuf, &buflen, sigalg.mdname))
{
return 0;
}
tbs = mdbuf;
tbslen = buflen;
}
if (!strcmp(sigalg.keytype, "EC"))
{
return xkey_cng_ec_sign(cd, sig, siglen, tbs, tbslen);
}
else if (!strcmp(sigalg.keytype, "RSA"))
{
return xkey_cng_rsa_sign(cd, sig, siglen, tbs, tbslen, sigalg);
}
else
{
return 0; /* Unknown keytype -- should not happen */
}
}
#endif /* HAVE_XKEY_PROVIDER */
int int
SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
{ {
HCERTSTORE cs; HCERTSTORE cs;
X509 *cert = NULL; X509 *cert = NULL;
RSA *rsa = NULL, *pub_rsa;
CAPI_DATA *cd = calloc(1, sizeof(*cd)); CAPI_DATA *cd = calloc(1, sizeof(*cd));
RSA_METHOD *my_rsa_method = NULL;
if (cd == NULL) if (cd == NULL)
{ {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); msg(M_NONFATAL, "Error in cryptoapicert: out of memory");
goto err; goto err;
} }
/* search CURRENT_USER first, then LOCAL_MACHINE */ /* search CURRENT_USER first, then LOCAL_MACHINE */
...@@ -811,7 +951,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) ...@@ -811,7 +951,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
|CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); |CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
if (cs == NULL) if (cs == NULL)
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: failed to open user certficate store");
goto err; goto err;
} }
cd->cert_context = find_certificate_in_store(cert_prop, cs); cd->cert_context = find_certificate_in_store(cert_prop, cs);
...@@ -822,14 +962,14 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) ...@@ -822,14 +962,14 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
|CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); |CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
if (cs == NULL) if (cs == NULL)
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: failed to open machine certficate store");
goto err; goto err;
} }
cd->cert_context = find_certificate_in_store(cert_prop, cs); cd->cert_context = find_certificate_in_store(cert_prop, cs);
CertCloseStore(cs, 0); CertCloseStore(cs, 0);
if (cd->cert_context == NULL) if (cd->cert_context == NULL)
{ {
CRYPTOAPIerr(CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE); msg(M_NONFATAL, "Error in cryptoapicert: certificate matching <%s> not found", cert_prop);
goto err; goto err;
} }
} }
...@@ -839,106 +979,20 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) ...@@ -839,106 +979,20 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
cd->cert_context->cbCertEncoded); cd->cert_context->cbCertEncoded);
if (cert == NULL) if (cert == NULL)
{ {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); msg(M_NONFATAL, "Error in cryptoapicert: X509 certificate decode failed");
goto err; goto err;
} }
/* set up stuff to use the private key */ /* set up stuff to use the private key */
/* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */ /* We support NCRYPT key handles only */
DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG
| CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG; | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL, if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL,
&cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
{ {
/* if we don't have a smart card reader here, and we try to access a /* private key may be in a token not available, or incompatible with CNG */
* smart card certificate, we get: msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: failed to acquire key. Key not present or "
* "Error 1223: The operation was canceled by the user." */ "is in a legacy token not supported by Windows CNG API");
CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY);
goto err;
}
/* here we don't need to do CryptGetUserKey() or anything; all necessary key
* info is in cd->cert_context, and then, in cd->crypt_prov. */
/* if we do not have an NCRYPT key handle restrict TLS to v1.1 or lower */
int max_version = SSL_CTX_get_max_proto_version(ssl_ctx);
if ((!max_version || max_version > TLS1_1_VERSION)
&& cd->key_spec != CERT_NCRYPT_KEY_SPEC)
{
msg(M_WARN, "WARNING: cryptoapicert: private key is in a legacy store."
" Restricting TLS version to 1.1");
if (SSL_CTX_get_min_proto_version(ssl_ctx) > TLS1_1_VERSION)
{
msg(M_NONFATAL,
"ERROR: cryptoapicert: min TLS version larger than 1.1."
" Try config option --tls-version-min 1.1");
goto err;
}
if (!SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION))
{
msg(M_NONFATAL, "ERROR: cryptoapicert: set max TLS version failed");
goto err;
}
}
my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method",
RSA_METHOD_FLAG_NO_CHECK);
check_malloc_return(my_rsa_method);
RSA_meth_set_pub_enc(my_rsa_method, rsa_pub_enc);
RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec);
RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc);
RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec);
RSA_meth_set_init(my_rsa_method, NULL);
RSA_meth_set_finish(my_rsa_method, finish);
RSA_meth_set0_app_data(my_rsa_method, cd);
/* For CNG, set the RSA_sign method which gets priority over priv_enc().
* This method is called with the raw hash without the digestinfo
* header and works better when using NCryptSignHash() with some tokens.
* However, if PSS padding is in use, openssl does not call this
* function but adds the padding and then calls rsa_priv_enc()
* with padding set to NONE which is not supported by CNG.
* So, when posisble (OpenSSL 1.1.0 and up), we hook on to the sign
* operation in EVP_PKEY_METHOD struct.
*/
if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
{
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG);
#else
/* pmethod is global -- initialize only if NULL */
if (!pmethod)
{
pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0);
if (!pmethod)
{
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
goto err;
}
const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA);
EVP_PKEY_meth_copy(pmethod, default_pmethod);
/* We want to override only sign_init() and sign() */
EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign);
EVP_PKEY_meth_add0(pmethod);
/* Keep a copy of the default sign and sign_init methods */
#if (OPENSSL_VERSION_NUMBER < 0x1010009fL) /* < version 1.1.0i */
/* The function signature is not const-correct in these versions */
EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD *)default_pmethod, &default_pkey_sign_init,
&default_pkey_sign);
#else
EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init,
&default_pkey_sign);
#endif
}
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */
}
rsa = RSA_new();
if (rsa == NULL)
{
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
goto err; goto err;
} }
...@@ -948,86 +1002,52 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) ...@@ -948,86 +1002,52 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
{ {
goto err; goto err;
} }
/* the public key */ /* the public key */
EVP_PKEY *pkey = X509_get0_pubkey(cert); EVP_PKEY *pkey = X509_get_pubkey(cert);
cd->pubkey = pkey; /* will be freed with cd */
/* SSL_CTX_use_certificate() increased the reference count in 'cert', so /* SSL_CTX_use_certificate() increased the reference count in 'cert', so
* we decrease it here with X509_free(), or it will never be cleaned up. */ * we decrease it here with X509_free(), or it will never be cleaned up. */
X509_free(cert); X509_free(cert);
cert = NULL; cert = NULL;
if (!(pub_rsa = EVP_PKEY_get0_RSA(pkey))) #ifdef HAVE_XKEY_PROVIDER
{
msg(M_WARN, "cryptoapicert requires an RSA certificate"); EVP_PKEY *privkey = xkey_load_generic_key(tls_libctx, cd, pkey,
goto err; xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free);
} SSL_CTX_use_PrivateKey(ssl_ctx, privkey);
return 1; /* do not free cd -- its kept by xkey provider */
/* Our private key is external, so we fill in only n and e from the public key */ #else /* ifdef HAVE_XKEY_PROVIDER */
const BIGNUM *n = NULL;
const BIGNUM *e = NULL; if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA)
RSA_get0_key(pub_rsa, &n, &e, NULL);
if (!RSA_set0_key(rsa, BN_dup(n), BN_dup(e), NULL))
{ {
goto err; if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey))
{
goto err;
}
} }
RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC)
if (!RSA_set_method(rsa, my_rsa_method))
{ {
goto err; if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey))
{
goto err;
}
} }
else
if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
{ {
msg(M_WARN|M_INFO, "WARNING: cryptoapicert: key type <%d> not supported",
EVP_PKEY_id(pkey));
goto err; goto err;
} }
/* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so CAPI_DATA_free(cd); /* this will do a ref_count-- */
* we decrease it here with RSA_free(), or it will never be cleaned up. */
RSA_free(rsa);
return 1; return 1;
#endif /* HAVE_XKEY_PROVIDER */
err: err:
if (cert) CAPI_DATA_free(cd);
{
X509_free(cert);
}
if (rsa)
{
RSA_free(rsa);
}
else
{
if (my_rsa_method)
{
free(my_rsa_method);
}
if (cd)
{
if (cd->free_crypt_prov && cd->crypt_prov)
{
if (cd->key_spec == CERT_NCRYPT_KEY_SPEC)
{
NCryptFreeObject(cd->crypt_prov);
}
else
{
CryptReleaseContext(cd->crypt_prov, 0);
}
}
if (cd->cert_context)
{
CertFreeCertificateContext(cd->cert_context);
}
free(cd);
}
}
return 0; return 0;
} }
#else /* ifdef ENABLE_CRYPTOAPI */
#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
static void
dummy(void)
{
}
#endif
#endif /* _WIN32 */ #endif /* _WIN32 */
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2021-2022 Arne Schwabe <arne@rfc2549.org>
* Copyright (C) 2021-2022 Antonio Quartulli <a@unstable.cc>
* Copyright (C) 2021-2022 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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 (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
#if defined(ENABLE_DCO)
#include "syshead.h"
#include "errlevel.h"
#include "networking.h"
#include "multi.h"
#include "ssl_verify.h"
#include "ssl_ncp.h"
#include "dco.h"
static bool
dco_multi_get_localaddr(struct multi_context *m, struct multi_instance *mi,
struct sockaddr_storage *local)
{
#if ENABLE_IP_PKTINFO
struct context *c = &mi->context;
if (!(c->options.sockflags & SF_USE_IP_PKTINFO))
{
return false;
}
struct link_socket_actual *actual = &c->c2.link_socket_info->lsa->actual;
switch (actual->dest.addr.sa.sa_family)
{
case AF_INET:
{
struct sockaddr_in *sock_in4 = (struct sockaddr_in *)local;
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
sock_in4->sin_addr = actual->pi.in4.ipi_addr;
#elif defined(IP_RECVDSTADDR)
sock_in4->sin_addr = actual->pi.in4;
#else
/* source IP not available on this platform */
return false;
#endif
sock_in4->sin_family = AF_INET;
break;
}
case AF_INET6:
{
struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)local;
sock_in6->sin6_addr = actual->pi.in6.ipi6_addr;
sock_in6->sin6_family = AF_INET6;
break;
}
default:
ASSERT(false);
}
return true;
#else /* if ENABLE_IP_PKTINFO */
return false;
#endif /* if ENABLE_IP_PKTINFO */
}
int
dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
{
struct context *c = &mi->context;
int peer_id = mi->context.c2.tls_multi->peer_id;
struct sockaddr *remoteaddr, *localaddr = NULL;
struct sockaddr_storage local = { 0 };
int sd = c->c2.link_socket->sd;
if (c->mode == CM_CHILD_TCP)
{
/* the remote address will be inferred from the TCP socket endpoint */
remoteaddr = NULL;
}
else
{
ASSERT(c->c2.link_socket_info->connection_established);
remoteaddr = &c->c2.link_socket_info->lsa->actual.dest.addr.sa;
}
struct in_addr remote_ip4 = { 0 };
struct in6_addr *remote_addr6 = NULL;
struct in_addr *remote_addr4 = NULL;
/* In server mode we need to fetch the remote addresses from the push config */
if (c->c2.push_ifconfig_defined)
{
remote_ip4.s_addr = htonl(c->c2.push_ifconfig_local);
remote_addr4 = &remote_ip4;
}
if (c->c2.push_ifconfig_ipv6_defined)
{
remote_addr6 = &c->c2.push_ifconfig_ipv6_local;
}
if (dco_multi_get_localaddr(m, mi, &local))
{
localaddr = (struct sockaddr *)&local;
}
int ret = dco_new_peer(&c->c1.tuntap->dco, peer_id, sd, localaddr,
remoteaddr, remote_addr4, remote_addr6);
if (ret < 0)
{
return ret;
}
c->c2.tls_multi->dco_peer_added = true;
if (c->mode == CM_CHILD_TCP)
{
multi_tcp_dereference_instance(m->mtcp, mi);
if (close(sd))
{
msg(D_DCO|M_ERRNO, "error closing TCP socket after DCO handover");
}
c->c2.link_socket->info.dco_installed = true;
c->c2.link_socket->sd = SOCKET_UNDEFINED;
}
return 0;
}
int
dco_p2p_add_new_peer(struct context *c)
{
if (!dco_enabled(&c->options))
{
return 0;
}
struct tls_multi *multi = c->c2.tls_multi;
struct link_socket *ls = c->c2.link_socket;
struct in6_addr remote_ip6 = { 0 };
struct in_addr remote_ip4 = { 0 };
struct in6_addr *remote_addr6 = NULL;
struct in_addr *remote_addr4 = NULL;
const char *gw = NULL;
ASSERT(ls->info.connection_established);
/* In client mode if a P2P style topology is used we assume the
* remote-gateway is the IP of the peer */
if (c->options.topology == TOP_NET30 || c->options.topology == TOP_P2P)
{
gw = c->options.ifconfig_remote_netmask;
}
if (c->options.route_default_gateway)
{
gw = c->options.route_default_gateway;
}
/* These inet_pton conversion are fatal since options.c already implements
* checks to have only valid addresses when setting the options */
if (c->options.ifconfig_ipv6_remote)
{
if (inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &remote_ip6) != 1)
{
msg(M_FATAL,
"DCO peer init: problem converting IPv6 ifconfig remote address %s to binary",
c->options.ifconfig_ipv6_remote);
}
remote_addr6 = &remote_ip6;
}
if (gw)
{
if (inet_pton(AF_INET, gw, &remote_ip4) != 1)
{
msg(M_FATAL, "DCO peer init: problem converting IPv4 ifconfig gateway address %s to binary", gw);
}
remote_addr4 = &remote_ip4;
}
else if (c->options.ifconfig_local)
{
msg(M_INFO, "DCO peer init: Need a peer VPN addresss to setup IPv4 (set --route-gateway)");
}
struct sockaddr *remoteaddr = &ls->info.lsa->actual.dest.addr.sa;
int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id,
c->c2.link_socket->sd, NULL, remoteaddr,
remote_addr4, remote_addr6);
if (ret < 0)
{
return ret;
}
c->c2.tls_multi->dco_peer_added = true;
c->c2.link_socket->info.dco_installed = true;
return 0;
}
void
dco_remove_peer(struct context *c)
{
if (!dco_enabled(&c->options))
{
return;
}
if (c->c1.tuntap && c->c2.tls_multi && c->c2.tls_multi->dco_peer_added)
{
dco_del_peer(&c->c1.tuntap->dco, c->c2.tls_multi->peer_id);
c->c2.tls_multi->dco_peer_added = false;
}
}
/**
* Find a usable key that is not the primary (i.e. the secondary key)
*
* @param multi The TLS struct to retrieve keys from
* @param primary The primary key that should be skipped during the scan
*
* @return The secondary key or NULL if none could be found
*/
static struct key_state *
dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary)
{
for (int i = 0; i < KEY_SCAN_SIZE; ++i)
{
struct key_state *ks = get_key_scan(multi, i);
struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
if (ks == primary)
{
continue;
}
if (ks->state >= S_GENERATED_KEYS && ks->authenticated == KS_AUTH_TRUE)
{
ASSERT(key->initialized);
return ks;
}
}
return NULL;
}
void
dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
{
msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
/* this function checks if keys have to be swapped or erased, therefore it
* can't do much if we don't have any key installed
*/
if (multi->dco_keys_installed == 0)
{
return;
}
struct key_state *primary = tls_select_encryption_key(multi);
ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
/* no primary key available -> no usable key exists, therefore we should
* tell DCO to simply wipe all keys
*/
if (!primary)
{
msg(D_DCO, "No encryption key found. Purging data channel keys");
dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
multi->dco_keys_installed = 0;
return;
}
struct key_state *secondary = dco_get_secondary_key(multi, primary);
ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
/* the current primary key was installed as secondary in DCO, this means
* that userspace has promoted it and we should tell DCO to swap keys
*/
if (primary->dco_status == DCO_INSTALLED_SECONDARY)
{
msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
primary->key_id, secondary ? secondary->key_id : -1);
dco_swap_keys(dco, multi->peer_id);
primary->dco_status = DCO_INSTALLED_PRIMARY;
if (secondary)
{
secondary->dco_status = DCO_INSTALLED_SECONDARY;
}
}
/* if we have no secondary key anymore, inform DCO about it */
if (!secondary && multi->dco_keys_installed == 2)
{
dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
multi->dco_keys_installed = 1;
}
/* all keys that are not installed are set to NOT installed */
for (int i = 0; i < KEY_SCAN_SIZE; ++i)
{
struct key_state *ks = get_key_scan(multi, i);
if (ks != primary && ks != secondary)
{
ks->dco_status = DCO_NOT_INSTALLED;
}
}
}
static int
dco_install_key(struct tls_multi *multi, struct key_state *ks,
const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
const char *ciphername)
{
msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d", __func__, multi->peer_id,
ks->key_id);
/* Install a key in the PRIMARY slot only when no other key exist.
* From that moment on, any new key will be installed in the SECONDARY
* slot and will be promoted to PRIMARY when userspace says so (a swap
* will be performed in that case)
*/
dco_key_slot_t slot = OVPN_KEY_SLOT_PRIMARY;
if (multi->dco_keys_installed > 0)
{
slot = OVPN_KEY_SLOT_SECONDARY;
}
int ret = dco_new_key(multi->dco, multi->peer_id, ks->key_id, slot,
encrypt_key, encrypt_iv,
decrypt_key, decrypt_iv,
ciphername);
if ((ret == 0) && (multi->dco_keys_installed < 2))
{
multi->dco_keys_installed++;
ks->dco_status = (slot == OVPN_KEY_SLOT_PRIMARY) ? DCO_INSTALLED_PRIMARY :
DCO_INSTALLED_SECONDARY;
}
return ret;
}
int
init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server)
{
struct key_direction_state kds;
key_direction_state_init(&kds, key_direction);
return dco_install_key(multi, ks,
key2->keys[kds.out_key].cipher,
key2->keys[(int)server].hmac,
key2->keys[kds.in_key].cipher,
key2->keys[1 - (int)server].hmac,
ciphername);
}
static bool
dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
{
if (ce->fragment)
{
msg(msglevel, "Note: --fragment disables data channel offload.");
return true;
}
if (ce->http_proxy_options)
{
msg(msglevel, "Note: --http-proxy disables data channel offload.");
return true;
}
if (ce->socks_proxy_server)
{
msg(msglevel, "Note: --socks-proxy disables data channel offload.");
return true;
}
return false;
}
static bool
dco_check_option_conflict_platform(int msglevel, const struct options *o)
{
#if defined(_WIN32)
if (o->mode == MODE_SERVER)
{
msg(msglevel, "Only client and p2p data channel offload is supported "
"with ovpn-dco-win.");
return true;
}
if (o->persist_tun)
{
msg(msglevel, "--persist-tun is not supported with ovpn-dco-win.");
return true;
}
#endif
return false;
}
bool
dco_check_option_conflict(int msglevel, const struct options *o)
{
if (o->tuntap_options.disable_dco)
{
/* already disabled by --disable-dco, no need to print warnings */
return true;
}
if (!dco_available(msglevel))
{
return true;
}
if (dco_check_option_conflict_platform(msglevel, o))
{
return true;
}
if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
{
msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
return true;
}
/* At this point the ciphers have already been normalised */
if (o->enable_ncp_fallback
&& !tls_item_in_cipher_list(o->ciphername, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
"disables data channel offload.", o->ciphername);
return true;
}
if (o->connection_list)
{
const struct connection_list *l = o->connection_list;
for (int i = 0; i < l->len; ++i)
{
if (dco_check_option_conflict_ce(l->array[i], msglevel))
{
return true;
}
}
}
else
{
if (dco_check_option_conflict_ce(&o->ce, msglevel))
{
return true;
}
}
if (o->mode == MODE_SERVER && o->topology != TOP_SUBNET)
{
msg(msglevel, "Note: NOT using '--topology subnet' disables data channel offload.");
return true;
}
#ifdef USE_COMP
if (o->comp.alg != COMP_ALG_UNDEF)
{
msg(msglevel, "Note: Using compression disables data channel offload.");
if (o->mode == MODE_SERVER && !(o->comp.flags & COMP_F_MIGRATE))
{
/* We can end up here from the multi.c call, only print the
* note if it is not already enabled */
msg(msglevel, "Consider using the '--compress migrate' option.");
}
return true;
}
#endif
struct gc_arena gc = gc_new();
char *tmp_ciphers = string_alloc(o->ncp_ciphers, &gc);
const char *token;
while ((token = strsep(&tmp_ciphers, ":")))
{
if (!tls_item_in_cipher_list(token, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
"by ovpn-dco, disabling data channel offload.", token);
gc_free(&gc);
return true;
}
}
gc_free(&gc);
return false;
}
/* These methods are currently Linux specific but likely to be used any
* platform that implements Server side DCO
*/
void
dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
struct mroute_addr *addr)
{
#if defined(TARGET_LINUX)
if (!dco_enabled(&m->top.options))
{
return;
}
int addrtype = (addr->type & MR_ADDR_MASK);
/* If we do not have local IP addr to install, skip the route */
if ((addrtype == MR_ADDR_IPV6 && !mi->context.c2.push_ifconfig_ipv6_defined)
|| (addrtype == MR_ADDR_IPV4 && !mi->context.c2.push_ifconfig_defined))
{
return;
}
struct context *c = &mi->context;
const char *dev = c->c1.tuntap->actual_name;
if (addrtype == MR_ADDR_IPV6)
{
int netbits = 128;
if (addr->type & MR_WITH_NETBITS)
{
netbits = addr->netbits;
}
net_route_v6_add(&m->top.net_ctx, &addr->v6.addr, netbits,
&mi->context.c2.push_ifconfig_ipv6_local, dev, 0,
DCO_IROUTE_METRIC);
}
else if (addrtype == MR_ADDR_IPV4)
{
int netbits = 32;
if (addr->type & MR_WITH_NETBITS)
{
netbits = addr->netbits;
}
in_addr_t dest = htonl(addr->v4.addr);
net_route_v4_add(&m->top.net_ctx, &dest, netbits,
&mi->context.c2.push_ifconfig_local, dev, 0,
DCO_IROUTE_METRIC);
}
#endif /* if defined(TARGET_LINUX) */
}
void
dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
{
#if defined(TARGET_LINUX)
if (!dco_enabled(&m->top.options))
{
return;
}
ASSERT(TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN);
struct context *c = &mi->context;
const char *dev = c->c1.tuntap->actual_name;
if (mi->context.c2.push_ifconfig_defined)
{
for (const struct iroute *ir = c->options.iroutes;
ir;
ir = ir->next)
{
net_route_v4_del(&m->top.net_ctx, &ir->network, ir->netbits,
&mi->context.c2.push_ifconfig_local, dev,
0, DCO_IROUTE_METRIC);
}
}
if (mi->context.c2.push_ifconfig_ipv6_defined)
{
for (const struct iroute_ipv6 *ir6 = c->options.iroutes_ipv6;
ir6;
ir6 = ir6->next)
{
net_route_v6_del(&m->top.net_ctx, &ir6->network, ir6->netbits,
&mi->context.c2.push_ifconfig_ipv6_local, dev,
0, DCO_IROUTE_METRIC);
}
}
#endif /* if defined(TARGET_LINUX) */
}
#endif /* defined(ENABLE_DCO) */
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2021-2022 Arne Schwabe <arne@rfc2549.org>
* Copyright (C) 2021-2022 Antonio Quartulli <a@unstable.cc>
* Copyright (C) 2021-2022 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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 (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DCO_H
#define DCO_H
#include "crypto.h"
#include "networking.h"
#include "dco_internal.h"
/* forward declarations, including multi.h leads to nasty include
* order problems */
struct context;
struct key_state;
struct multi_context;
struct multi_instance;
struct mroute_addr;
struct tls_multi;
struct tuntap;
struct event_set;
#define DCO_DEFAULT_METRIC 200
#if defined(ENABLE_DCO)
/**
* Check whether the options struct has any option that is not supported by
* our current dco implementation. If so print a warning at warning level
* for the first conflicting option found and return true.
*
* @param msglevel the msg level to use to print the warnings
* @param o the options struct that hold the options
* @return true if a conflict was detected, false otherwise
*/
bool dco_check_option_conflict(int msglevel, const struct options *o);
/**
* Initialize the DCO context
*
* @param mode the instance operating mode (P2P or multi-peer)
* @param dco the context to initialize
* @return true on success, false otherwise
*/
bool ovpn_dco_init(int mode, dco_context_t *dco);
/**
* Open/create a DCO interface
*
* @param tt the tuntap context
* @param ctx the networking API context
* @param dev the name of the interface to create
* @return 0 on success or a negative error code otherwise
*/
int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev);
/**
* Close/destroy a DCO interface
*
* @param tt the tuntap context
* @param ctx the networking API context
*/
void close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx);
/**
* Install a new peer in DCO - to be called by a SERVER instance
*
* @param m the server context
* @param mi the client instance
* @return 0 on success or a negative error code otherwise
*/
int dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi);
/**
* Install a new peer in DCO - to be called by a CLIENT (or P2P) instance
*
* @param c the main instance context
* @return 0 on success or a negative error code otherwise
*/
int dco_p2p_add_new_peer(struct context *c);
/**
* Remove a peer from DCO
*
* @param c the main instance context of the peer to remove
*/
void dco_remove_peer(struct context *c);
/**
* Read data from the DCO communication channel (i.e. a control packet)
*
* @param dco the DCO context
* @return 0 on success or a negative error code otherwise
*/
int dco_do_read(dco_context_t *dco);
/**
* Write data to the DCO communication channel (control packet expected)
*
* @param dco the DCO context
* @param peer_id the ID of the peer to send the data to
* @param buf the buffer containing the data to send
*/
int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
/**
* Install an iroute in DCO, which means adding a route to the system routing
* table. To be called by a SERVER instance only.
*
* @param m the server context
* @param mi the client instance acting as nexthop for the route
* @param addr the route to add
*/
void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
struct mroute_addr *addr);
/**
* Remove all routes added through the specified client
*
* @param m the server context
* @param mi the client instance for which routes have to be removed
*/
void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
/**
* Install the key material in DCO for the specified peer, at the specified slot
*
* @param multi the TLS context of the current instance
* @param ks the state of the key being installed
* @param key2 the container for the raw key material
* @param key_direction the key direction to be used to extract the material
* @param ciphername the name of the cipher to use the key with
* @param server whether we are running on a server instance or not
*
* @return 0 on success or a negative error code otherwise
*/
int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server);
/**
* Possibly swap or wipe keys from DCO
*
* @param dco DCO device context
* @param multi TLS multi instance
*/
void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
/**
* Check whether ovpn-dco is available on this platform (i.e. kernel support is
* there)
*
* @param msglevel level to print messages to
* @return true if ovpn-dco is available, false otherwise
*/
bool dco_available(int msglevel);
/**
* Return list of colon-separated ciphers supported by platform
*/
const char *dco_get_supported_ciphers();
/**
* Install a DCO in the main event loop
*/
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
/**
* Modify DCO peer options. Special values are 0 (disable)
* and -1 (do not touch).
*
* @param dco DCO device context
* @param peer_id the ID of the peer to be modified
* @param keepalive_interval keepalive interval in seconds
* @param keepalive_timeout keepalive timeout in seconds
* @param mss TCP MSS value
*
* @return 0 on success or a negative error code otherwise
*/
int dco_set_peer(dco_context_t *dco, unsigned int peerid,
int keepalive_interval, int keepalive_timeout, int mss);
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
static inline bool
dco_check_option_conflict(int msglevel, const struct options *o)
{
return true;
}
static inline bool
ovpn_dco_init(int mode, dco_context_t *dco)
{
return true;
}
static inline void
dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
struct mroute_addr *addr)
{
}
static inline void
dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
{
}
static inline int
open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
{
return 0;
}
static inline void
close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
}
static inline int
init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server)
{
ASSERT(false);
}
static inline void
dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
{
ASSERT(false);
}
static inline bool
dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
{
return true;
}
static inline bool
dco_p2p_add_new_peer(struct context *c)
{
return true;
}
static inline int
dco_set_peer(dco_context_t *dco, unsigned int peerid,
int keepalive_interval, int keepalive_timeout, int mss)
{
return 0;
}
static inline void
dco_remove_peer(struct context *c)
{
}
static inline int
dco_do_read(dco_context_t *dco)
{
ASSERT(false);
return 0;
}
static inline int
dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
{
ASSERT(false);
return 0;
}
static inline bool
dco_available(int msglevel)
{
return false;
}
static inline void
dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
{
}
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2022 Antonio Quartulli <a@unstable.cc>
* Copyright (C) 2022 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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 (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DCO_INTERNAL_H
#define DCO_INTERNAL_H
#if defined(ENABLE_DCO)
#include "dco_linux.h"
#include "dco_win.h"
/**
* This file contains the internal DCO API definition.
* It is expected that this file is included only in dco.h after including the
* platform specific DCO header (i.e. dco_linux.h or dco_win.h).
*
* The OpenVPN code should never directly include this file
*/
static inline dco_cipher_t
dco_get_cipher(const char *cipher)
{
if (strcmp(cipher, "AES-256-GCM") == 0 || strcmp(cipher, "AES-128-GCM") == 0
|| strcmp(cipher, "AES-192-GCM") == 0)
{
return OVPN_CIPHER_ALG_AES_GCM;
}
else if (strcmp(cipher, "CHACHA20-POLY1305") == 0)
{
return OVPN_CIPHER_ALG_CHACHA20_POLY1305;
}
else if (strcmp(cipher, "none") == 0)
{
return OVPN_CIPHER_ALG_NONE;
}
else
{
msg(M_FATAL, "DCO: provided unsupported cipher: %s", cipher);
}
}
/**
* The following are the DCO APIs used to control the driver.
* They are implemented by either dco_linux.c or dco_win.c
*/
int dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
struct sockaddr *localaddr, struct sockaddr *remoteaddr,
struct in_addr *remote_in4, struct in6_addr *remote_in6);
int dco_del_peer(dco_context_t *dco, unsigned int peerid);
int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
dco_key_slot_t slot,
const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
const char *ciphername);
int dco_del_key(dco_context_t *dco, unsigned int peerid,
dco_key_slot_t slot);
int dco_swap_keys(dco_context_t *dco, unsigned int peerid);
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_INTERNAL_H */