FreeRDP
Test_x509_utils.c
1 #include <winpr/file.h>
2 #include <winpr/string.h>
3 #include "../x509_utils.h"
4 
5 typedef char* (*get_field_pr)(const X509*);
6 typedef 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 
18 static 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 
24 static 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 NULL;
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 NULL;
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 
64 static 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", 0, "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 
87 static 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 = NULL;
105 
106  if (test->status == DISABLED)
107  {
108  continue;
109  }
110 
111  result = (test->get_field ? test->get_field(certificate) : 0);
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 
132 fail:
133  X509_free(certificate);
134  return success;
135 }
136 
137 /* clang-format off */
138 /*
139 These certificates were generated with the following commands:
140 
141 openssl ecparam -name P-256 -out /tmp/p256.pem
142 openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha1_cert.pem -sha1
143 openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha256_cert.pem -sha256
144 openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha384_cert.pem -sha384
145 openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha512_cert.pem -sha512
146 
147 openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha1_cert.pem -sha1
148 openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha256_cert.pem -sha256
149 openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha384_cert.pem -sha384
150 openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha512_cert.pem -sha512
151 
152 openssl 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
153 openssl 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
154 openssl 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
155 openssl 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
156 openssl 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 
160 typedef struct
161 {
162  const char* filename;
163  WINPR_MD_TYPE expected;
164 } signature_alg_test_t;
165 
166 static 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 
188 static 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 
215 fail:
216  X509_free(certificate);
217  free(path);
218  return success;
219 }
220 
221 int Test_x509_utils(int argc, char* argv[])
222 {
223  char* cert_path = certificate_path("Test_x509_cert_info.pem");
224  int ret = 0;
225  WINPR_UNUSED(argc);
226  WINPR_UNUSED(argv);
227 
228  ret = TestCertificateFile(cert_path, certificate_tests, ARRAYSIZE(certificate_tests));
229  free(cert_path);
230  if (ret != 0)
231  return ret;
232 
233  for (size_t i = 0; i < ARRAYSIZE(signature_alg_tests); i++)
234  {
235  ret = TestSignatureAlgorithm(&signature_alg_tests[i]);
236  if (ret != 0)
237  return ret;
238  }
239 
240  return ret;
241 }