FreeRDP
Loading...
Searching...
No Matches
Test_x509_utils.c
1#include <winpr/file.h>
2#include <winpr/string.h>
3#include "../x509_utils.h"
4
5typedef char* (*get_field_pr)(const X509*);
6typedef struct
7{
8 enum
9 {
10 DISABLED,
11 ENABLED,
12 } status;
13 const char* field_description;
14 get_field_pr get_field;
15 const char* expected_result;
16} certificate_test_t;
17
18static char* x509_utils_subject_common_name_wo_length(const X509* xcert)
19{
20 size_t length = 0;
21 return x509_utils_get_common_name(xcert, &length);
22}
23
24static char* certificate_path(const char* filename)
25{
26 /*
27 Assume the .pem file is in the same directory as this source file.
28 Assume that __FILE__ will be a valid path to this file, even from the current working directory
29 where the tests are run. (ie. no chdir occurs between compilation and test running, or __FILE__
30 is an absolute path).
31 */
32 static const char dirsep = '/';
33#ifdef TEST_SOURCE_DIR
34 const char* file = TEST_SOURCE_DIR;
35 const size_t flen = strlen(file) + sizeof(dirsep) + strlen(filename) + sizeof(char);
36 char* result = calloc(1, flen);
37 if (!result)
38 return nullptr;
39 (void)_snprintf(result, flen, "%s%c%s", file, dirsep, filename);
40 return result;
41#else
42 const char* file = __FILE__;
43 const char* last_dirsep = strrchr(file, dirsep);
44
45 if (last_dirsep)
46 {
47 const size_t filenameLen = strlen(filename);
48 const size_t dirsepLen = last_dirsep - file + 1;
49 char* result = malloc(dirsepLen + filenameLen + 1);
50 if (!result)
51 return nullptr;
52 strncpy(result, file, dirsepLen);
53 strncpy(result + dirsepLen, filename, filenameLen + 1);
54 return result;
55 }
56 else
57 {
58 /* No dirsep => relative path in same directory */
59 return _strdup(filename);
60 }
61#endif
62}
63
64static const certificate_test_t certificate_tests[] = {
65
66 { ENABLED, "Certificate Common Name", x509_utils_subject_common_name_wo_length,
67 "TESTJEAN TESTMARTIN 9999999" },
68
69 { ENABLED, "Certificate subject", x509_utils_get_subject,
70 "CN = TESTJEAN TESTMARTIN 9999999, C = FR, O = MINISTERE DES TESTS, OU = 0002 110014016, OU "
71 "= PERSONNES, UID = 9999999, GN = TESTJEAN, SN = TESTMARTIN" },
72
73 { DISABLED, "Kerberos principal name", nullptr, "testjean.testmartin@kpn.test.example.com" },
74
75 { ENABLED, "Certificate e-mail", x509_utils_get_email, "testjean.testmartin@test.example.com"
76
77 },
78
79 { ENABLED, "Microsoft's Universal Principal Name", x509_utils_get_upn,
80 "testjean.testmartin.9999999@upn.test.example.com" },
81
82 { ENABLED, "Certificate issuer", x509_utils_get_issuer,
83 "CN = ADMINISTRATION CENTRALE DES TESTS, C = FR, O = MINISTERE DES TESTS, OU = 0002 "
84 "110014016" },
85};
86
87static int TestCertificateFile(const char* certificate_path,
88 const certificate_test_t* ccertificate_tests, size_t count)
89{
90 int success = 0;
91
92 X509* certificate = x509_utils_from_pem(certificate_path, strlen(certificate_path), TRUE);
93
94 if (!certificate)
95 {
96 printf("%s: failure: cannot read certificate file '%s'\n", __func__, certificate_path);
97 success = -1;
98 goto fail;
99 }
100
101 for (size_t i = 0; i < count; i++)
102 {
103 const certificate_test_t* test = &ccertificate_tests[i];
104 char* result = nullptr;
105
106 if (test->status == DISABLED)
107 {
108 continue;
109 }
110
111 result = (test->get_field ? test->get_field(certificate) : nullptr);
112
113 if (result)
114 {
115 printf("%s: crypto got %-40s -> \"%s\"\n", __func__, test->field_description, result);
116
117 if (0 != strcmp(result, test->expected_result))
118 {
119 printf("%s: failure: for %s, actual: \"%s\", expected \"%s\"\n", __func__,
120 test->field_description, result, test->expected_result);
121 success = -1;
122 }
123
124 free(result);
125 }
126 else
127 {
128 printf("%s: failure: cannot get %s\n", __func__, test->field_description);
129 }
130 }
131
132fail:
133 X509_free(certificate);
134 return success;
135}
136
137/* clang-format off */
138/*
139These certificates were generated with the following commands:
140
141openssl ecparam -name P-256 -out /tmp/p256.pem
142openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha1_cert.pem -sha1
143openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha256_cert.pem -sha256
144openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha384_cert.pem -sha384
145openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha512_cert.pem -sha512
146
147openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha1_cert.pem -sha1
148openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha256_cert.pem -sha256
149openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha384_cert.pem -sha384
150openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha512_cert.pem -sha512
151
152openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha1_cert.pem -sha1 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
153openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha256_cert.pem -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
154openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha384_cert.pem -sha384 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
155openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha512_cert.pem -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
156openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha256_mgf1_sha384_cert.pem -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest -sigopt rsa_mgf1_md:sha384
157*/
158/* clang-format on */
159
160typedef struct
161{
162 const char* filename;
163 WINPR_MD_TYPE expected;
164} signature_alg_test_t;
165
166static const signature_alg_test_t signature_alg_tests[] = {
167 { "rsa_pkcs1_sha1_cert.pem", WINPR_MD_SHA1 },
168 { "rsa_pkcs1_sha256_cert.pem", WINPR_MD_SHA256 },
169 { "rsa_pkcs1_sha384_cert.pem", WINPR_MD_SHA384 },
170 { "rsa_pkcs1_sha512_cert.pem", WINPR_MD_SHA512 },
171
172 { "ecdsa_sha1_cert.pem", WINPR_MD_SHA1 },
173 { "ecdsa_sha256_cert.pem", WINPR_MD_SHA256 },
174 { "ecdsa_sha384_cert.pem", WINPR_MD_SHA384 },
175 { "ecdsa_sha512_cert.pem", WINPR_MD_SHA512 },
176
177 { "rsa_pss_sha1_cert.pem", WINPR_MD_SHA1 },
178 { "rsa_pss_sha256_cert.pem", WINPR_MD_SHA256 },
179 { "rsa_pss_sha384_cert.pem", WINPR_MD_SHA384 },
180 { "rsa_pss_sha512_cert.pem", WINPR_MD_SHA512 },
181 /*
182 PSS may use different digests for the message hash and MGF-1 hash. In this case, RFC 5929
183 leaves the tls-server-end-point hash unspecified, so it should return WINPR_MD_NONE.
184 */
185 { "rsa_pss_sha256_mgf1_sha384_cert.pem", WINPR_MD_NONE },
186};
187
188static int TestSignatureAlgorithm(const signature_alg_test_t* test)
189{
190 int success = 0;
191 WINPR_MD_TYPE signature_alg = WINPR_MD_NONE;
192 char* path = certificate_path(test->filename);
193 X509* certificate = x509_utils_from_pem(path, strlen(path), TRUE);
194
195 if (!certificate)
196 {
197 printf("%s: failure: cannot read certificate file '%s'\n", __func__, path);
198 success = -1;
199 goto fail;
200 }
201
202 signature_alg = x509_utils_get_signature_alg(certificate);
203 if (signature_alg != test->expected)
204 {
205 const char* signature_alg_string =
206 signature_alg == WINPR_MD_NONE ? "none" : winpr_md_type_to_string(signature_alg);
207 const char* expected_string =
208 test->expected == WINPR_MD_NONE ? "none" : winpr_md_type_to_string(test->expected);
209 printf("%s: failure: for \"%s\", actual: %s, expected %s\n", __func__, test->filename,
210 signature_alg_string, expected_string);
211 success = -1;
212 goto fail;
213 }
214
215fail:
216 X509_free(certificate);
217 free(path);
218 return success;
219}
220
221/*
222SAN='DNS:*.wildcard.example.com,DNS:*.com,DNS:*..com,DNS:*.com.,DNS:host.example.com'
223openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes \
224 -subj "/CN=*.wildcard.example.com" -addext "subjectAltName=$SAN" \
225 -out Test_x509_wildcard_cert.pem
226*/
227static int TestWildcardNames(void)
228{
229 int rc = -1;
230 size_t count = 0;
231 size_t* lengths = nullptr;
232 char** names = nullptr;
233 char* common_name = nullptr;
234 char* path = certificate_path("Test_x509_wildcard_cert.pem");
235 X509* certificate = path ? x509_utils_from_pem(path, strlen(path), TRUE) : nullptr;
236
237 if (!certificate)
238 {
239 printf("%s: failure: cannot read certificate file '%s'\n", __func__,
240 path ? path : "(null)");
241 goto fail;
242 }
243
244 common_name = x509_utils_get_common_name(certificate, nullptr);
245 if (!common_name || (strcmp(common_name, "*.wildcard.example.com") != 0))
246 {
247 printf("%s: failure: wildcard common name not returned: \"%s\"\n", __func__,
248 common_name ? common_name : "(null)");
249 goto fail;
250 }
251
252 names = x509_utils_get_dns_names(certificate, &count, &lengths);
253 if (!names || (count != 2) || (strcmp(names[0], "*.wildcard.example.com") != 0) ||
254 (strcmp(names[1], "host.example.com") != 0))
255 {
256 printf("%s: failure: expected only the valid wildcard and host names, got %" PRIuz "\n",
257 __func__, count);
258 goto fail;
259 }
260
261 rc = 0;
262fail:
263 x509_utils_dns_names_free(count, lengths, names);
264 free(common_name);
265 X509_free(certificate);
266 free(path);
267 return rc;
268}
269
270int Test_x509_utils(int argc, char* argv[])
271{
272 char* cert_path = certificate_path("Test_x509_cert_info.pem");
273 int ret = 0;
274 WINPR_UNUSED(argc);
275 WINPR_UNUSED(argv);
276
277 ret = TestCertificateFile(cert_path, certificate_tests, ARRAYSIZE(certificate_tests));
278 free(cert_path);
279 if (ret != 0)
280 return ret;
281
282 ret = TestWildcardNames();
283 if (ret != 0)
284 return ret;
285
286 for (size_t i = 0; i < ARRAYSIZE(signature_alg_tests); i++)
287 {
288 ret = TestSignatureAlgorithm(&signature_alg_tests[i]);
289 if (ret != 0)
290 return ret;
291 }
292
293 return ret;
294}