1 /* $OpenBSD: cttest.c,v 1.8 2023/04/14 14:36:13 tb Exp $ */
2 /*
3 * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <err.h>
19 #include <string.h>
20
21 #include <openssl/ct.h>
22 #include <openssl/err.h>
23 #include <openssl/pem.h>
24 #include <openssl/x509v3.h>
25
26 #ifndef CTPATH
27 #define CTPATH "."
28 #endif
29
30 char *test_ctlog_conf_file;
31 char *test_cert_file;
32 char *test_issuer_file;
33
34 const int debug = 0;
35
36 const uint8_t scts_asn1[] = {
37 0x04, 0x81, 0xf2, 0x00, 0xf0, 0x00, 0x77, 0x00,
38 0x29, 0x79, 0xbe, 0xf0, 0x9e, 0x39, 0x39, 0x21,
39 0xf0, 0x56, 0x73, 0x9f, 0x63, 0xa5, 0x77, 0xe5,
40 0xbe, 0x57, 0x7d, 0x9c, 0x60, 0x0a, 0xf8, 0xf9,
41 0x4d, 0x5d, 0x26, 0x5c, 0x25, 0x5d, 0xc7, 0x84,
42 0x00, 0x00, 0x01, 0x7d, 0x39, 0x51, 0x1f, 0x6f,
43 0x00, 0x00, 0x04, 0x03, 0x00, 0x48, 0x30, 0x46,
44 0x02, 0x21, 0x00, 0x93, 0xed, 0x3a, 0x65, 0x98,
45 0x9a, 0x85, 0xf0, 0x3b, 0x3c, 0x26, 0xf7, 0x52,
46 0x94, 0xd7, 0x92, 0x48, 0xc2, 0xc0, 0x64, 0xcb,
47 0x01, 0xf5, 0xec, 0xf7, 0x6d, 0x41, 0xe0, 0xbd,
48 0x28, 0x56, 0xad, 0x02, 0x21, 0x00, 0xc2, 0x4f,
49 0x92, 0xfb, 0xa0, 0xbb, 0xef, 0x55, 0x67, 0x80,
50 0x06, 0x10, 0x07, 0xe7, 0xb9, 0xb1, 0x96, 0xa7,
51 0xa9, 0x8b, 0xb2, 0xcb, 0xd3, 0x9c, 0x4e, 0x02,
52 0xe8, 0xdb, 0x24, 0x65, 0x1e, 0xc8, 0x00, 0x75,
53 0x00, 0x6f, 0x53, 0x76, 0xac, 0x31, 0xf0, 0x31,
54 0x19, 0xd8, 0x99, 0x00, 0xa4, 0x51, 0x15, 0xff,
55 0x77, 0x15, 0x1c, 0x11, 0xd9, 0x02, 0xc1, 0x00,
56 0x29, 0x06, 0x8d, 0xb2, 0x08, 0x9a, 0x37, 0xd9,
57 0x13, 0x00, 0x00, 0x01, 0x7d, 0x39, 0x51, 0x20,
58 0x3b, 0x00, 0x00, 0x04, 0x03, 0x00, 0x46, 0x30,
59 0x44, 0x02, 0x20, 0x26, 0xc9, 0x12, 0x28, 0x70,
60 0x2d, 0x15, 0x05, 0xa7, 0xa2, 0xea, 0x12, 0x1a,
61 0xff, 0x39, 0x36, 0x5f, 0x93, 0xdf, 0x83, 0x36,
62 0x5f, 0xed, 0x07, 0x38, 0xb8, 0x0a, 0x40, 0xe1,
63 0x8d, 0xb9, 0xfa, 0x02, 0x20, 0x61, 0xae, 0x2b,
64 0x86, 0xbd, 0x8e, 0x86, 0x65, 0x2b, 0xfb, 0x63,
65 0xe1, 0xda, 0x77, 0xb3, 0xf3, 0xc5, 0x2a, 0x32,
66 0xb8, 0x23, 0x1e, 0x7e, 0xfa, 0x7d, 0x83, 0xa5,
67 0x49, 0x00, 0xc4, 0x57, 0xb8,
68 };
69
70 const char *sct_log_id1_base64 = "KXm+8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4Q=";
71
72 const uint8_t sct_signature1[] = {
73 0x30, 0x46, 0x02, 0x21, 0x00, 0x93, 0xed, 0x3a,
74 0x65, 0x98, 0x9a, 0x85, 0xf0, 0x3b, 0x3c, 0x26,
75 0xf7, 0x52, 0x94, 0xd7, 0x92, 0x48, 0xc2, 0xc0,
76 0x64, 0xcb, 0x01, 0xf5, 0xec, 0xf7, 0x6d, 0x41,
77 0xe0, 0xbd, 0x28, 0x56, 0xad, 0x02, 0x21, 0x00,
78 0xc2, 0x4f, 0x92, 0xfb, 0xa0, 0xbb, 0xef, 0x55,
79 0x67, 0x80, 0x06, 0x10, 0x07, 0xe7, 0xb9, 0xb1,
80 0x96, 0xa7, 0xa9, 0x8b, 0xb2, 0xcb, 0xd3, 0x9c,
81 0x4e, 0x02, 0xe8, 0xdb, 0x24, 0x65, 0x1e, 0xc8
82 };
83
84 const char *sct_signature1_base64 =
85 "BAMASDBGAiEAk+06ZZiahfA7PCb3UpTXkkjCwGTLAfXs921B4L0oVq0CIQDCT5L7oLvvVWeABh"
86 "AH57mxlqepi7LL05xOAujbJGUeyA==";
87
88 const char *sct_log_id2_base64 = "b1N2rDHwMRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RM=";
89
90 const uint8_t sct_signature2[] = {
91 0x30, 0x44, 0x02, 0x20, 0x26, 0xc9, 0x12, 0x28,
92 0x70, 0x2d, 0x15, 0x05, 0xa7, 0xa2, 0xea, 0x12,
93 0x1a, 0xff, 0x39, 0x36, 0x5f, 0x93, 0xdf, 0x83,
94 0x36, 0x5f, 0xed, 0x07, 0x38, 0xb8, 0x0a, 0x40,
95 0xe1, 0x8d, 0xb9, 0xfa, 0x02, 0x20, 0x61, 0xae,
96 0x2b, 0x86, 0xbd, 0x8e, 0x86, 0x65, 0x2b, 0xfb,
97 0x63, 0xe1, 0xda, 0x77, 0xb3, 0xf3, 0xc5, 0x2a,
98 0x32, 0xb8, 0x23, 0x1e, 0x7e, 0xfa, 0x7d, 0x83,
99 0xa5, 0x49, 0x00, 0xc4, 0x57, 0xb8
100 };
101
102 const char *sct_signature2_base64 =
103 "BAMARjBEAiAmyRIocC0VBaei6hIa/zk2X5PfgzZf7Qc4uApA4Y25+gIgYa4rhr2OhmUr+2Ph2n"
104 "ez88UqMrgjHn76fYOlSQDEV7g=";
105
106 struct sct_data {
107 uint8_t version;
108 uint8_t log_id[32];
109 uint64_t timestamp;
110 size_t extensions_len;
111 int signature_nid;
112 const uint8_t *signature;
113 size_t signature_len;
114 };
115
116 const struct sct_data sct_test_data[] = {
117 {
118 .version = 0,
119 .log_id = {
120 0x29, 0x79, 0xbe, 0xf0, 0x9e, 0x39, 0x39, 0x21,
121 0xf0, 0x56, 0x73, 0x9f, 0x63, 0xa5, 0x77, 0xe5,
122 0xbe, 0x57, 0x7d, 0x9c, 0x60, 0x0a, 0xf8, 0xf9,
123 0x4d, 0x5d, 0x26, 0x5c, 0x25, 0x5d, 0xc7, 0x84,
124 },
125 .timestamp = 1637344157551LL,
126 .extensions_len = 0,
127 .signature_nid = NID_ecdsa_with_SHA256,
128 .signature = sct_signature1,
129 .signature_len = sizeof(sct_signature1),
130 },
131 {
132 .version = 0,
133 .log_id = {
134 0x6f, 0x53, 0x76, 0xac, 0x31, 0xf0, 0x31, 0x19,
135 0xd8, 0x99, 0x00, 0xa4, 0x51, 0x15, 0xff, 0x77,
136 0x15, 0x1c, 0x11, 0xd9, 0x02, 0xc1, 0x00, 0x29,
137 0x06, 0x8d, 0xb2, 0x08, 0x9a, 0x37, 0xd9, 0x13
138 },
139 .timestamp = 1637344157755LL,
140 .extensions_len = 0,
141 .signature_nid = NID_ecdsa_with_SHA256,
142 .signature = sct_signature2,
143 .signature_len = sizeof(sct_signature2),
144 },
145 };
146
147 #define N_SCT_TEST_DATA (sizeof(sct_test_data) / sizeof(*sct_test_data))
148
149 static void
hexdump(const unsigned char * buf,size_t len)150 hexdump(const unsigned char *buf, size_t len)
151 {
152 size_t i;
153
154 for (i = 1; i <= len; i++)
155 fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
156
157 if (len % 8)
158 fprintf(stderr, "\n");
159 }
160
161 static void
cert_from_file(const char * filename,X509 ** cert)162 cert_from_file(const char *filename, X509 **cert)
163 {
164 BIO *bio = NULL;
165 X509 *x;
166
167 if ((bio = BIO_new_file(filename, "r")) == NULL) {
168 ERR_print_errors_fp(stderr);
169 errx(1, "failed to create bio");
170 }
171 if ((x = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL)
172 errx(1, "failed to read PEM");
173
174 *cert = x;
175
176 BIO_free(bio);
177 }
178
179 static int
ct_compare_test_scts(STACK_OF (SCT)* scts)180 ct_compare_test_scts(STACK_OF(SCT) *scts)
181 {
182 const struct sct_data *sdt;
183 BIO *bio_err = NULL;
184 SCT *sct;
185 uint8_t *data;
186 size_t len;
187 int i;
188 int ret = 0;
189
190 bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
191
192 if (sk_SCT_num(scts) != N_SCT_TEST_DATA) {
193 fprintf(stderr, "FAIL: got %d SCTS, want %zu\n",
194 sk_SCT_num(scts), N_SCT_TEST_DATA);
195 goto failure;
196 }
197
198 for (i = 0; i < sk_SCT_num(scts); i++) {
199 sct = sk_SCT_value(scts, i);
200 sdt = &sct_test_data[i];
201
202 if (debug > 0) {
203 SCT_print(sct, bio_err, 0, NULL);
204 BIO_printf(bio_err, "\n");
205 }
206
207 if (SCT_get_version(sct) != sdt->version) {
208 fprintf(stderr, "FAIL: SCT %d - got version %u, "
209 "want %u\n", i, SCT_get_version(sct), sdt->version);
210 goto failure;
211 }
212 len = SCT_get0_log_id(sct, &data);
213 if (len != sizeof(sdt->log_id)) {
214 fprintf(stderr, "FAIL: SCT %d - got version %u, "
215 "want %u\n", i, SCT_get_version(sct), sdt->version);
216 goto failure;
217 }
218 if (memcmp(data, sdt->log_id, len) != 0) {
219 fprintf(stderr, "FAIL: SCT %d - log ID differs\n", i);
220 fprintf(stderr, "Got:\n");
221 hexdump(data, len);
222 fprintf(stderr, "Want:\n");
223 hexdump(sdt->log_id, sizeof(sdt->log_id));
224 goto failure;
225 }
226 if (SCT_get_timestamp(sct) != sdt->timestamp) {
227 fprintf(stderr, "FAIL: SCT %d - got timestamp %llu, "
228 "want %llu\n", i,
229 (unsigned long long)SCT_get_timestamp(sct),
230 (unsigned long long)sdt->timestamp);
231 goto failure;
232 }
233 if (SCT_get_signature_nid(sct) != sdt->signature_nid) {
234 fprintf(stderr, "FAIL: SCT %d - got signature_nid %d, "
235 "want %d\n", i, SCT_get_signature_nid(sct),
236 sdt->signature_nid);
237 goto failure;
238 }
239 len = SCT_get0_extensions(sct, &data);
240 if (len != sdt->extensions_len) {
241 fprintf(stderr, "FAIL: SCT %d - got extensions with "
242 "length %zu, want %zu\n", i, len,
243 sdt->extensions_len);
244 goto failure;
245 }
246 len = SCT_get0_signature(sct, &data);
247 if (len != sdt->signature_len) {
248 fprintf(stderr, "FAIL: SCT %d - got signature with "
249 "length %zu, want %zu\n", i, len,
250 sdt->signature_len);
251 goto failure;
252 }
253 if (memcmp(data, sdt->signature, len) != 0) {
254 fprintf(stderr, "FAIL: SCT %d - signature differs\n",
255 i);
256 fprintf(stderr, "Got:\n");
257 hexdump(data, len);
258 fprintf(stderr, "Want:\n");
259 hexdump(sdt->signature, sdt->signature_len);
260 goto failure;
261 }
262 }
263
264 ret = 1;
265
266 failure:
267 BIO_free(bio_err);
268
269 return ret;
270 }
271
272 static int
ct_cert_test(void)273 ct_cert_test(void)
274 {
275 X509 *cert = NULL;
276 X509_EXTENSION *ext;
277 STACK_OF(SCT) *scts = NULL;
278 int idx;
279 int failed = 1;
280
281 cert_from_file(test_cert_file, &cert);
282
283 if ((idx = X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1)) == -1) {
284 fprintf(stderr, "FAIL: failed to find SCTs\n");
285 goto failure;
286 }
287 if ((ext = X509_get_ext(cert, idx)) == NULL) {
288 fprintf(stderr, "FAIL: failed to get SCT extension\n");
289 goto failure;
290 }
291 if ((scts = X509V3_EXT_d2i(ext)) == NULL) {
292 fprintf(stderr, "FAIL: failed to decode SCTs\n");
293 ERR_print_errors_fp(stderr);
294 goto failure;
295 }
296
297 if (!ct_compare_test_scts(scts))
298 goto failure;
299
300 failed = 0;
301
302 failure:
303 SCT_LIST_free(scts);
304 X509_free(cert);
305
306 return failed;
307 }
308
309 static int
ct_sct_test(void)310 ct_sct_test(void)
311 {
312 STACK_OF(SCT) *scts = NULL;
313 const uint8_t *p;
314 uint8_t *data = NULL;
315 int len;
316 int failed = 1;
317
318 p = scts_asn1;
319 if ((scts = d2i_SCT_LIST(NULL, &p, sizeof(scts_asn1))) == NULL) {
320 fprintf(stderr, "FAIL: failed to decode SCTS from ASN.1\n");
321 ERR_print_errors_fp(stderr);
322 goto failure;
323 }
324
325 if (!ct_compare_test_scts(scts))
326 goto failure;
327
328 data = NULL;
329 if ((len = i2d_SCT_LIST(scts, &data)) <= 0) {
330 fprintf(stderr, "FAIL: failed to encode SCTS to ASN.1\n");
331 ERR_print_errors_fp(stderr);
332 goto failure;
333 }
334 if (len != sizeof(scts_asn1)) {
335 fprintf(stderr, "FAIL: ASN.1 length differs - got %d, want "
336 "%zu\n", len, sizeof(scts_asn1));
337 goto failure;
338 }
339 if (memcmp(data, scts_asn1, len) != 0) {
340 fprintf(stderr, "FAIL: ASN.1 for SCTS differs\n");
341 fprintf(stderr, "Got:\n");
342 hexdump(data, len);
343 fprintf(stderr, "Want:\n");
344 hexdump(scts_asn1, sizeof(scts_asn1));
345 goto failure;
346 }
347
348 failed = 0;
349
350 failure:
351 SCT_LIST_free(scts);
352 free(data);
353
354 return failed;
355 }
356
357 static int
ct_sct_base64_test(void)358 ct_sct_base64_test(void)
359 {
360 SCT *sct1 = NULL, *sct2 = NULL;
361 STACK_OF(SCT) *scts = NULL;
362 int failed = 1;
363
364 if ((sct1 = SCT_new_from_base64(SCT_VERSION_V1, sct_log_id1_base64,
365 CT_LOG_ENTRY_TYPE_X509, 1637344157551LL, "",
366 sct_signature1_base64)) == NULL) {
367 fprintf(stderr, "FAIL: SCT_new_from_base64() failed\n");
368 ERR_print_errors_fp(stderr);
369 goto failure;
370 }
371 if ((sct2 = SCT_new_from_base64(SCT_VERSION_V1, sct_log_id2_base64,
372 CT_LOG_ENTRY_TYPE_X509, 1637344157755LL, "",
373 sct_signature2_base64)) == NULL) {
374 fprintf(stderr, "FAIL: SCT_new_from_base64() failed\n");
375 ERR_print_errors_fp(stderr);
376 goto failure;
377 }
378 if ((scts = sk_SCT_new_null()) == NULL)
379 goto failure;
380 if (!sk_SCT_push(scts, sct1))
381 goto failure;
382 sct1 = NULL;
383 if (!sk_SCT_push(scts, sct2))
384 goto failure;
385 sct2 = NULL;
386
387 if (!ct_compare_test_scts(scts))
388 goto failure;
389
390 failed = 0;
391
392 failure:
393 SCT_LIST_free(scts);
394 SCT_free(sct1);
395 SCT_free(sct2);
396
397 return failed;
398 }
399
400 static int
ct_sct_verify_test(void)401 ct_sct_verify_test(void)
402 {
403 STACK_OF(SCT) *scts = NULL;
404 CT_POLICY_EVAL_CTX *ct_policy = NULL;
405 CTLOG_STORE *ctlog_store = NULL;
406 X509 *cert = NULL, *issuer = NULL;
407 const uint8_t *p;
408 SCT *sct;
409 int failed = 1;
410
411 cert_from_file(test_cert_file, &cert);
412 cert_from_file(test_issuer_file, &issuer);
413
414 if ((ctlog_store = CTLOG_STORE_new()) == NULL)
415 goto failure;
416 if (!CTLOG_STORE_load_file(ctlog_store, test_ctlog_conf_file))
417 goto failure;
418
419 if ((ct_policy = CT_POLICY_EVAL_CTX_new()) == NULL)
420 goto failure;
421
422 CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE(ct_policy, ctlog_store);
423 CT_POLICY_EVAL_CTX_set_time(ct_policy, 1641393117000LL);
424
425 if (!CT_POLICY_EVAL_CTX_set1_cert(ct_policy, cert))
426 goto failure;
427 if (!CT_POLICY_EVAL_CTX_set1_issuer(ct_policy, issuer))
428 goto failure;
429
430 p = scts_asn1;
431 if ((scts = d2i_SCT_LIST(NULL, &p, sizeof(scts_asn1))) == NULL) {
432 fprintf(stderr, "FAIL: failed to decode SCTS from ASN.1\n");
433 ERR_print_errors_fp(stderr);
434 goto failure;
435 }
436 sct = sk_SCT_value(scts, 0);
437
438 if (!SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_PRECERT))
439 goto failure;
440 if (!SCT_validate(sct, ct_policy)) {
441 fprintf(stderr, "FAIL: SCT_validate failed\n");
442 ERR_print_errors_fp(stderr);
443 goto failure;
444 }
445
446 failed = 0;
447
448 failure:
449 CT_POLICY_EVAL_CTX_free(ct_policy);
450 CTLOG_STORE_free(ctlog_store);
451 X509_free(cert);
452 X509_free(issuer);
453 SCT_LIST_free(scts);
454
455 return failed;
456 }
457
458 int
main(int argc,char ** argv)459 main(int argc, char **argv)
460 {
461 const char *ctpath = CTPATH;
462 int failed = 0;
463
464 if (argc > 2) {
465 fprintf(stderr, "usage %s [ctpath]\n", argv[0]);
466 exit(1);
467 }
468 if (argc == 2)
469 ctpath = argv[1];
470
471 if (asprintf(&test_cert_file, "%s/%s", ctpath,
472 "libressl.org.crt") == -1)
473 errx(1, "asprintf test_cert_file");
474 if (asprintf(&test_issuer_file, "%s/%s", ctpath,
475 "letsencrypt-r3.crt") == -1)
476 errx(1, "asprintf test_issuer_file");
477 if (asprintf(&test_ctlog_conf_file, "%s/%s", ctpath,
478 "ctlog.conf") == -1)
479 errx(1, "asprintf test_ctlog_conf_file");
480
481 failed |= ct_cert_test();
482 failed |= ct_sct_test();
483 failed |= ct_sct_base64_test();
484 failed |= ct_sct_verify_test();
485
486 free(test_cert_file);
487 free(test_issuer_file);
488 free(test_ctlog_conf_file);
489
490 return (failed);
491 }
492