1 #include "mutual_tls.hpp"
2
3 #include "sessions.hpp"
4
5 extern "C"
6 {
7 #include <openssl/asn1.h>
8 #include <openssl/ec.h>
9 #include <openssl/evp.h>
10 #include <openssl/obj_mac.h>
11 #include <openssl/types.h>
12 #include <openssl/x509.h>
13 #include <openssl/x509_vfy.h>
14 #include <openssl/x509v3.h>
15 }
16
17 #include <boost/asio/ip/address.hpp>
18 #include <boost/asio/ssl/verify_context.hpp>
19
20 #include <array>
21 #include <memory>
22
23 #include <gmock/gmock.h>
24 #include <gtest/gtest.h>
25
26 using ::testing::IsNull;
27 using ::testing::NotNull;
28
29 namespace
30 {
31 class OSSLX509
32 {
33 X509* ptr = X509_new();
34
35 public:
36 OSSLX509& operator=(const OSSLX509&) = delete;
37 OSSLX509& operator=(OSSLX509&&) = delete;
38
39 OSSLX509(const OSSLX509&) = delete;
40 OSSLX509(OSSLX509&&) = delete;
41
42 OSSLX509() = default;
43
setSubjectName()44 void setSubjectName()
45 {
46 X509_NAME* name = X509_get_subject_name(ptr);
47 std::array<unsigned char, 5> user = {'u', 's', 'e', 'r', '\0'};
48 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, user.data(), -1,
49 -1, 0);
50 }
sign()51 void sign()
52 {
53 // Generate test key
54 EVP_PKEY* pkey = nullptr;
55 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
56 ASSERT_EQ(EVP_PKEY_keygen_init(pctx), 1);
57 ASSERT_EQ(
58 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1),
59 1);
60 ASSERT_EQ(EVP_PKEY_keygen(pctx, &pkey), 1);
61 EVP_PKEY_CTX_free(pctx);
62
63 // Sign cert with key
64 ASSERT_EQ(X509_set_pubkey(ptr, pkey), 1);
65 ASSERT_GT(X509_sign(ptr, pkey, EVP_sha256()), 0);
66 EVP_PKEY_free(pkey);
67 }
68
get()69 X509* get()
70 {
71 return ptr;
72 }
~OSSLX509()73 ~OSSLX509()
74 {
75 X509_free(ptr);
76 }
77 };
78
79 class OSSLX509StoreCTX
80 {
81 X509_STORE_CTX* ptr = X509_STORE_CTX_new();
82
83 public:
84 OSSLX509StoreCTX& operator=(const OSSLX509StoreCTX&) = delete;
85 OSSLX509StoreCTX& operator=(OSSLX509StoreCTX&&) = delete;
86
87 OSSLX509StoreCTX(const OSSLX509StoreCTX&) = delete;
88 OSSLX509StoreCTX(OSSLX509StoreCTX&&) = delete;
89
90 OSSLX509StoreCTX() = default;
get()91 X509_STORE_CTX* get()
92 {
93 return ptr;
94 }
~OSSLX509StoreCTX()95 ~OSSLX509StoreCTX()
96 {
97 X509_STORE_CTX_free(ptr);
98 }
99 };
100
TEST(MutualTLS,GoodCert)101 TEST(MutualTLS, GoodCert)
102 {
103 OSSLX509 x509;
104
105 x509.setSubjectName();
106 X509_EXTENSION* ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage,
107 "digitalSignature, keyAgreement");
108 ASSERT_THAT(ex, NotNull());
109 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
110 X509_EXTENSION_free(ex);
111 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage, "clientAuth");
112 ASSERT_THAT(ex, NotNull());
113 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
114 X509_EXTENSION_free(ex);
115
116 x509.sign();
117
118 OSSLX509StoreCTX x509Store;
119 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
120
121 boost::asio::ip::address ip;
122 boost::asio::ssl::verify_context ctx(x509Store.get());
123 std::shared_ptr<persistent_data::UserSession> session =
124 verifyMtlsUser(ip, ctx);
125 ASSERT_THAT(session, NotNull());
126 EXPECT_THAT(session->username, "user");
127 }
128
TEST(MutualTLS,MissingKeyUsage)129 TEST(MutualTLS, MissingKeyUsage)
130 {
131 for (const char* usageString :
132 {"digitalSignature", "keyAgreement", "digitalSignature, keyAgreement"})
133 {
134 OSSLX509 x509;
135 x509.setSubjectName();
136
137 X509_EXTENSION* ex =
138 X509V3_EXT_conf_nid(nullptr, nullptr, NID_key_usage, usageString);
139
140 ASSERT_THAT(ex, NotNull());
141 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
142 X509_EXTENSION_free(ex);
143 ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_ext_key_usage,
144 "clientAuth");
145 ASSERT_THAT(ex, NotNull());
146 ASSERT_EQ(X509_add_ext(x509.get(), ex, -1), 1);
147 X509_EXTENSION_free(ex);
148 x509.sign();
149
150 OSSLX509StoreCTX x509Store;
151 X509_STORE_CTX_set_current_cert(x509Store.get(), x509.get());
152
153 boost::asio::ip::address ip;
154 boost::asio::ssl::verify_context ctx(x509Store.get());
155 std::shared_ptr<persistent_data::UserSession> session =
156 verifyMtlsUser(ip, ctx);
157 ASSERT_THAT(session, NotNull());
158 }
159 }
160
TEST(MutualTLS,MissingCert)161 TEST(MutualTLS, MissingCert)
162 {
163 OSSLX509StoreCTX x509Store;
164
165 boost::asio::ip::address ip;
166 boost::asio::ssl::verify_context ctx(x509Store.get());
167 std::shared_ptr<persistent_data::UserSession> session =
168 verifyMtlsUser(ip, ctx);
169 ASSERT_THAT(session, IsNull());
170 }
171 } // namespace
172