xref: /openbsd/regress/lib/libcrypto/ct/cttest.c (revision db8e9f93)
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