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