0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00
openvpn3/openvpn/crypto/crypto_aead.hpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

385 lines
12 KiB
C++
Raw Normal View History

// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2022 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// OpenVPN AEAD data channel interface
#ifndef OPENVPN_CRYPTO_CRYPTO_AEAD_H
#define OPENVPN_CRYPTO_CRYPTO_AEAD_H
#include <cstring> // for std::memcpy, std::memset
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/clamp_typerange.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/crypto/cryptodc.hpp>
// Sample AES-GCM head:
// 48000001 00000005 7e7046bd 444a7e28 cc6387b1 64a4d6c1 380275a...
// [ OP32 ] [seq # ] [ auth tag ] [ payload ... ]
// [4-byte
// IV head]
using openvpn::numeric_util::clamp_to_default;
Eliminate some conversion warnings - [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
2023-02-27 21:47:53 +01:00
namespace openvpn::AEAD {
OPENVPN_EXCEPTION(aead_error);
template <typename CRYPTO_API>
class Crypto : public CryptoDCInstance
{
class Nonce
{
public:
Nonce()
{
static_assert(4 + CRYPTO_API::CipherContextAEAD::IV_LEN == sizeof(data),
"AEAD IV_LEN inconsistency");
ad_op32 = false;
std::memset(data, 0, sizeof(data));
}
// setup
void set_tail(const StaticKey &sk)
{
if (sk.size() < 8)
throw aead_error("insufficient key material for nonce tail");
std::memcpy(data + 8, sk.data(), 8);
}
// for encrypt
Nonce(const Nonce &ref, PacketIDSend &pid_send, const PacketID::time_t now, const unsigned char *op32)
{
std::memcpy(data, ref.data, sizeof(data));
Buffer buf(data + 4, 4, false);
pid_send.write_next(buf, false, now);
if (op32)
{
ad_op32 = true;
std::memcpy(data, op32, 4);
}
else
ad_op32 = false;
}
// for encrypt
void prepend_ad(Buffer &buf) const
{
buf.prepend(data + 4, 4);
}
// for decrypt
Nonce(const Nonce &ref, Buffer &buf, const unsigned char *op32)
{
std::memcpy(data, ref.data, sizeof(data));
buf.read(data + 4, 4);
if (op32)
{
ad_op32 = true;
std::memcpy(data, op32, 4);
}
else
ad_op32 = false;
}
// for decrypt
bool verify_packet_id(PacketIDReceive &pid_recv, const PacketID::time_t now)
{
Buffer buf(data + 4, 4, true);
const PacketID pid = pid_recv.read_next(buf);
return pid_recv.test_add(pid, now, true); // verify packet ID
}
const unsigned char *iv() const
{
return data + 4;
}
const unsigned char *ad() const
{
return ad_op32 ? data : data + 4;
}
size_t ad_len() const
{
return ad_op32 ? 8 : 4;
}
private:
bool ad_op32; // true if AD includes op32 opcode
// Sample data:
// [ OP32 (optional) ] [ pkt ID ] [ nonce tail ]
2014-12-31 08:24:54 +01:00
// [ 48 00 00 01 ] [ 00 00 00 05 ] [ 7f 45 64 db 33 5b 6c 29 ]
unsigned char data[16];
};
struct Encrypt
{
typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce;
PacketIDSend pid_send;
BufferAllocated work;
};
struct Decrypt
{
typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce;
PacketIDReceive pid_recv;
BufferAllocated work;
};
public:
typedef CryptoDCInstance Base;
Crypto(SSLLib::Ctx libctx_arg,
const CryptoAlgs::Type cipher_arg,
const Frame::Ptr &frame_arg,
const SessionStats::Ptr &stats_arg)
: cipher(cipher_arg),
frame(frame_arg),
stats(stats_arg),
libctx(libctx_arg)
{
}
// Encrypt/Decrypt
// returns true if packet ID is close to wrapping
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
{
// only process non-null packets
if (buf.size())
{
// build nonce/IV/AD
Nonce nonce(e.nonce, e.pid_send, now, op32);
// encrypt to work buf
frame->prepare(Frame::ENCRYPT_WORK, e.work);
if (e.work.max_size() < buf.size())
throw aead_error("encrypt work buffer too small");
// alloc auth tag in buffer
unsigned char *auth_tag = e.work.prepend_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
unsigned char *auth_tag_end;
// prepare output buffer
unsigned char *work_data = e.work.write_alloc(buf.size());
if (e.impl.requires_authtag_at_end())
{
auth_tag_end = e.work.write_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
}
// encrypt
e.impl.encrypt(buf.data(), work_data, buf.size(), nonce.iv(), auth_tag, nonce.ad(), nonce.ad_len());
if (e.impl.requires_authtag_at_end())
{
/* move the auth tag to the front */
std::memcpy(auth_tag, auth_tag_end, CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
/* Ignore the auth tag at the end */
e.work.inc_size(-CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
}
buf.swap(e.work);
// prepend additional data
nonce.prepend_ad(buf);
}
return e.pid_send.wrap_warning();
}
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
{
// only process non-null packets
if (buf.size())
{
// get nonce/IV/AD
Nonce nonce(d.nonce, buf, op32);
// get auth tag
unsigned char *auth_tag = buf.read_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
// initialize work buffer
frame->prepare(Frame::DECRYPT_WORK, d.work);
if (d.work.max_size() < buf.size())
throw aead_error("decrypt work buffer too small");
if (e.impl.requires_authtag_at_end())
{
unsigned char *auth_tag_end = buf.write_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
std::memcpy(auth_tag_end, auth_tag, CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
}
// decrypt from buf -> work
2014-12-31 08:24:54 +01:00
if (!d.impl.decrypt(buf.c_data(), d.work.data(), buf.size(), nonce.iv(), auth_tag, nonce.ad(), nonce.ad_len()))
{
buf.reset_size();
return Error::DECRYPT_ERROR;
}
if (e.impl.requires_authtag_at_end())
{
d.work.set_size(buf.size() - CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
}
else
{
d.work.set_size(buf.size());
}
// verify packet ID
if (!nonce.verify_packet_id(d.pid_recv, now))
{
buf.reset_size();
return Error::REPLAY_ERROR;
}
// return cleartext result in buf
buf.swap(d.work);
}
return Error::SUCCESS;
}
// Initialization
// TODO: clamp_to_default probably will cause an error further along if triggered, investigate
void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
{
Eliminate some conversion warnings - [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
2023-02-27 21:47:53 +01:00
e.impl.init(libctx,
cipher,
encrypt_key.data(),
clamp_to_default<unsigned int>(encrypt_key.size(), 0),
Eliminate some conversion warnings - [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
2023-02-27 21:47:53 +01:00
CRYPTO_API::CipherContextAEAD::ENCRYPT);
d.impl.init(libctx,
cipher,
decrypt_key.data(),
clamp_to_default<unsigned int>(decrypt_key.size(), 0),
Eliminate some conversion warnings - [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
2023-02-27 21:47:53 +01:00
CRYPTO_API::CipherContextAEAD::DECRYPT);
}
void init_hmac(StaticKey &&encrypt_key,
StaticKey &&decrypt_key) override
{
e.nonce.set_tail(encrypt_key);
d.nonce.set_tail(decrypt_key);
}
void init_pid(const int send_form,
const int recv_mode,
const int recv_form,
const char *recv_name,
const int recv_unit,
const SessionStats::Ptr &recv_stats_arg) override
{
e.pid_send.init(send_form);
d.pid_recv.init(recv_mode, recv_form, recv_name, recv_unit, recv_stats_arg);
}
// Indicate whether or not cipher/digest is defined
unsigned int defined() const override
{
unsigned int ret = CRYPTO_DEFINED;
// AEAD mode doesn't use HMAC, but we still indicate HMAC_DEFINED
// because we want to use the HMAC keying material for the AEAD nonce tail.
if (CryptoAlgs::defined(cipher))
ret |= (CIPHER_DEFINED | HMAC_DEFINED);
return ret;
}
bool consider_compression(const CompressContext &comp_ctx) override
{
return true;
}
// Rekeying
void rekey(const typename Base::RekeyType type) override
{
}
private:
CryptoAlgs::Type cipher;
Frame::Ptr frame;
SessionStats::Ptr stats;
SSLLib::Ctx libctx;
Encrypt e;
Decrypt d;
};
template <typename CRYPTO_API>
class CryptoContext : public CryptoDCContext
{
public:
typedef RCPtr<CryptoContext> Ptr;
CryptoContext(SSLLib::Ctx libctx_arg,
const CryptoAlgs::Type cipher_arg,
const CryptoAlgs::KeyDerivation key_method,
const Frame::Ptr &frame_arg,
const SessionStats::Ptr &stats_arg)
: CryptoDCContext(key_method),
cipher(CryptoAlgs::legal_dc_cipher(cipher_arg)),
frame(frame_arg),
stats(stats_arg),
libctx(libctx_arg)
{
}
CryptoDCInstance::Ptr new_obj(const unsigned int key_id) override
{
return new Crypto<CRYPTO_API>(libctx, cipher, frame, stats);
}
// cipher/HMAC/key info
Info crypto_info() override
{
Info ret;
ret.cipher_alg = cipher;
ret.hmac_alg = CryptoAlgs::NONE;
ret.key_derivation = key_derivation;
return ret;
}
// Info for ProtoContext::link_mtu_adjust
size_t encap_overhead() const override
{
return CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN;
}
private:
CryptoAlgs::Type cipher;
Frame::Ptr frame;
SessionStats::Ptr stats;
SSLLib::Ctx libctx;
};
Eliminate some conversion warnings - [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
2023-02-27 21:47:53 +01:00
} // namespace openvpn::AEAD
#endif