Tests for parsing and printing certificates containing SCTs
[openssl.git] / test / ct_test.c
1 /*
2  * Tests the Certificate Transparency public and internal APIs.
3  *
4  * Author:    Rob Percival (robpercival@google.com)
5  * Date:        2016-01-26
6  *
7  * ====================================================================
8  * Copyright (c) 2016 The OpenSSL Project.    All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *        notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *        notice, this list of conditions and the following disclaimer in
19  *        the documentation and/or other materials provided with the
20  *        distribution.
21  *
22  * 3. All advertising materials mentioning features or use of this
23  *        software must display the following acknowledgment:
24  *        "This product includes software developed by the OpenSSL Project
25  *        for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
26  *
27  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
28  *        endorse or promote products derived from this software without
29  *        prior written permission. For written permission, please contact
30  *        licensing@OpenSSL.org.
31  *
32  * 5. Products derived from this software may not be called "OpenSSL"
33  *        nor may "OpenSSL" appear in their names without prior written
34  *        permission of the OpenSSL Project.
35  *
36  * 6. Redistributions of any form whatsoever must retain the following
37  *        acknowledgment:
38  *        "This product includes software developed by the OpenSSL Project
39  *        for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT AS IS'' AND ANY
42  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44  * PURPOSE ARE DISCLAIMED.    IN NO EVENT SHALL THE OpenSSL PROJECT OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
48  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
52  * OF THE POSSIBILITY OF SUCH DAMAGE.
53  * ====================================================================
54  */
55
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60
61 #include "crypto/include/internal/ct_int.h"
62 #include "openssl/err.h"
63 #include "openssl/safestack.h"
64 #include "openssl/ssl.h"
65 #include "openssl/x509.h"
66 #include "openssl/x509v3.h"
67 #include "testutil.h"
68
69 #if !defined(OPENSSL_NO_CT) && !defined(OPENSSL_NO_UNIT_TEST)
70
71 /* Used when declaring buffers to read text files into */
72 #define CT_TEST_MAX_FILE_SIZE 8096
73
74 typedef struct ct_test_fixture {
75     const char *test_case_name;
76     /* Set the following to test handling of SCTs in X509 certificates */
77     const char *certificate_file_path;
78     size_t expected_sct_count;
79     /* Set the following to test handling of SCTs in TLS format */
80     const uint8_t *tls_sct;
81     size_t tls_sct_len;
82     const SCT *sct;
83     /*
84      * A file to load the expected SCT text from.
85      * This text will be compared to the actual text output during the test.
86      * A maximum of |CT_TEST_MAX_FILE_SIZE| bytes will be read of this file.
87      */
88     const char *sct_text_file_path;
89
90 } CT_TEST_FIXTURE;
91
92 static CT_TEST_FIXTURE set_up(const char *const test_case_name)
93 {
94     CT_TEST_FIXTURE fixture;
95     int setup_ok = 1;
96
97     memset(&fixture, 0, sizeof(fixture));
98     fixture.test_case_name = test_case_name;
99
100     if (!setup_ok) {
101         exit(EXIT_FAILURE);
102     }
103     return fixture;
104 }
105
106 static void tear_down(CT_TEST_FIXTURE fixture)
107 {
108     ERR_print_errors_fp(stderr);
109 }
110
111 static X509 *load_pem_cert(const char *file)
112 {
113     BIO *cert_io = BIO_new_file(file, "r");
114     X509 *cert = NULL;
115
116     if (cert_io == NULL) goto end;
117
118     cert = PEM_read_bio_X509(cert_io, NULL, NULL, NULL);
119
120 end:
121     BIO_free(cert_io);
122     return cert;
123 }
124
125 static int read_text_file(const char *path, char *buffer, int buffer_length)
126 {
127     BIO *file = BIO_new_file(path, "r");
128     int result = -1;
129
130     if (file != NULL) {
131         result = BIO_read(file, buffer, buffer_length);
132         BIO_free(file);
133     }
134
135     return result;
136 }
137
138 static int compare_sct_printout(SCT *sct,
139     const char *expected_output)
140 {
141     BIO *text_buffer = NULL;
142     char *actual_output = NULL;
143     int result = 1;
144
145     text_buffer = BIO_new(BIO_s_mem());
146     if (text_buffer == NULL) {
147         fprintf(stderr, "Unable to allocate buffer\n");
148         goto end;
149     }
150
151     SCT_print(sct, text_buffer, 0);
152
153     /* Append null terminator because we're about to use the buffer contents
154     * as a string. */
155     if (BIO_write(text_buffer, "\0", 1) != 1) {
156         fprintf(stderr, "Failed to append null terminator to SCT text\n");
157         goto end;
158     }
159
160     BIO_get_mem_data(text_buffer, &actual_output);
161     result = strcmp(actual_output, expected_output);
162
163     if (result != 0) {
164         fprintf(stderr,
165             "Expected SCT printout:\n%s\nActual SCT printout:\n%s\n",
166             expected_output, actual_output);
167     }
168
169 end:
170     BIO_free(text_buffer);
171     return result;
172 }
173
174 static int compare_extension_printout(X509_EXTENSION *extension,
175                                       const char *expected_output)
176 {
177     BIO *text_buffer = NULL;
178     char *actual_output = NULL;
179     int result = 1;
180
181     text_buffer = BIO_new(BIO_s_mem());
182     if (text_buffer == NULL) {
183         fprintf(stderr, "Unable to allocate buffer\n");
184         goto end;
185     }
186
187     if (!X509V3_EXT_print(text_buffer, extension, X509V3_EXT_DEFAULT, 0)) {
188         fprintf(stderr, "Failed to print extension\n");
189         goto end;
190     }
191
192     /* Append null terminator because we're about to use the buffer contents
193      * as a string. */
194     if (BIO_write(text_buffer, "\0", 1) != 1) {
195         fprintf(stderr, "Failed to append null terminator to extension text\n");
196         goto end;
197     }
198
199     BIO_get_mem_data(text_buffer, &actual_output);
200     result = strcmp(actual_output, expected_output);
201
202     if (result != 0) {
203         fprintf(stderr,
204                 "Expected SCT printout:\n%s\nActual SCT printout:\n%s\n",
205                 expected_output, actual_output);
206     }
207
208 end:
209     BIO_free(text_buffer);
210     return result;
211 }
212
213 static int execute_cert_test(CT_TEST_FIXTURE fixture)
214 {
215     int result = 0;
216     X509 *cert = NULL;
217     SCT *sct = NULL;
218     char expected_sct_text[CT_TEST_MAX_FILE_SIZE];
219     int sct_text_len = 0;
220
221     if (fixture.sct_text_file_path != NULL) {
222         sct_text_len = read_text_file(
223             fixture.sct_text_file_path,
224             expected_sct_text,
225             CT_TEST_MAX_FILE_SIZE - 1);
226
227         if (sct_text_len < 0) {
228             result = 1;
229             fprintf(stderr, "Test data file not found: %s\n",
230                 fixture.sct_text_file_path);
231             goto end;
232         }
233
234         expected_sct_text[sct_text_len] = '\0';
235     }
236
237     if (fixture.certificate_file_path != NULL) {
238         int sct_extension_index;
239         X509_EXTENSION *sct_extension = NULL;
240         cert = load_pem_cert(fixture.certificate_file_path);
241
242         if (cert == NULL) {
243             result = 1;
244             fprintf(stderr, "Unable to load certificate: %s\n",
245                 fixture.certificate_file_path);
246             goto end;
247         }
248
249         sct_extension_index = X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1);
250         sct_extension = X509_get_ext(cert, sct_extension_index);
251         if (fixture.expected_sct_count > 0) {
252             if (sct_extension == NULL) {
253                 result = 1;
254                 fprintf(stderr, "SCT extension not found in: %s\n",
255                     fixture.certificate_file_path);
256                 goto end;
257             }
258
259             if (fixture.sct_text_file_path) {
260                 result = compare_extension_printout(sct_extension,
261                                                     expected_sct_text);
262                 if (result != 0)
263                     goto end;
264             }
265         } else if (sct_extension != NULL) {
266             result = 1;
267             fprintf(stderr, "Expected no SCTs, but found SCT extension in: %s\n",
268                 fixture.certificate_file_path);
269             goto end;
270         }
271     }
272
273     if (fixture.tls_sct != NULL) {
274         const unsigned char *p = fixture.tls_sct;
275         unsigned char *tls_sct;
276         size_t tls_sct_len;
277         if (o2i_SCT(&sct, &p, fixture.tls_sct_len) == NULL) {
278             result = 1;
279             fprintf(stderr, "Failed to decode SCT from TLS format\n");
280             goto end;
281         }
282
283         if (fixture.sct_text_file_path) {
284             result = compare_sct_printout(sct, expected_sct_text);
285             if (result != 0)
286                 goto end;
287         }
288
289         tls_sct_len = i2o_SCT(sct, &tls_sct);
290         if (tls_sct_len != fixture.tls_sct_len ||
291             memcmp(fixture.tls_sct, tls_sct, tls_sct_len) != 0) {
292             result = 1;
293             fprintf(stderr, "Failed to encode SCT into TLS format correctly\n");
294             goto end;
295         }
296     }
297
298 end:
299     X509_free(cert);
300     SCT_free(sct);
301     return result;
302 }
303
304 #define SETUP_CT_TEST_FIXTURE() SETUP_TEST_FIXTURE(CT_TEST_FIXTURE, set_up)
305 #define EXECUTE_CT_TEST() EXECUTE_TEST(execute_cert_test, tear_down)
306
307 static int test_no_scts_in_certificate()
308 {
309     SETUP_CT_TEST_FIXTURE();
310     fixture.certificate_file_path = "certs/leaf.pem";
311     fixture.expected_sct_count = 0;
312     EXECUTE_CT_TEST();
313 }
314
315 static int test_one_sct_in_certificate()
316 {
317     SETUP_CT_TEST_FIXTURE();
318     fixture.certificate_file_path = "certs/embeddedSCTs1.pem";
319     fixture.expected_sct_count = 1;
320     fixture.sct_text_file_path = "certs/embeddedSCTs1.sct";
321     EXECUTE_CT_TEST();
322 }
323
324 static int test_multiple_scts_in_certificate()
325 {
326     SETUP_CT_TEST_FIXTURE();
327     fixture.certificate_file_path = "certs/embeddedSCTs3.pem";
328     fixture.expected_sct_count = 3;
329     fixture.sct_text_file_path = "certs/embeddedSCTs3.sct";
330     EXECUTE_CT_TEST();
331 }
332
333 static int test_decode_tls_sct()
334 {
335     SETUP_CT_TEST_FIXTURE();
336     fixture.tls_sct = (unsigned char *)
337         "\x00" /* version */
338         /* log ID */
339         "\xDF\x1C\x2E\xC1\x15\x00\x94\x52\x47\xA9\x61\x68\x32\x5D\xDC\x5C\x79"
340         "\x59\xE8\xF7\xC6\xD3\x88\xFC\x00\x2E\x0B\xBD\x3F\x74\xD7\x64"
341         "\x00\x00\x01\x3D\xDB\x27\xDF\x93" /* timestamp */
342         "\x00\x00" /* extensions length */
343         "" /* extensions */
344         "\x04\x03" /* hash and signature algorithms */
345         "\x00\x47" /* signature length */
346         "\x30\x45\x02\x20\x48\x2F\x67\x51\xAF\x35\xDB\xA6\x54\x36\xBE\x1F\xD6"
347         "\x64\x0F\x3D\xBF\x9A\x41\x42\x94\x95\x92\x45\x30\x28\x8F\xA3\xE5\xE2"
348         "\x3E\x06\x02\x21\x00\xE4\xED\xC0\xDB\x3A\xC5\x72\xB1\xE2\xF5\xE8\xAB"
349         "\x6A\x68\x06\x53\x98\x7D\xCF\x41\x02\x7D\xFE\xFF\xA1\x05\x51\x9D\x89"
350         "\xED\xBF\x08"; /* signature */
351     fixture.tls_sct_len = 118;
352     fixture.sct_text_file_path = "ct/tls1.sct";
353     EXECUTE_CT_TEST();
354 }
355
356 static int test_encode_tls_sct()
357 {
358     SETUP_CT_TEST_FIXTURE();
359
360     SCT *sct = SCT_new();
361     SCT_set_version(sct, 0);
362     SCT_set1_log_id(sct, (unsigned char *)
363         "\xDF\x1C\x2E\xC1\x15\x00\x94\x52\x47\xA9\x61\x68\x32\x5D\xDC\x5C\x79"
364         "\x59\xE8\xF7\xC6\xD3\x88\xFC\x00\x2E\x0B\xBD\x3F\x74\xD7\x64", 32);
365     SCT_set_timestamp(sct, 1);
366     SCT_set1_extensions(sct, (unsigned char *)"", 0);
367     SCT_set_signature_nid(sct, NID_ecdsa_with_SHA256);
368     SCT_set1_signature(sct, (unsigned char *)
369         "\x45\x02\x20\x48\x2F\x67\x51\xAF\x35\xDB\xA6\x54\x36\xBE"
370         "\x1F\xD6\x64\x0F\x3D\xBF\x9A\x41\x42\x94\x95\x92\x45\x30\x28\x8F\xA3"
371         "\xE5\xE2\x3E\x06\x02\x21\x00\xE4\xED\xC0\xDB\x3A\xC5\x72\xB1\xE2\xF5"
372         "\xE8\xAB\x6A\x68\x06\x53\x98\x7D\xCF\x41\x02\x7D\xFE\xFF\xA1\x05\x51"
373         "\x9D\x89\xED\xBF\x08", 71);
374     fixture.sct = sct;
375     fixture.sct_text_file_path = "ct/tls1.sct";
376     EXECUTE_CT_TEST();
377
378     SCT_free(sct);
379 }
380
381 int main(int argc, char *argv[])
382 {
383     int result = 0;
384
385     ADD_TEST(test_no_scts_in_certificate);
386     ADD_TEST(test_one_sct_in_certificate);
387     ADD_TEST(test_multiple_scts_in_certificate);
388     ADD_TEST(test_decode_tls_sct);
389     ADD_TEST(test_encode_tls_sct);
390
391     result = run_tests(argv[0]);
392     ERR_print_errors_fp(stderr);
393
394     return result;
395 }
396
397 #else /* OPENSSL_NO_CT */
398
399 int main(int argc, char* argv[])
400 {
401     return EXIT_SUCCESS;
402 }
403
404 #endif /* OPENSSL_NO_CT */