Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Lucas Kanashiro
openvpn
Compare Revisions
e385682719c2c6215022bcfa9ae0ddc6a593db5d...e1e1377bb8424326e05de1aebfd09e65942af9d9
Hide whitespace changes
Inline
Side-by-side
Too many changes to show.
To preserve performance only
9 of 249+
files are displayed.
src/openvpn/crypto_backend.h
View file @
e1e1377b
...
...
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-20
18
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-201
8
Fox Crypto B.V. <openvpn@fox
-it
.com>
* Copyright (C) 2002-20
22
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-20
2
1 Fox Crypto B.V. <openvpn@fox
crypto
.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
...
...
@@ -36,6 +36,7 @@
#include
"crypto_mbedtls.h"
#endif
#include
"basic.h"
#include
"buffer.h"
/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
#define OPENVPN_AEAD_TAG_LENGTH 16
...
...
@@ -50,7 +51,7 @@
typedef
enum
{
MD_SHA1
,
MD_SHA256
}
hash_algo_type
;
}
hash_algo_type
;
/** Struct used in cipher name translation table */
typedef
struct
{
...
...
@@ -77,6 +78,21 @@ void crypto_clear_error(void);
*/
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
/*
* OpenSSL memory debugging. If dmalloc debugging is enabled, tell
...
...
@@ -87,24 +103,40 @@ void crypto_init_dmalloc(void);
#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_digests
(
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
...
...
@@ -125,43 +157,6 @@ void show_available_engines(void);
*/
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
*
...
...
@@ -193,104 +188,136 @@ void cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
#define MAX_CIPHER_KEY_LENGTH 64
/**
* Return cipher parameters, based on the given cipher name. The
* contents of these parameters are library-specific, and can be used to
* initialise encryption/decryption.
* Returns if the cipher is valid, based on the given cipher name and provides a
* reason if invalid.
*
* @param ciphername Name of the cipher to retrieve parameters for (e.g.
* \c AES-128-CBC).
* @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 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
* for the given cipher, or NULL if no matching parameters
* were found.
* @return if the cipher is valid
*/
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.
*/
const
char
*
cipher_kt_name
(
const
c
ipher_kt_t
*
cipher
_kt
);
const
char
*
cipher_kt_name
(
const
c
har
*
cipher
name
);
/**
* Returns the size of keys used by the cipher, in bytes. If the cipher has a
* variable key size, return the default key size.
*
* @param cipher
_kt Static cipher parameters
* @param cipher
name Cipher name to lookup
*
* @return (Default) size of keys used by the cipher, in bytes.
*/
int
cipher_kt_key_size
(
const
c
ipher_kt_t
*
cipher
_kt
);
int
cipher_kt_key_size
(
const
c
har
*
cipher
name
);
/**
* Returns the size of the IV used by the cipher, in bytes, or 0 if no IV is
* used.
*
* @param cipher
_kt Static cipher parameters
* @param cipher
name cipher name to lookup
*
* @return Size of the IV, in bytes, or 0 if the cipher does not
* use an IV.
*/
int
cipher_kt_iv_size
(
const
c
ipher_kt_t
*
cipher
_kt
);
int
cipher_kt_iv_size
(
const
c
har
*
cipher
name
);
/**
* Returns the block size of the cipher, in bytes.
*
* @param cipher
_kt Static cipher parameters
* @param cipher
name cipher name
*
* @return Block size, in bytes.
*/
int
cipher_kt_block_size
(
const
c
ipher_kt_t
*
cipher
_kt
);
int
cipher_kt_block_size
(
const
c
har
*
cipher
name
);
/**
* Returns the MAC tag size of the cipher, in bytes.
*
* @param c
tx Static cipher parameters.
* @param c
iphername Name of the cipher
*
* @return Tag size in bytes, or 0 if the tag size could not be
* determined.
*/
int
cipher_kt_tag_size
(
const
c
ipher_kt_t
*
cipher
_kt
);
int
cipher_kt_tag_size
(
const
c
har
*
cipher
name
);
/**
* Returns the mode that the cipher runs in.
*
* @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
* Returns true if we consider this cipher to be insecure.
*/
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.
*
* @param cipher
Static cipher parameters.
* @param cipher
name cipher name
*
* @return true iff the cipher is a CBC mode cipher.
*/
bool
cipher_kt_mode_cbc
(
const
c
ipher_kt_t
*
cipher
);
bool
cipher_kt_mode_cbc
(
const
c
har
*
cipher
name
);
/**
* Check if the supplied cipher is a supported OFB or CFB mode cipher.
*
* @param cipher
Static cipher parameters.
* @param cipher
name cipher name
*
* @return true iff the cipher is a OFB or CFB mode cipher.
*/
bool
cipher_kt_mode_ofb_cfb
(
const
c
ipher_kt_t
*
cipher
);
bool
cipher_kt_mode_ofb_cfb
(
const
c
har
*
cipher
name
);
/**
* Check if the supplied cipher is a supported AEAD mode cipher.
*
* @param cipher
Static cipher parameters.
* @param cipher
name name of the cipher
*
* @return true iff the cipher is a AEAD mode cipher.
*/
bool
cipher_kt_mode_aead
(
const
c
ipher_kt_t
*
cipher
);
bool
cipher_kt_mode_aead
(
const
c
har
*
cipher
name
);
/**
...
...
@@ -318,13 +345,12 @@ void cipher_ctx_free(cipher_ctx_t *ctx);
*
* @param ctx Cipher context. May not be NULL
* @param key Buffer containing the key to use
* @param key_len Length of the key, in bytes
* @param kt Static cipher parameters to use
* @param ciphername Ciphername of the cipher to use
* @param enc Whether to encrypt or decrypt (either
* \c MBEDTLS_OP_ENCRYPT or \c MBEDTLS_OP_DECRYPT).
*/
void
cipher_ctx_init
(
cipher_ctx_t
*
ctx
,
const
uint8_t
*
key
,
int
key_len
,
const
c
ipher_kt_t
*
kt
,
int
enc
);
void
cipher_ctx_init
(
cipher_ctx_t
*
ctx
,
const
uint8_t
*
key
,
const
c
har
*
cipername
,
int
enc
);
/**
* 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,
* @param ctx The cipher's context
*
* @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
);
...
...
@@ -366,14 +392,31 @@ int cipher_ctx_block_size(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
* NULL if unable to determine cipher parameters.
* @return true iff the cipher is a AEAD mode cipher.
*/
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.
...
...
@@ -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.
*/
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
...
...
@@ -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
/**
* Return message digest parameters, based on the given digest name. The
* contents of these parameters are library-specific, and can be used to
* initialise HMAC or message digest operations.
* Checks if the cipher is defined and is not the null (none) cipher
*
* @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.
* \c MD5).
* @param digest Name of the digest to verify, e.g. \c MD5).
*
* @return A statically allocated structure containing parameters
* for the given message digest.
* @return Whether a digest of the given name is available
*/
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).
*
* @param
kt
Static m
essage digest
parameters
* @param
mdname
M
essage digest
name
*
* @return Statically allocated string describing the message
* 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.
*
* @param
kt
Static m
essage digest
parameters
* @param
mdname
M
essage digest
name
*
* @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);
*
*/
/*
/*
*
* 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_len The length of the incoming buffer.
* @param dst Buffer to write the message digest to. May not be NULL.
*
* @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
...
...
@@ -527,13 +579,13 @@ md_ctx_t *md_ctx_new(void);
*/
void
md_ctx_free
(
md_ctx_t
*
ctx
);
/*
/*
*
* Initialises the given message digest context.
*
* @param ctx Message digest context
* @param
kt
Static m
essage digest
parameters
* @param
mdname
M
essage 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.
...
...
@@ -593,14 +645,13 @@ void hmac_ctx_free(hmac_ctx_t *ctx);
* Initialises the given HMAC context, using the given digest
* and key.
*
* @param ctx HMAC context to intialise
* @param ctx HMAC context to in
i
tialise
* @param key The key to use for the HMAC
* @param key_len The key length to use
* @param kt Static message digest parameters
* @param mdname message digest name
*
*/
void
hmac_ctx_init
(
hmac_ctx_t
*
ctx
,
const
uint8_t
*
key
,
int
key_length
,
const
md_kt_t
*
kt
);
void
hmac_ctx_init
(
hmac_ctx_t
*
ctx
,
const
uint8_t
*
key
,
const
char
*
mdname
);
/*
* Free the given HMAC context.
...
...
@@ -616,7 +667,7 @@ void hmac_ctx_cleanup(hmac_ctx_t *ctx);
*
* @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
...
...
@@ -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
);
/**
* 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_ */
src/openvpn/crypto_mbedtls.c
View file @
e1e1377b
...
...
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-20
18
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-201
8
Fox Crypto B.V. <openvpn@fox
-it
.com>
* Copyright (C) 2002-20
22
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-20
2
1 Fox Crypto B.V. <openvpn@fox
crypto
.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
...
...
@@ -34,23 +34,26 @@
#include
"syshead.h"
#if
defined(ENABLE_CRYPTO) &&
defined(ENABLE_CRYPTO_MBEDTLS)
#if defined(ENABLE_CRYPTO_MBEDTLS)
#include
"errlevel.h"
#include
"basic.h"
#include
"buffer.h"
#include
"crypto.h"
#include
"integer.h"
#include
"crypto_backend.h"
#include
"otime.h"
#include
"misc.h"
#include
<mbedtls/base64.h>
#include
<mbedtls/des.h>
#include
<mbedtls/error.h>
#include
<mbedtls/md5.h>
#include
<mbedtls/cipher.h>
#include
<mbedtls/
havege
.h>
#include
<mbedtls/
pem
.h>
#include
<mbedtls/entropy.h>
#include
<mbedtls/ssl.h>
/*
...
...
@@ -66,6 +69,21 @@ crypto_init_lib_engine(const char *engine_name)
"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
...
...
@@ -138,26 +156,6 @@ const cipher_name_pair cipher_name_translation_table[] = {
const
size_t
cipher_name_translation_table_count
=
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
show_available_ciphers
(
void
)
{
...
...
@@ -166,16 +164,18 @@ show_available_ciphers(void)
#ifndef ENABLE_SMALL
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
"
"parameter to the --cipher option. Using a CBC or GCM mode is
\n
"
"recommended. In static key mode only CBC mode is allowed.
\n\n
"
);
"parameter to the --data-ciphers (or --cipher) option. Using a
\n
"
"GCM or CBC mode is recommended. In static key mode only CBC
\n
"
"mode is allowed.
\n\n
"
);
#endif
while
(
*
ciphers
!=
0
)
{
const
cipher_kt_t
*
info
=
mbedtls_cipher_info_from_type
(
*
ciphers
);
if
(
info
&&
cipher_kt_block_size
(
info
)
>=
128
/
8
)
const
mbedtls_cipher_info_t
*
info
=
mbedtls_cipher_info_from_type
(
*
ciphers
);
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
++
;
}
...
...
@@ -185,10 +185,11 @@ show_available_ciphers(void)
ciphers
=
mbedtls_cipher_list
();
while
(
*
ciphers
!=
0
)
{
const
cipher_kt_t
*
info
=
mbedtls_cipher_info_from_type
(
*
ciphers
);
if
(
info
&&
cipher_kt_block_size
(
info
)
<
128
/
8
)
const
mbedtls_cipher_info_t
*
info
=
mbedtls_cipher_info_from_type
(
*
ciphers
);
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
++
;
}
...
...
@@ -229,6 +230,84 @@ show_available_engines(void)
"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
...
...
@@ -310,104 +389,33 @@ rand_bytes(uint8_t *output, int len)
/*
*
*
Key functions, allow manipulation of keys.
*
Generic cipher key type functions
*
*/
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
)
static
const
mbedtls_cipher_info_t
*
cipher_get
(
const
char
*
ciphername
)
{
int
i
;
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
;
}
ASSERT
(
ciphername
);
void
key_des_fixup
(
uint8_t
*
key
,
int
key_len
,
int
ndc
)
{
int
i
;
struct
buffer
b
;
const
mbedtls_cipher_info_t
*
cipher
=
NULL
;
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: fixup_key_DES: insufficient key material"
);
return
;
}
mbedtls_des_key_set_parity
(
key
);
}
ciphername
=
translate_cipher_name_from_openvpn
(
ciphername
);
cipher
=
mbedtls_cipher_info_from_string
(
ciphername
);
return
cipher
;
}
/*
*
* Generic cipher key type functions
*
*/
const
mbedtls_cipher_info_t
*
cipher_kt_get
(
const
char
*
ciphername
)
bool
cipher_valid_reason
(
const
char
*
ciphername
,
const
char
**
reason
)
{
const
mbedtls_cipher_info_t
*
cipher
=
NULL
;
ASSERT
(
ciphername
);
ASSERT
(
reason
);
c
ipher
=
mbedtls_cipher_info_
from_string
(
ciphername
);
c
onst
mbedtls_cipher_info_
t
*
cipher
=
cipher_get
(
ciphername
);
if
(
NULL
==
cipher
)
{
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
)
...
...
@@ -415,15 +423,18 @@ cipher_kt_get(const char *ciphername)
msg
(
D_LOW
,
"Cipher algorithm '%s' uses a default key size (%d bytes) "
"which is larger than "
PACKAGE_NAME
"'s current maximum key size "
"(%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
*
cipher_kt_name
(
const
mbedtls_cipher_info_t
*
cipher
_kt
)
cipher_kt_name
(
const
char
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher_kt
=
cipher_get
(
ciphername
);
if
(
NULL
==
cipher_kt
)
{
return
"[null-cipher]"
;
...
...
@@ -433,8 +444,10 @@ cipher_kt_name(const mbedtls_cipher_info_t *cipher_kt)
}
int
cipher_kt_key_size
(
const
mbedtls_cipher_info_t
*
cipher
_kt
)
cipher_kt_key_size
(
const
char
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher_kt
=
cipher_get
(
ciphername
);
if
(
NULL
==
cipher_kt
)
{
return
0
;
...
...
@@ -444,8 +457,10 @@ cipher_kt_key_size(const mbedtls_cipher_info_t *cipher_kt)
}
int
cipher_kt_iv_size
(
const
mbedtls_cipher_info_t
*
cipher
_kt
)
cipher_kt_iv_size
(
const
char
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher_kt
=
cipher_get
(
ciphername
);
if
(
NULL
==
cipher_kt
)
{
return
0
;
...
...
@@ -454,8 +469,9 @@ cipher_kt_iv_size(const mbedtls_cipher_info_t *cipher_kt)
}
int
cipher_kt_block_size
(
const
mbedtls_cipher_info_t
*
cipher
_kt
)
cipher_kt_block_size
(
const
char
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher_kt
=
cipher_get
(
ciphername
);
if
(
NULL
==
cipher_kt
)
{
return
0
;
...
...
@@ -464,18 +480,32 @@ cipher_kt_block_size(const mbedtls_cipher_info_t *cipher_kt)
}
int
cipher_kt_tag_size
(
const
mbedtls_cipher_info_t
*
cipher
_kt
)
cipher_kt_tag_size
(
const
char
*
cipher
name
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
if
(
cipher_kt
&&
cipher_kt_mode_aead
(
cipher_kt
))
if
(
cipher_kt_mode_aead
(
ciphername
))
{
return
OPENVPN_AEAD_TAG_LENGTH
;
}
#endif
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
)
{
ASSERT
(
NULL
!=
cipher_kt
);
...
...
@@ -483,22 +513,29 @@ cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt)
}
bool
cipher_kt_mode_cbc
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_cbc
(
const
c
har
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher
=
cipher_get
(
ciphername
);
return
cipher
&&
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_CBC
;
}
bool
cipher_kt_mode_ofb_cfb
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_ofb_cfb
(
const
c
har
*
cipher
name
)
{
const
mbedtls_cipher_info_t
*
cipher
=
cipher_get
(
ciphername
);
return
cipher
&&
(
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_OFB
||
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_CFB
);
}
bool
cipher_kt_mode_aead
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_aead
(
const
c
har
*
cipher
name
)
{
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)
}
void
cipher_ctx_init
(
mbedtls_cipher_context_t
*
ctx
,
const
uint8_t
*
key
,
int
key_len
,
const
mbedtls_cipher_info_t
*
kt
,
const
mbedtls_operation_t
operation
)
cipher_ctx_init
(
mbedtls_cipher_context_t
*
ctx
,
const
uint8_t
*
key
,
const
char
*
ciphername
,
const
mbedtls_operation_t
operation
)
{
ASSERT
(
NULL
!=
kt
&&
NULL
!=
ctx
);
ASSERT
(
NULL
!=
ciphername
&&
NULL
!=
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
)))
{
msg
(
M_FATAL
,
"mbed TLS cipher context init #1"
);
...
...
@@ -554,7 +595,6 @@ cipher_ctx_iv_length(const mbedtls_cipher_context_t *ctx)
int
cipher_ctx_get_tag
(
cipher_ctx_t
*
ctx
,
uint8_t
*
tag
,
int
tag_len
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
if
(
tag_len
>
SIZE_MAX
)
{
return
0
;
...
...
@@ -566,9 +606,6 @@ cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len)
}
return
1
;
#else
/* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT
(
0
);
#endif
/* HAVE_AEAD_CIPHER_MODES */
}
int
...
...
@@ -585,14 +622,32 @@ cipher_ctx_mode(const mbedtls_cipher_context_t *ctx)
return
cipher_kt_mode
(
ctx
->
cipher_info
);
}
const
cipher_kt_t
*
cipher_ctx_get_cipher_kt
(
const
cipher_ctx_t
*
ctx
)
bool
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
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
)))
{
...
...
@@ -610,7 +665,6 @@ cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf)
int
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
)
{
return
0
;
...
...
@@ -622,9 +676,6 @@ cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len)
}
return
1
;
#else
/* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT
(
0
);
#endif
/* HAVE_AEAD_CIPHER_MODES */
}
int
...
...
@@ -663,7 +714,6 @@ int
cipher_ctx_final_check_tag
(
mbedtls_cipher_context_t
*
ctx
,
uint8_t
*
dst
,
int
*
dst_len
,
uint8_t
*
tag
,
size_t
tag_len
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
size_t
olen
=
0
;
if
(
MBEDTLS_DECRYPT
!=
ctx
->
operation
)
...
...
@@ -695,15 +745,12 @@ cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst,
}
return
1
;
#else
/* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT
(
0
);
#endif
/* HAVE_AEAD_CIPHER_MODES */
}
void
cipher_des_encrypt_ecb
(
const
unsigned
char
key
[
DES_KEY_LENGTH
],
unsigned
char
*
src
,
unsigned
char
*
dst
)
unsigned
char
src
[
DES_KEY_LENGTH
]
,
unsigned
char
dst
[
DES_KEY_LENGTH
]
)
{
mbedtls_des_context
ctx
;
...
...
@@ -720,8 +767,8 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
*/
const
mbedtls_md_info_t
*
md_
kt_
get
(
const
char
*
digest
)
static
const
mbedtls_md_info_t
*
md_get
(
const
char
*
digest
)
{
const
mbedtls_md_info_t
*
md
=
NULL
;
ASSERT
(
digest
);
...
...
@@ -741,19 +788,28 @@ md_kt_get(const char *digest)
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
*
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]"
;
}
const
mbedtls_md_info_t
*
kt
=
md_get
(
mdname
);
return
mbedtls_md_get_name
(
kt
);
}
int
md_kt_size
(
const
mbedtls_md_info_t
*
kt
)
unsigned
char
md_kt_size
(
const
char
*
mdname
)
{
const
mbedtls_md_info_t
*
kt
=
md_get
(
mdname
);
if
(
NULL
==
kt
)
{
return
0
;
...
...
@@ -768,8 +824,9 @@ md_kt_size(const mbedtls_md_info_t *kt)
*/
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
);
}
...
...
@@ -781,14 +838,16 @@ md_ctx_new(void)
return
ctx
;
}
void
md_ctx_free
(
mbedtls_md_context_t
*
ctx
)
void
md_ctx_free
(
mbedtls_md_context_t
*
ctx
)
{
free
(
ctx
);
}
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
);
mbedtls_md_init
(
ctx
);
...
...
@@ -852,12 +911,13 @@ hmac_ctx_free(mbedtls_md_context_t *ctx)
}
void
hmac_ctx_init
(
mbedtls_md_context_t
*
ctx
,
const
uint8_t
*
key
,
int
key_len
,
const
mbedtls_md_info_t
*
kt
)
hmac_ctx_init
(
mbedtls_md_context_t
*
ctx
,
const
uint8_t
*
key
,
const
char
*
mdname
)
{
const
mbedtls_md_info_t
*
kt
=
md_get
(
mdname
);
ASSERT
(
NULL
!=
kt
&&
NULL
!=
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_hmac_starts
(
ctx
,
key
,
key_len
));
...
...
@@ -872,7 +932,7 @@ hmac_ctx_cleanup(mbedtls_md_context_t *ctx)
}
int
hmac_ctx_size
(
const
mbedtls_md_context_t
*
ctx
)
hmac_ctx_size
(
mbedtls_md_context_t
*
ctx
)
{
if
(
NULL
==
ctx
)
{
...
...
@@ -899,4 +959,163 @@ hmac_ctx_final(mbedtls_md_context_t *ctx, uint8_t *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 */
src/openvpn/crypto_mbedtls.h
View file @
e1e1377b
...
...
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-20
18
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-201
8
Fox Crypto B.V. <openvpn@fox
-it
.com>
* Copyright (C) 2002-20
22
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-20
2
1 Fox Crypto B.V. <openvpn@fox
crypto
.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
...
...
@@ -33,9 +33,6 @@
#include
<mbedtls/md.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. */
typedef
mbedtls_md_info_t
md_kt_t
;
...
...
@@ -48,6 +45,9 @@ typedef mbedtls_md_context_t md_ctx_t;
/** Generic HMAC %context. */
typedef
mbedtls_md_context_t
hmac_ctx_t
;
/* Use a dummy type for the provider */
typedef
void
provider_t
;
/** Maximum length of an IV */
#define OPENVPN_MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH
...
...
@@ -146,5 +146,4 @@ mbed_log_func_line_lite(unsigned int flags, int errval,
#define mbed_ok(errval) \
mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__)
#endif
/* CRYPTO_MBEDTLS_H_ */
src/openvpn/crypto_openssl.c
View file @
e1e1377b
...
...
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-20
18
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-201
8
Fox Crypto B.V. <openvpn@fox
-it
.com>
* Copyright (C) 2002-20
22
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-20
2
1 Fox Crypto B.V. <openvpn@fox
crypto
.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
...
...
@@ -34,7 +34,7 @@
#include
"syshead.h"
#if
defined(ENABLE_CRYPTO) &&
defined(ENABLE_CRYPTO_OPENSSL)
#if defined(ENABLE_CRYPTO_OPENSSL)
#include
"basic.h"
#include
"buffer.h"
...
...
@@ -43,6 +43,7 @@
#include
"crypto_backend.h"
#include
"openssl_compat.h"
#include
<openssl/conf.h>
#include
<openssl/des.h>
#include
<openssl/err.h>
#include
<openssl/evp.h>
...
...
@@ -50,6 +51,22 @@
#include
<openssl/rand.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.
*/
...
...
@@ -63,6 +80,7 @@
#endif
#if HAVE_OPENSSL_ENGINE
#include
<openssl/ui.h>
#include
<openssl/engine.h>
static
bool
engine_initialized
=
false
;
/* GLOBAL */
...
...
@@ -139,6 +157,34 @@ crypto_init_lib_engine(const char *engine_name)
#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
...
...
@@ -148,6 +194,11 @@ crypto_init_lib_engine(const char *engine_name)
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
* CRYPTO_MDEBUG, you will get a listing of OpenSSL
...
...
@@ -188,7 +239,7 @@ crypto_clear_error(void)
void
crypto_print_openssl_errors
(
const
unsigned
int
flags
)
{
size_t
err
=
0
;
unsigned
long
err
=
0
;
while
((
err
=
ERR_get_error
()))
{
...
...
@@ -202,12 +253,12 @@ crypto_print_openssl_errors(const unsigned int flags)
else
if
(
ERR_GET_REASON
(
err
)
==
SSL_R_UNSUPPORTED_PROTOCOL
)
{
msg
(
D_CRYPT_ERRORS
,
"TLS error: Unsupported protocol. This typically "
"indicates that client and server have no common TLS version enabled. "
"This can be caused by mismatched tls-version-min and tls-version-max "
"options on client and server. "
"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+ "
"instead of TLS 1.0 only"
);
"indicates that client and server have no common TLS version enabled. "
"This can be caused by mismatched tls-version-min and tls-version-max "
"options on client and server. "
"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+ "
"instead of TLS 1.0 only"
);
}
msg
(
flags
,
"OpenSSL: %s"
,
ERR_error_string
(
err
,
NULL
));
}
...
...
@@ -254,6 +305,7 @@ const cipher_name_pair cipher_name_translation_table[] = {
{
"AES-128-GCM"
,
"id-aes128-GCM"
},
{
"AES-192-GCM"
,
"id-aes192-GCM"
},
{
"AES-256-GCM"
,
"id-aes256-GCM"
},
{
"CHACHA20-POLY1305"
,
"ChaCha20-Poly1305"
},
};
const
size_t
cipher_name_translation_table_count
=
sizeof
(
cipher_name_translation_table
)
/
sizeof
(
*
cipher_name_translation_table
);
...
...
@@ -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_b
=
b
;
const
char
*
cipher_name_a
=
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
);
return
strcmp
(
EVP_CIPHER_get0_name
(
*
cipher_a
),
EVP_CIPHER_get0_name
(
*
cipher_b
));
}
struct
collect_ciphers
{
/* If we ever exceed this, we must be more selective */
const
EVP_CIPHER
*
list
[
1000
];
size_t
num
;
};
static
void
prin
t_cipher
(
const
EVP_CIPHER
*
cipher
)
collec
t_cipher
s
(
EVP_CIPHER
*
cipher
,
void
*
list
)
{
const
char
*
var_key_size
=
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_VARIABLE_LENGTH
)
?
" by default"
:
""
;
const
char
*
ssl_only
=
cipher_kt_mode_cbc
(
cipher
)
?
""
:
", TLS client/server mode only"
;
if
(
!
cipher
)
{
return
;
}
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
"
,
translate_cipher_name_to_openvpn
(
EVP_CIPHER_name
(
cipher
)),
EVP_CIPHER_key_length
(
cipher
)
*
8
,
var_key_size
,
cipher_kt_block_size
(
cipher
)
*
8
,
ssl_only
);
if
(
ciphername
&&
(
cipher_kt_mode_cbc
(
ciphername
)
#ifdef ENABLE_OFB_CFB_MODE
||
cipher_kt_mode_ofb_cfb
(
ciphername
)
#endif
||
cipher_kt_mode_aead
(
ciphername
)
))
{
cipher_list
->
list
[
cipher_list
->
num
++
]
=
cipher
;
}
}
void
show_available_ciphers
(
void
)
{
int
nid
;
size_t
i
;
struct
collect_ciphers
cipher_list
=
{
0
};
/* If we ever exceed this, we must be more selective */
const
EVP_CIPHER
*
cipher_list
[
1000
];
size_t
num_ciphers
=
0
;
#ifndef ENABLE_SMALL
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
"
"parameter to the --cipher option. The default key size is
\n
"
"shown as well as whether or not it can be changed with the
\n
"
"--keysize directive. Using a CBC or GCM mode is recommended.
\n
"
"In static key mode only CBC mode is allowed.
\n\n
"
);
"with "
PACKAGE_NAME
". Each cipher shown below may be used as a
\n
"
"parameter to the --data-ciphers (or --cipher) option. In static
\n
"
"key mode only CBC mode is allowed.
\n
"
);
printf
(
"See also openssl list -cipher-algorithms
\n\n
"
);
#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
);
if
(
cipher
&&
(
cipher_kt_mode_cbc
(
cipher
)
#ifdef ENABLE_OFB_CFB_MODE
||
cipher_kt_mode_ofb_cfb
(
cipher
)
#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
;
}
/* We cast the const away so we can keep the function prototype
* compatible with EVP_CIPHER_do_all_provided */
collect_ciphers
((
EVP_CIPHER
*
)
cipher
,
&
cipher_list
);
}
#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
++
)
{
if
(
cipher_kt_block_size
(
cipher_list
[
i
])
>=
128
/
8
)
for
(
size_t
i
=
0
;
i
<
cipher_list
.
num
;
i
++
)
{
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
The 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
"
);
for
(
i
=
0
;
i
<
num_ciphers
;
i
++
)
{
if
(
cipher_kt_block_size
(
cipher_list
[
i
])
<
128
/
8
)
for
(
int
i
=
0
;
i
<
cipher_list
.
num
;
i
++
)
{
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
"
);
}
void
show_available_
digest
s
(
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
printf
(
"The following message digests are available for use with
\n
"
PACKAGE_NAME
". A message digest is used in conjunction with
\n
"
"the HMAC function, to authenticate received packets.
\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
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
);
if
(
digest
)
{
printf
(
"%s %d bit digest size
\n
"
,
OBJ_nid2sn
(
nid
),
EVP_MD_size
(
digest
)
*
8
);
/* We cast the const away so we can keep the function prototype
* compatible with EVP_MD_do_all_provided */
print_digest
((
EVP_MD
*
)
digest
,
NULL
);
}
}
#endif
printf
(
"
\n
"
);
}
...
...
@@ -396,110 +461,107 @@ show_available_engines(void)
#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
rand_bytes
(
uint8_t
*
output
,
int
len
)
bool
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"
)
;
return
0
;
ret
=
false
;
goto
cleanup
;
}
return
1
;
}
/*
*
* Key functions, allow manipulation of keys.
*
*/
BUF_MEM
*
bptr
;
BIO_get_mem_ptr
(
bio
,
&
bptr
);
*
dst
=
alloc_buf_gc
(
bptr
->
length
,
gc
);
ASSERT
(
buf_write
(
dst
,
bptr
->
data
,
bptr
->
length
));
int
key_des_num_cblocks
(
const
EVP_CIPHER
*
kt
)
{
int
ret
=
0
;
const
char
*
name
=
OBJ_nid2sn
(
EVP_CIPHER_nid
(
kt
));
if
(
name
)
ret
=
true
;
cleanup:
if
(
!
BIO_free
(
bio
))
{
if
(
!
strncmp
(
name
,
"DES-"
,
4
))
{
ret
=
EVP_CIPHER_key_length
(
kt
)
/
sizeof
(
DES_cblock
);
}
else
if
(
!
strncmp
(
name
,
"DESX-"
,
5
))
{
ret
=
1
;
}
ret
=
false
;
}
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
)
crypto_pem_decode
(
const
char
*
name
,
struct
buffer
*
dst
,
const
struct
buffer
*
src
)
{
int
i
;
struct
buffer
b
;
bool
ret
=
false
;
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
));
if
(
!
dc
)
{
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
;
}
dmsg
(
D_CRYPT_ERRORS
,
"%s: PEM decode failed"
,
__func__
);
goto
cleanup
;
}
return
true
;
err:
ERR_clear_error
();
return
false
;
}
if
(
strcmp
(
name
,
name_read
))
{
dmsg
(
D_CRYPT_ERRORS
,
"%s: unexpected PEM name (got '%s', expected '%s')"
,
__func__
,
name_read
,
name
);
goto
cleanup
;
}
void
key_des_fixup
(
uint8_t
*
key
,
int
key_len
,
int
ndc
)
{
int
i
;
struct
buffer
b
;
uint8_t
*
dst_data
=
buf_write_alloc
(
dst
,
data_read_len
);
if
(
!
dst_data
)
{
dmsg
(
D_CRYPT_ERRORS
,
"%s: dst too small (%i, needs %li)"
,
__func__
,
BCAP
(
dst
),
data_read_len
);
goto
cleanup
;
}
memcpy
(
dst_data
,
data_read
,
data_read_len
);
buf_set_read
(
&
b
,
key
,
key_len
);
for
(
i
=
0
;
i
<
ndc
;
++
i
)
ret
=
true
;
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
));
if
(
!
dc
)
{
msg
(
D_CRYPT_ERRORS
,
"CRYPTO INFO: fixup_key_DES: insufficient key material"
);
ERR_clear_error
();
return
;
}
DES_set_odd_parity
(
dc
);
ret
=
false
;
}
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)
*
*/
const
EVP_CIPHER
*
cipher_kt_get
(
const
char
*
ciphername
)
static
evp_cipher_type
*
cipher_get
(
const
char
*
ciphername
)
{
const
EVP_CIPHER
*
cipher
=
NULL
;
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
);
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
)
{
msg
(
D_LOW
,
"Cipher algorithm '%s' uses a default key size (%d bytes) "
"which is larger than "
PACKAGE_NAME
"'s current maximum key size "
"(%d bytes)"
,
ciphername
,
EVP_CIPHER_key_length
(
cipher
),
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
*
cipher_kt_name
(
const
EVP_CIPHER
*
cipher
_kt
)
cipher_kt_name
(
const
char
*
cipher
name
)
{
if
(
NULL
==
cipher_kt
)
ASSERT
(
ciphername
);
if
(
strcmp
(
"none"
,
ciphername
)
==
0
)
{
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
cipher_kt_key_size
(
const
EVP_CIPHER
*
cipher
_kt
)
cipher_kt_key_size
(
const
char
*
cipher
name
)
{
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
cipher_kt_iv_size
(
const
EVP_CIPHER
*
cipher
_kt
)
cipher_kt_iv_size
(
const
char
*
cipher
name
)
{
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
cipher_kt_block_size
(
const
EVP_CIPHER
*
cipher
)
cipher_kt_block_size
(
const
char
*
cipher
name
)
{
/*
* 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)
char
*
name
=
NULL
;
char
*
mode_str
=
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
);
orig_name
=
cipher_kt
_name
(
cipher
);
orig_name
=
EVP_CIPHER
_name
(
cipher
);
if
(
!
orig_name
)
{
goto
cleanup
;
...
...
@@ -589,21 +693,23 @@ cipher_kt_block_size(const EVP_CIPHER *cipher)
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
)
{
block_size
=
EVP_CIPHER_block_size
(
cbc_cipher
);
}
cleanup:
EVP_CIPHER_free
(
cbc_cipher
);
EVP_CIPHER_free
(
cipher
);
free
(
name
);
return
block_size
;
}
int
cipher_kt_tag_size
(
const
EVP_CIPHER
*
cipher
_kt
)
cipher_kt_tag_size
(
const
char
*
cipher
name
)
{
if
(
cipher_kt_mode_aead
(
cipher
_kt
))
if
(
cipher_kt_mode_aead
(
cipher
name
))
{
return
OPENVPN_AEAD_TAG_LENGTH
;
}
...
...
@@ -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
cipher_kt_mode
(
const
EVP_CIPHER
*
cipher_kt
)
{
...
...
@@ -621,36 +750,56 @@ cipher_kt_mode(const EVP_CIPHER *cipher_kt)
}
bool
cipher_kt_mode_cbc
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_cbc
(
const
c
har
*
cipher
name
)
{
return
cipher
&&
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_CBC
#ifdef EVP_CIPH_FLAG_AEAD_CIPHER
/* Exclude AEAD cipher modes, they require a different API */
&&
!
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_FLAG_AEAD_CIPHER
)
evp_cipher_type
*
cipher
=
cipher_get
(
ciphername
);
bool
ret
=
cipher
&&
(
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_CBC
/* Exclude AEAD cipher modes, they require a different API */
#ifdef EVP_CIPH_FLAG_CTS
&&
!
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_FLAG_CTS
)
#endif
;
&&
!
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_FLAG_AEAD_CIPHER
));
EVP_CIPHER_free
(
cipher
);
return
ret
;
}
bool
cipher_kt_mode_ofb_cfb
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_ofb_cfb
(
const
c
har
*
cipher
name
)
{
return
cipher
&&
(
cipher_
kt_mode
(
cipher
)
==
OPENVPN_MODE_OFB
||
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_
C
FB
)
#ifdef EVP_CIPH_FLAG_AEAD_CIPHER
/* Exclude AEAD cipher modes, they require a different API */
&&
!
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_FLAG_AEAD_CIPHER
)
#endif
;
evp_cipher_type
*
cipher
=
cipher_
get
(
ciphername
);
bool
ofb_cfb
=
cipher
&&
(
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_
O
FB
||
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_CFB
)
/* Exclude AEAD cipher modes, they require a different API */
&&
!
(
EVP_CIPHER_flags
(
cipher
)
&
EVP_CIPH_FLAG_AEAD_CIPHER
)
;
EVP_CIPHER_free
(
cipher
);
return
ofb_cfb
;
}
bool
cipher_kt_mode_aead
(
const
c
ipher_kt_t
*
cipher
)
cipher_kt_mode_aead
(
const
c
har
*
cipher
name
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
return
cipher
&&
(
cipher_kt_mode
(
cipher
)
==
OPENVPN_MODE_GCM
);
#else
return
false
;
bool
isaead
=
false
;
evp_cipher_type
*
cipher
=
cipher_get
(
ciphername
);
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
}
EVP_CIPHER_free
(
cipher
);
return
isaead
;
}
/*
...
...
@@ -674,29 +823,25 @@ cipher_ctx_free(EVP_CIPHER_CTX *ctx)
}
void
cipher_ctx_init
(
EVP_CIPHER_CTX
*
ctx
,
const
uint8_t
*
key
,
int
key_len
,
const
EVP_CIPHER
*
kt
,
int
enc
)
cipher_ctx_init
(
EVP_CIPHER_CTX
*
ctx
,
const
uint8_t
*
key
,
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
);
if
(
!
EVP_CipherInit
(
ctx
,
kt
,
NULL
,
NULL
,
enc
))
{
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
))
{
crypto_msg
(
M_FATAL
,
"EVP cipher init #2"
);
}
EVP_CIPHER_free
(
kt
);
/* 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
...
...
@@ -708,11 +853,7 @@ cipher_ctx_iv_length(const EVP_CIPHER_CTX *ctx)
int
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_GCM_GET_TAG
,
tag_size
,
tag_buf
);
#else
ASSERT
(
0
);
#endif
return
EVP_CIPHER_CTX_ctrl
(
ctx
,
EVP_CTRL_AEAD_GET_TAG
,
tag_size
,
tag_buf
);
}
int
...
...
@@ -727,15 +868,66 @@ cipher_ctx_mode(const EVP_CIPHER_CTX *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
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
);
}
...
...
@@ -743,16 +935,12 @@ cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf)
int
cipher_ctx_update_ad
(
EVP_CIPHER_CTX
*
ctx
,
const
uint8_t
*
src
,
int
src_len
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
int
len
;
if
(
!
EVP_CipherUpdate
(
ctx
,
NULL
,
&
len
,
src
,
src_len
))
{
crypto_msg
(
M_FATAL
,
"%s: EVP_CipherUpdate() failed"
,
__func__
);
}
return
1
;
#else
/* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT
(
0
);
#endif
}
int
...
...
@@ -776,28 +964,58 @@ int
cipher_ctx_final_check_tag
(
EVP_CIPHER_CTX
*
ctx
,
uint8_t
*
dst
,
int
*
dst_len
,
uint8_t
*
tag
,
size_t
tag_len
)
{
#ifdef HAVE_AEAD_CIPHER_MODES
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
cipher_ctx_final
(
ctx
,
dst
,
dst_len
);
#else
/* ifdef HAVE_AEAD_CIPHER_MODES */
ASSERT
(
0
);
#endif
}
void
cipher_des_encrypt_ecb
(
const
unsigned
char
key
[
DES_KEY_LENGTH
],
unsigned
char
*
src
,
unsigned
char
*
dst
)
unsigned
char
src
[
DES_KEY_LENGTH
]
,
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
);
DES_ecb_encrypt
((
DES_cblock
*
)
src
,
(
DES_cblock
*
)
dst
,
&
sched
,
DES_ENCRYPT
);
EVP_CIPHER_CTX_free
(
ctx
);
}
/*
...
...
@@ -807,12 +1025,12 @@ cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
*/
const
EVP_MD
*
md_
kt_
get
(
const
char
*
digest
)
static
evp_md_type
*
md_get
(
const
char
*
digest
)
{
const
EVP_MD
*
md
=
NULL
;
evp_md_type
*
md
=
NULL
;
ASSERT
(
digest
);
md
=
EVP_
get_digestbyname
(
digest
);
md
=
EVP_
MD_fetch
(
NULL
,
digest
,
NULL
);
if
(
!
md
)
{
crypto_msg
(
M_FATAL
,
"Message hash algorithm '%s' not found"
,
digest
);
...
...
@@ -827,20 +1045,40 @@ md_kt_get(const char *digest)
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
*
md_kt_name
(
const
EVP_MD
*
kt
)
md_kt_name
(
const
char
*
mdname
)
{
if
(
NULL
==
kt
)
if
(
!
strcmp
(
"none"
,
mdname
)
)
{
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
md_kt_size
(
const
EVP_MD
*
kt
)
unsigned
char
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)
*/
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
;
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
*
...
...
@@ -866,18 +1107,24 @@ md_ctx_new(void)
return
ctx
;
}
void
md_ctx_free
(
EVP_MD_CTX
*
ctx
)
void
md_ctx_free
(
EVP_MD_CTX
*
ctx
)
{
EVP_MD_CTX_free
(
ctx
);
}
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
);
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
...
...
@@ -912,7 +1159,7 @@ md_ctx_final(EVP_MD_CTX *ctx, uint8_t *dst)
* Generic HMAC functions
*
*/
#if OPENSSL_VERSION_NUMBER < 0x30000000L
HMAC_CTX
*
hmac_ctx_new
(
void
)
{
...
...
@@ -928,13 +1175,17 @@ hmac_ctx_free(HMAC_CTX *ctx)
}
void
hmac_ctx_init
(
HMAC_CTX
*
ctx
,
const
uint8_t
*
key
,
int
key_len
,
const
EVP_MD
*
kt
)
hmac_ctx_init
(
HMAC_CTX
*
ctx
,
const
uint8_t
*
key
,
const
char
*
mdname
)
{
evp_md_type
*
kt
=
md_get
(
mdname
);
ASSERT
(
NULL
!=
kt
&&
NULL
!=
ctx
);
int
key_len
=
EVP_MD_size
(
kt
);
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 */
ASSERT
(
HMAC_size
(
ctx
)
<=
key_len
);
...
...
@@ -947,7 +1198,7 @@ hmac_ctx_cleanup(HMAC_CTX *ctx)
}
int
hmac_ctx_size
(
const
HMAC_CTX
*
ctx
)
hmac_ctx_size
(
HMAC_CTX
*
ctx
)
{
return
HMAC_size
(
ctx
);
}
...
...
@@ -955,7 +1206,10 @@ hmac_ctx_size(const HMAC_CTX *ctx)
void
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
...
...
@@ -971,5 +1225,390 @@ hmac_ctx_final(HMAC_CTX *ctx, uint8_t *dst)
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 */
src/openvpn/crypto_openssl.h
View file @
e1e1377b
...
...
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-20
18
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-201
8
Fox Crypto B.V. <openvpn@fox
-it
.com>
* Copyright (C) 2002-20
22
OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-20
2
1 Fox Crypto B.V. <openvpn@fox
crypto
.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
...
...
@@ -33,12 +33,9 @@
#include
<openssl/hmac.h>
#include
<openssl/md5.h>
#include
<openssl/sha.h>
/** Generic cipher key type %context. */
typedef
EVP_CIPHER
cipher_kt_t
;
/** Generic message digest key type %context. */
typedef
EVP_MD
md_kt_t
;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include
<openssl/provider.h>
#endif
/** Generic cipher %context. */
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
;
/** Generic HMAC %context. */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
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 */
#define OPENVPN_MAX_IV_LENGTH EVP_MAX_IV_LENGTH
...
...
@@ -61,13 +82,9 @@ typedef HMAC_CTX hmac_ctx_t;
/** Cipher is in CFB mode */
#define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE
#ifdef HAVE_AEAD_CIPHER_MODES
/** Cipher is in GCM mode */
#define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE
#endif
/* HAVE_AEAD_CIPHER_MODES */
/** Cipher should encrypt */
#define OPENVPN_OP_ENCRYPT 1
...
...
@@ -101,5 +118,16 @@ void crypto_print_openssl_errors(const unsigned int flags);
msg((flags), __VA_ARGS__); \
} 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_ */
src/openvpn/cryptoapi.c
View file @
e1e1377b
/*
* Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>
* Copyright (c) 2018 Selva Nair <selva.nair@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifi-
...
...
@@ -51,103 +52,31 @@
#include
"buffer.h"
#include
"openssl_compat.h"
#include
"win32.h"
#include
"xkey_common.h"
/* MinGW w32api 3.17 is still incomplete when it comes to CryptoAPI while
* MinGW32-w64 defines all macros used. This is a hack around that problem.
*/
#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
}
};
#ifndef HAVE_XKEY_PROVIDER
/* index for storing external data in EC_KEY: < 0 means uninitialized */
static
int
ec_data_idx
=
-
1
;
/* Global EVP_PKEY_METHOD used to override the sign operation */
static
EVP_PKEY_METHOD
*
pmethod
;
static
int
(
*
default_pkey_sign_init
)
(
EVP_PKEY_CTX
*
ctx
);
static
int
(
*
default_pkey_sign
)
(
EVP_PKEY_CTX
*
ctx
,
unsigned
char
*
sig
,
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
{
const
CERT_CONTEXT
*
cert_context
;
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE
crypt_prov
;
EVP_PKEY
*
pubkey
;
DWORD
key_spec
;
BOOL
free_crypt_prov
;
int
ref_count
;
}
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
* "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1
* mixed hash used in TLS 1.1 and earlier.
...
...
@@ -190,116 +119,69 @@ cng_hash_algo(int md_type)
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
err_put_ms_error
(
DWORD
ms_err
,
int
func
,
const
char
*
file
,
int
line
)
CAPI_DATA_free
(
CAPI_DATA
*
cd
)
{
static
int
init
=
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
)
if
(
!
cd
||
cd
->
ref_count
--
>
0
)
{
/* 0 is not an error */
return
;
}
if
(
!
init
)
if
(
cd
->
free_crypt_prov
&&
cd
->
crypt_prov
)
{
ERR_load_strings
(
ERR_LIB_CRYPTOAPI
,
CRYPTOAPI_str_functs
);
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
)
if
(
cd
->
key_spec
==
CERT_NCRYPT_KEY_SPEC
)
{
ERR_PUT_error
(
ERR_LIB_CRYPTOAPI
,
func
,
err_map
[
i
].
err
,
file
,
line
);
break
;
NCryptFreeObject
(
cd
->
crypt_prov
);
}
else
if
(
err_map
[
i
].
ms_err
==
0
)
else
{
/* end of table, add new entry */
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
;
CryptReleaseContext
(
cd
->
crypt_prov
,
0
);
}
}
if
(
cd
->
cert_context
)
{
CertFreeCertificateContext
(
cd
->
cert_context
);
}
EVP_PKEY_free
(
cd
->
pubkey
);
/* passing NULL is okay */
free
(
cd
);
}
/* encrypt */
static
int
rsa_pub_enc
(
int
flen
,
const
unsigned
char
*
from
,
unsigned
char
*
to
,
RSA
*
rsa
,
int
padding
)
#ifndef HAVE_XKEY_PROVIDER
/* 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... */
assert
(
0
);
DWORD
pad
=
0
;
return
0
;
}
switch
(
padding
)
{
case
RSA_NO_PADDING
:
break
;
/* verify arbitrary data */
static
int
rsa_pub_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
);
case
RSA_PKCS1_PADDING
:
pad
=
BCRYPT_PAD_PKCS1
;
break
;
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
* key handle in cd->crypt_prov. On return the signature is in 'to'. Returns
* 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.
* 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
...
...
@@ -333,14 +215,14 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char
}
else
{
RSAerr
(
RSA_F_RSA_OSSL_PRIVATE_ENCRYPT
,
RSA_R_UNKNOWN_PADDING_TYPE
);
msg
(
M_NONFATAL
,
"Error in cryptoapicert: Unknown padding type"
);
return
0
;
}
if
(
status
!=
ERROR_SUCCESS
)
{
SetLastError
(
status
);
CRYPTOAPIerr
(
CRYPTOAPI_F_NCRYPT_SIGN_HASH
);
msg
(
M_NONFATAL
|
M_ERRNO
,
"Error in cryptoapicert: NCryptSignHash failed"
);
len
=
0
;
}
...
...
@@ -348,180 +230,212 @@ priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char
return
len
;
}
/*
sign arbitrary data
*/
/*
called at RSA_free
*/
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
));
HCRYPTHASH
hash
;
DWORD
hash_size
,
len
,
i
;
unsigned
char
*
buf
;
const
RSA_METHOD
*
rsa_meth
=
RSA_get_method
(
rsa
);
CAPI_DATA
*
cd
=
(
CAPI_DATA
*
)
RSA_meth_get0_app_data
(
rsa_meth
);
if
(
cd
==
NULL
)
{
RSAerr
(
RSA_F_RSA_OSSL_PRIVATE_ENCRYPT
,
ERR_R_PASSED_NULL_PARAMETER
);
return
0
;
}
CAPI_DATA_free
(
cd
);
RSA_meth_free
((
RSA_METHOD
*
)
rsa_meth
);
return
1
;
}
if
(
cd
->
key_spec
==
CERT_NCRYPT_KEY_SPEC
)
{
return
priv_enc_CNG
(
cd
,
NULL
,
from
,
flen
,
to
,
RSA_size
(
rsa
),
cng_padding_type
(
padding
),
0
);
}
static
EC_KEY_METHOD
*
ec_method
=
NULL
;
if
(
padding
!=
RSA_PKCS1_PADDING
)
{
/* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */
RSAerr
(
RSA_F_RSA_OSSL_PRIVATE_ENCRYPT
,
RSA_R_UNKNOWN_PADDING_TYPE
);
return
0
;
}
/** EC_KEY_METHOD callback: called when the key is freed */
static
void
ec_finish
(
EC_KEY
*
ec
)
{
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
* be way to straightforward for M$, I guess... So we have to do it this
* tricky way instead, by creating a "Hash", and load the already-made hash
* from 'from' into it. */
/* For now, we only support NID_md5_sha1 */
if
(
flen
!=
SSL_SIG_LENGTH
)
{
RSAerr
(
RSA_F_RSA_OSSL_PRIVATE_ENCRYPT
,
RSA_R_INVALID_MESSAGE_LENGTH
);
return
0
;
}
if
(
!
CryptCreateHash
(
cd
->
crypt_prov
,
CALG_SSL3_SHAMD5
,
0
,
0
,
&
hash
))
{
CRYPTOAPIerr
(
CRYPTOAPI_F_CRYPT_CREATE_HASH
);
return
0
;
}
len
=
sizeof
(
hash_size
);
if
(
!
CryptGetHashParam
(
hash
,
HP_HASHSIZE
,
(
BYTE
*
)
&
hash_size
,
&
len
,
0
))
/** EC_KEY_METHOD callback sign_setup(): we do nothing here */
static
int
ecdsa_sign_setup
(
EC_KEY
*
eckey
,
BN_CTX
*
ctx_in
,
BIGNUM
**
kinvp
,
BIGNUM
**
rp
)
{
return
1
;
}
#endif
/* HAVE_XKEY_PROVIDER */
/**
* Helper to convert ECDSA signature returned by NCryptSignHash
* to an ECDSA_SIG structure.
* On entry 'buf[]' of length len contains r and s concatenated.
* Returns a newly allocated ECDSA_SIG or NULL (on error).
*/
static
ECDSA_SIG
*
ecdsa_bin2sig
(
unsigned
char
*
buf
,
int
len
)
{
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
);
CryptDestroyHash
(
hash
);
return
0
;
goto
err
;
}
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
);
CryptDestroyHash
(
hash
);
return
0
;
goto
err
;
}
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
);
CryptDestroyHash
(
hash
);
return
0
;
ECDSA_SIG_free
(
ecsig
);
goto
err
;
}
return
ecsig
;
err:
BN_free
(
r
);
/* it is ok to free NULL BN */
BN_free
(
s
);
return
NULL
;
}
len
=
RSA_size
(
rsa
);
buf
=
malloc
(
len
);
if
(
buf
==
NULL
)
{
RSAerr
(
RSA_F_RSA_OSSL_PRIVATE_ENCRYPT
,
ERR_R_MALLOC_FAILURE
);
CryptDestroyHash
(
hash
);
return
0
;
}
if
(
!
CryptSignHash
(
hash
,
cd
->
key_spec
,
NULL
,
0
,
buf
,
&
len
))
#ifndef HAVE_XKEY_PROVIDER
/** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */
static
ECDSA_SIG
*
ecdsa_sign_sig
(
const
unsigned
char
*
dgst
,
int
dgstlen
,
const
BIGNUM
*
in_kinv
,
const
BIGNUM
*
in_r
,
EC_KEY
*
ec
)
{
ECDSA_SIG
*
ecsig
=
NULL
;
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
);
CryptDestroyHash
(
hash
);
free
(
buf
);
return
0
;
SetLastError
(
status
);
msg
(
M_NONFATAL
|
M_ERRNO
,
"Error in cryptoapticert: NCryptSignHash failed"
);
}
/* and now, we have to reverse the byte-order in the result from CryptSignHash()... */
for
(
i
=
0
;
i
<
len
;
i
++
)
else
{
to
[
i
]
=
buf
[
len
-
i
-
1
];
/* NCryptSignHash returns r, s concatenated in buf[] */
ecsig
=
ecdsa_bin2sig
(
buf
,
len
);
}
free
(
buf
);
CryptDestroyHash
(
hash
);
return
len
;
return
ecsig
;
}
/**
* 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.
*/
/** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */
static
int
r
sa_sign
_CNG
(
int
type
,
const
unsigned
char
*
m
,
unsigned
int
m_len
,
unsigned
char
*
sig
,
unsigned
int
*
siglen
,
const
RSA
*
rsa
)
ecd
sa_sign
(
int
type
,
const
unsigned
char
*
dgst
,
int
dgstlen
,
unsigned
char
*
sig
,
unsigned
int
*
sig
len
,
const
BIGNUM
*
kinv
,
const
BIGNUM
*
r
,
EC_KEY
*
ec
)
{
CAPI_DATA
*
cd
=
(
CAPI_DATA
*
)
RSA_meth_get0_app_data
(
RSA_get_method
(
rsa
));
const
wchar_t
*
alg
=
NULL
;
int
padding
=
RSA_PKCS1_PADDING
;
ECDSA_SIG
*
s
;
*
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
;
}
alg
=
cng_hash_algo
(
type
);
if
(
alg
&&
wcscmp
(
alg
,
L"UNKNOWN"
)
==
0
)
/* convert internal signature structure 's' to DER encoded byte array in sig */
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
;
}
*
siglen
=
i2d_ECDSA_SIG
(
s
,
&
sig
);
ECDSA_SIG_free
(
s
);
*
siglen
=
priv_enc_CNG
(
cd
,
alg
,
m
,
(
int
)
m_len
,
sig
,
RSA_size
(
rsa
),
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
;
return
1
;
}
/* called at RSA_new */
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 */
static
int
finish
(
RSA
*
rsa
)
{
const
RSA_METHOD
*
rsa_meth
=
RSA_get_method
(
rsa
);
CAPI_DATA
*
cd
=
(
CAPI_DATA
*
)
RSA_meth_get0_app_data
(
rsa_meth
);
/* We only need to set finish among init methods, and sign methods */
EC_KEY_METHOD_set_init
(
ec_method
,
NULL
,
ec_finish
,
NULL
,
NULL
,
NULL
,
NULL
);
EC_KEY_METHOD_set_sign
(
ec_method
,
ecdsa_sign
,
ecdsa_sign_setup
,
ecdsa_sign_sig
);
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
)
{
NCryptFreeObject
(
cd
->
crypt_prov
);
}
else
goto
err
;
}
/* get an index to store cd as external data */
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
);
RSA_meth_free
((
RSA_METHOD
*
)
rsa_meth
);
/* from here on ec will get freed with privkey */
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
;
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
*
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 */
{
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
;
}
if
(
*
p
>=
'0'
&&
*
p
<=
'9'
)
...
...
@@ -595,11 +509,11 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
}
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
;
}
while
(
true
)
while
(
true
)
{
int
validity
=
1
;
/* this frees previous rv, if not NULL */
...
...
@@ -613,7 +527,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
{
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"
);
}
...
...
@@ -622,7 +536,7 @@ out:
return
rv
;
}
#if
(OPENSSL_VERSION_NUMBER >= 0x10100000L)
#if
ndef HAVE_XKEY_PROVIDER
static
const
CAPI_DATA
*
retrieve_capi_data
(
EVP_PKEY
*
pkey
)
...
...
@@ -643,6 +557,8 @@ retrieve_capi_data(EVP_PKEY *pkey)
static
int
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
);
if
(
pkey
&&
retrieve_capi_data
(
pkey
))
...
...
@@ -660,7 +576,7 @@ pkey_rsa_sign_init(EVP_PKEY_CTX *ctx)
* Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs|
* and save the the signature in |sig| and its size 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
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
;
const
wchar_t
*
alg
=
NULL
;
int
padding
;
int
hashlen
;
int
saltlen
;
int
padding
=
0
;
int
hashlen
=
0
;
int
saltlen
=
0
;
pkey
=
EVP_PKEY_CTX_get0_pkey
(
ctx
);
if
(
pkey
)
...
...
@@ -693,7 +609,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
}
else
/* This should not happen */
{
msg
(
M_FATAL
,
"crypto
p
aicert: Unknown key and no default sign operation to fallback on"
);
msg
(
M_FATAL
,
"
Error in
cryptoa
p
icert: Unknown key and no default sign operation to fallback on"
);
return
-
1
;
}
}
...
...
@@ -714,20 +630,19 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
*/
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
;
}
}
else
{
msg
(
M_NONFATAL
,
"cryptoapicert: could not determine the signature digest algorithm"
);
RSAerr
(
RSA_F_PKEY_RSA_SIGN
,
RSA_R_UNKNOWN_ALGORITHM_TYPE
);
msg
(
M_NONFATAL
,
"Error in cryptoapicert: could not determine the signature digest algorithm"
);
return
-
1
;
}
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
;
}
...
...
@@ -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
)
||
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."
);
RSAerr
(
RSA_F_PKEY_RSA_SIGN
,
RSA_R_UNSUPPORTED_MASK_PARAMETER
)
;
return
-
1
;
}
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,
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
;
}
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
);
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
SSL_CTX_use_CryptoAPI_certificate
(
SSL_CTX
*
ssl_ctx
,
const
char
*
cert_prop
)
{
HCERTSTORE
cs
;
X509
*
cert
=
NULL
;
RSA
*
rsa
=
NULL
,
*
pub_rsa
;
CAPI_DATA
*
cd
=
calloc
(
1
,
sizeof
(
*
cd
));
RSA_METHOD
*
my_rsa_method
=
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
;
}
/* search CURRENT_USER first, then LOCAL_MACHINE */
...
...
@@ -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"
);
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
;
}
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)
|
CERT_STORE_OPEN_EXISTING_FLAG
|
CERT_STORE_READONLY_FLAG
,
L"MY"
);
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
;
}
cd
->
cert_context
=
find_certificate_in_store
(
cert_prop
,
cs
);
CertCloseStore
(
cs
,
0
);
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
;
}
}
...
...
@@ -839,106 +979,20 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
cd
->
cert_context
->
cbCertEncoded
);
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
;
}
/* 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 handle
s only
*/
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
,
&
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
* smart card certificate, we get:
* "Error 1223: The operation was canceled by the user." */
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
);
/* private key may be in a token not available, or incompatible with CNG */
msg
(
M_NONFATAL
|
M_ERRNO
,
"Error in cryptoapicert: failed to acquire key. Key not present or "
"is in a legacy token not supported by Windows CNG API"
);
goto
err
;
}
...
...
@@ -948,86 +1002,52 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
{
goto
err
;
}
/* 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
* we decrease it here with X509_free(), or it will never be cleaned up. */
X509_free
(
cert
);
cert
=
NULL
;
if
(
!
(
pub_rsa
=
EVP_PKEY_get0_RSA
(
pkey
)))
{
msg
(
M_WARN
,
"cryptoapicert requires an RSA certificate"
);
goto
err
;
}
#ifdef HAVE_XKEY_PROVIDER
EVP_PKEY
*
privkey
=
xkey_load_generic_key
(
tls_libctx
,
cd
,
pkey
,
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 */
const
BIGNUM
*
n
=
NULL
;
const
BIGNUM
*
e
=
NULL
;
RSA_get0_key
(
pub_rsa
,
&
n
,
&
e
,
NULL
);
if
(
!
RSA_set0_key
(
rsa
,
BN_dup
(
n
),
BN_dup
(
e
),
NULL
))
#else
/* ifdef HAVE_XKEY_PROVIDER */
if
(
EVP_PKEY_id
(
pkey
)
==
EVP_PKEY_RSA
)
{
goto
err
;
if
(
!
ssl_ctx_set_rsakey
(
ssl_ctx
,
cd
,
pkey
))
{
goto
err
;
}
}
RSA_set_flags
(
rsa
,
RSA_flags
(
rsa
)
|
RSA_FLAG_EXT_PKEY
);
if
(
!
RSA_set_method
(
rsa
,
my_rsa_method
))
else
if
(
EVP_PKEY_id
(
pkey
)
==
EVP_PKEY_EC
)
{
goto
err
;
if
(
!
ssl_ctx_set_eckey
(
ssl_ctx
,
cd
,
pkey
))
{
goto
err
;
}
}
if
(
!
SSL_CTX_use_RSAPrivateKey
(
ssl_ctx
,
rsa
))
else
{
msg
(
M_WARN
|
M_INFO
,
"WARNING: cryptoapicert: key type <%d> not supported"
,
EVP_PKEY_id
(
pkey
));
goto
err
;
}
/* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
* we decrease it here with RSA_free(), or it will never be cleaned up. */
RSA_free
(
rsa
);
CAPI_DATA_free
(
cd
);
/* this will do a ref_count-- */
return
1
;
#endif
/* HAVE_XKEY_PROVIDER */
err:
if
(
cert
)
{
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
);
}
}
CAPI_DATA_free
(
cd
);
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 */
src/openvpn/dco.c
0 → 100644
View file @
e1e1377b
/*
* 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) */
src/openvpn/dco.h
0 → 100644
View file @
e1e1377b
/*
* 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 */
src/openvpn/dco_internal.h
0 → 100644
View file @
e1e1377b
/*
* 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 */
Prev
1
…
9
10
11
12
13
Next