Skip to content

Commit b206dcf

Browse files
committed
unit test strategy
1 parent 6514182 commit b206dcf

File tree

1 file changed

+151
-1
lines changed

1 file changed

+151
-1
lines changed

test/unit/test/sansio/handshake/handshake_csha2p_hash_password.cpp

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@
88
#include <boost/mysql/client_errc.hpp>
99

1010
#include <boost/mysql/impl/internal/sansio/caching_sha2_password.hpp>
11+
#include <boost/mysql/impl/internal/sansio/csha2p_encrypt_password.hpp>
12+
13+
#include <boost/core/span.hpp>
14+
#include <boost/test/unit_test.hpp>
15+
16+
#include <cstddef>
17+
#include <openssl/bio.h>
18+
#include <openssl/evp.h>
19+
#include <openssl/pem.h>
20+
#include <vector>
1121

1222
#include "test_common/assert_buffer_equals.hpp"
1323

1424
using namespace boost::mysql;
1525
using namespace boost::mysql::test;
26+
using detail::csha2p_encrypt_password;
1627
using detail::csha2p_hash_password;
1728

29+
using buffer_type = boost::container::small_vector<std::uint8_t, 512>;
30+
1831
namespace {
1932

2033
BOOST_AUTO_TEST_SUITE(test_handshake_csha2p_hash_password)
@@ -43,11 +56,147 @@ BOOST_AUTO_TEST_CASE(empty_password)
4356

4457
BOOST_AUTO_TEST_SUITE_END()
4558

59+
// Encrypting with RSA and OAEP padding involves random numbers for padding.
60+
// There isn't a reliable way to seed OpenSSL's random number generators so that
61+
// the output is deterministic. So we do the following:
62+
// 1. We know the server's public and private keys and the password (the constants below).
63+
// 2. We capture a scramble and a corresponding ciphertext using Wireshark.
64+
// 3. We decrypt the ciphertext with OpenSSL to obtain the expected plaintext (the salted password).
65+
// 4. Tests run csha2p_encrypt_password, decrypt its output, and verify that it matches the expected
66+
// plaintext.
4667
BOOST_AUTO_TEST_SUITE(test_handshake_csha2p_encrypt_password)
4768

69+
constexpr unsigned char public_key_2048[] = R"%(-----BEGIN PUBLIC KEY-----
70+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA36OYSpdiy1lFDrdO1Vux
71+
GwjPTo35R2+mXqW2SZV7kH5C6BSCeoTk6STVRBJbgOCabtp5bpUZ+x2bYWOZp4fs
72+
JakC75CN2YTJoYg5z5U6XUBEWn6WNBpvEoSJaUtrzfU69J07uWqB6v0MdJf3JTgd
73+
ILfGKvk2T+maxqUiYObs0BJd5eKJZDlUaf2r4a9KC8zGUZzHdgtZEXlkHVNLEbbD
74+
Ju4KjtCtJCG1NEBAh3oSnNp/Q1FKFywqU7YnEBWI0B9C5UcKNFbg7M35daimXfGp
75+
V7WJKhO9w7iBJYL1SW+PwyUCh3DNsuSm3nLmuwKhTvGQHZJS/5OVdSHgZhjDnk2V
76+
WwIDAQAB
77+
-----END PUBLIC KEY-----
78+
)%";
79+
80+
constexpr unsigned char private_key_2048[] = R"%(-----BEGIN PRIVATE KEY-----
81+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDfo5hKl2LLWUUO
82+
t07VW7EbCM9OjflHb6ZepbZJlXuQfkLoFIJ6hOTpJNVEEluA4Jpu2nlulRn7HZth
83+
Y5mnh+wlqQLvkI3ZhMmhiDnPlTpdQERafpY0Gm8ShIlpS2vN9Tr0nTu5aoHq/Qx0
84+
l/clOB0gt8Yq+TZP6ZrGpSJg5uzQEl3l4olkOVRp/avhr0oLzMZRnMd2C1kReWQd
85+
U0sRtsMm7gqO0K0kIbU0QECHehKc2n9DUUoXLCpTticQFYjQH0LlRwo0VuDszfl1
86+
qKZd8alXtYkqE73DuIElgvVJb4/DJQKHcM2y5Kbecua7AqFO8ZAdklL/k5V1IeBm
87+
GMOeTZVbAgMBAAECggEANI0UtDJunKoVeCfK9ofdTiT70dG6yfaKeaMm+pONvZ5t
88+
ymtHXdLsl3x4QM6vgdFFeNcNwdZ3jHKgmHn3GU7vRso4TmMBciOp3bNNImJGnLMF
89+
XN5yHTw47XkHcR6v7m25tNFdv2wvqzBbROqQwMY20gFdJ6v3/z89h4A2W97nttyp
90+
ixcNdSTHOfu6iUceEGi2PjHrvw4STPQeihXFNTnYG7hvlWzAerQ5STx5K2n4JoXz
91+
xI5MuHS6PGj5EBPUoq0+EQhmhORWBdNCMcijpHqobVDjifPRY2JbI3zZHtVjYpGH
92+
otmc72hIDjNW7RX1ePKW/gsq6p2by3U8+4dOdya4AQKBgQDz4/6uo7atJSTgVo3d
93+
Zr/7UJ4Qi2mlc992jLAqTQle6JchhNERoPvb19Qy8BaOz6LcxmuMXnRij05mxdyb
94+
LmoH8TPe6RFLCOJrRapkUjUtRD8dIg6UNFLk98LD5t3o5PoHwNpBFfokRlchwoHL
95+
uBvbEHQkrPX2xPbgla5e7zJLgQKBgQDqvjCUMvmap9+AlqxGFhv51V9dIlKvT0xW
96+
p5KEMVfs3JXCHAM7o0ClZ8NitHXw18E8+iMG4pWw3+FX4K6tKGR+rCeGCUNGuiQT
97+
FzXjrEej+Pnuk8zacjXkbS+PNnEqhpSq6STZVFn2UW+ZWAoq2iAMR7qw0eAjylln
98+
h//Wad3+2wKBgAjwWEtKUM2zyNA4G+b7dxnc8I4mre6UeqI7sdE7FZbW64Mc/RSq
99+
U9DQ7kQXrJv7XDq/Qv3YEGf0XKlDozxEzToRSxdmb23Sm4nW+dHHeY95Kt8EeohQ
100+
CqG9uvO3KHb6vXc/SECOb6aYtWTVXjB7RPoYdklJ1ZH/0hSVJ9ju52cBAoGBAK63
101+
A90p25F6ZOWGP46iogve/e2JwFTvBnhwnKJ7P1/yBhzFULqwlUsG4euzOR0a2J6T
102+
5kIXnyZYW5ZWimwi5jlJ1Nj0R/h6TqNO4TMlZOTsSMmDhDMKUoZDpeRHtw7ZwAk9
103+
IcoH+DVXA2L0ngyq8LNzJ8a3TsYUs1pVZNunTC2FAoGARe4x29tdri8akxxwF3BV
104+
bjJ9qRUIfIDK8rGWRdw94vVCB7XVmSWCEchmLqA1DqGYvAhYMYjkXTg9akfBTUQS
105+
s+8JasUuQam8Y88JAfC0QqGbLgUsh0TpRUOXj+YQuoNiMVu14NNgYgFkx71WtvAq
106+
kUmkxr/moPcZ+O1ahVjv/Us=
107+
-----END PRIVATE KEY-----
108+
)%";
109+
110+
// Decrypts the output of
111+
std::vector<std::uint8_t> decrypt(
112+
boost::span<const std::uint8_t> private_key,
113+
boost::span<const std::uint8_t> ciphertext
114+
)
115+
{
116+
// RAII helpers
117+
struct bio_deleter
118+
{
119+
void operator()(BIO* bio) const noexcept { BIO_free(bio); }
120+
};
121+
using unique_bio = std::unique_ptr<BIO, bio_deleter>;
122+
123+
struct evp_pkey_deleter
124+
{
125+
void operator()(EVP_PKEY* pkey) const noexcept { EVP_PKEY_free(pkey); }
126+
};
127+
using unique_evp_pkey = std::unique_ptr<EVP_PKEY, evp_pkey_deleter>;
128+
129+
struct evp_pkey_ctx_deleter
130+
{
131+
void operator()(EVP_PKEY_CTX* ctx) const noexcept { EVP_PKEY_CTX_free(ctx); }
132+
};
133+
using unique_evp_pkey_ctx = std::unique_ptr<EVP_PKEY_CTX, evp_pkey_ctx_deleter>;
134+
135+
// Create a BIO with the key
136+
unique_bio bio(BIO_new_mem_buf(private_key.data(), private_key.size()));
137+
BOOST_TEST_REQUIRE(bio != nullptr, "Creating a BIO failed: " << ERR_get_error());
138+
139+
// Load the key
140+
unique_evp_pkey pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
141+
BOOST_TEST_REQUIRE(bio != nullptr, "Loading the key failed: " << ERR_get_error());
142+
143+
// Create a decryption context
144+
unique_evp_pkey_ctx ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
145+
BOOST_TEST_REQUIRE(ctx != nullptr, "Creating a context failed: " << ERR_get_error());
146+
147+
// Initialize it
148+
BOOST_TEST_REQUIRE(
149+
EVP_PKEY_decrypt_init(ctx.get()) > 0,
150+
"Initializing decryption failed: " << ERR_get_error()
151+
);
152+
153+
// Set the padding scheme
154+
BOOST_TEST_REQUIRE(
155+
EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) > 0,
156+
"Setting the padding scheme failed: " << ERR_get_error()
157+
);
158+
159+
// Determine the size of the decrypted buffer
160+
std::size_t out_size = 0;
161+
BOOST_TEST_REQUIRE(
162+
EVP_PKEY_decrypt(ctx.get(), nullptr, &out_size, ciphertext.data(), ciphertext.size()) > 0,
163+
"Determining decryption size failed: " << ERR_get_error()
164+
);
165+
std::vector<std::uint8_t> res(out_size, 0);
166+
167+
// Actually decrypt
168+
BOOST_TEST_REQUIRE(
169+
EVP_PKEY_decrypt(ctx.get(), res.data(), &out_size, ciphertext.data(), ciphertext.size()) > 0,
170+
"Decrypting failed: " << ERR_get_error()
171+
);
172+
res.resize(out_size);
173+
174+
// Done
175+
return res;
176+
}
177+
178+
//
179+
BOOST_AUTO_TEST_CASE(password_lt20)
180+
{
181+
// Setup
182+
constexpr std::uint8_t scramble[] = {
183+
0x0f, 0x64, 0x4f, 0x2f, 0x2b, 0x3b, 0x27, 0x6b, 0x45, 0x5c,
184+
0x53, 0x01, 0x13, 0x7e, 0x4f, 0x10, 0x26, 0x23, 0x5d, 0x27,
185+
};
186+
constexpr std::uint8_t expected_decrypted[] =
187+
{0x6c, 0x17, 0x27, 0x4e, 0x19, 0x4b, 0x78, 0x1b, 0x24, 0x2f, 0x20, 0x76, 0x7c, 0x0c, 0x2b, 0x10};
188+
buffer_type buff;
189+
190+
// Call the function
191+
unsigned long err = csha2p_encrypt_password("csha2p_password", scramble, public_key_2048, buff);
192+
193+
// Verify
194+
BOOST_TEST_REQUIRE(err == 0u);
195+
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(decrypt(private_key_2048, buff), expected_decrypted);
196+
}
197+
48198
/**
49199
encrypt password success
50-
success
51200
success password is 20 bytes
52201
success password is > 20 bytes
53202
success password is max length
@@ -72,6 +221,7 @@ encrypting
72221
the returned size is > buffer (mock)
73222
encryption fails (probably merge with the one below)
74223
password is too big for encryption (with 2 sizes)
224+
buffer is reset?
75225
*/
76226

77227
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)