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