blob: 19f7efd7065f2a42ae3dcec7004ea825a2a8e51a [file] [log] [blame]
David Benjamin2e521212014-07-16 14:37:51 -04001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <stdio.h>
16
David Benjaminbb0a17c2014-09-20 15:35:39 -040017#include <openssl/err.h>
18#include <openssl/ssl.h>
19
20typedef struct {
21 int id;
22 int in_group_flag;
23} EXPECTED_CIPHER;
24
25typedef struct {
26 /* The rule string to apply. */
27 const char *rule;
28 /* The list of expected ciphers, in order, terminated with -1. */
29 const EXPECTED_CIPHER *expected;
30} CIPHER_TEST;
31
32/* Selecting individual ciphers should work. */
33static const char kRule1[] =
34 "ECDHE-ECDSA-CHACHA20-POLY1305:"
35 "ECDHE-RSA-CHACHA20-POLY1305:"
36 "ECDHE-ECDSA-AES128-GCM-SHA256:"
37 "ECDHE-RSA-AES128-GCM-SHA256";
38
39static const EXPECTED_CIPHER kExpected1[] = {
40 { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
41 { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
42 { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
43 { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
44 { -1, -1 },
45};
46
47/* + reorders selected ciphers to the end, keeping their relative
48 * order. */
49static const char kRule2[] =
50 "ECDHE-ECDSA-CHACHA20-POLY1305:"
51 "ECDHE-RSA-CHACHA20-POLY1305:"
52 "ECDHE-ECDSA-AES128-GCM-SHA256:"
53 "ECDHE-RSA-AES128-GCM-SHA256:"
54 "+aRSA";
55
56static const EXPECTED_CIPHER kExpected2[] = {
57 { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
58 { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
59 { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
60 { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
61 { -1, -1 },
62};
63
64/* ! banishes ciphers from future selections. */
65static const char kRule3[] =
66 "!aRSA:"
67 "ECDHE-ECDSA-CHACHA20-POLY1305:"
68 "ECDHE-RSA-CHACHA20-POLY1305:"
69 "ECDHE-ECDSA-AES128-GCM-SHA256:"
70 "ECDHE-RSA-AES128-GCM-SHA256";
71
72static const EXPECTED_CIPHER kExpected3[] = {
73 { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
74 { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
75 { -1, -1 },
76};
77
78/* Multiple masks can be ANDed in a single rule. */
79static const char kRule4[] = "kRSA+AESGCM+AES128";
80
81static const EXPECTED_CIPHER kExpected4[] = {
82 { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
83 { -1, -1 },
84};
85
86/* - removes selected ciphers, but preserves their order for future
87 * selections. Select AES_128_GCM, but order the key exchanges RSA,
88 * DHE_RSA, ECDHE_RSA. */
89static const char kRule5[] =
90 "ALL:-kEECDH:-kEDH:-kRSA:-ALL:"
91 "AESGCM+AES128+aRSA";
92
93static const EXPECTED_CIPHER kExpected5[] = {
94 { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
95 { TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
96 { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
97 { -1, -1 },
98};
99
100/* Unknown selectors are no-ops. */
101static const char kRule6[] =
102 "ECDHE-ECDSA-CHACHA20-POLY1305:"
103 "ECDHE-RSA-CHACHA20-POLY1305:"
104 "ECDHE-ECDSA-AES128-GCM-SHA256:"
105 "ECDHE-RSA-AES128-GCM-SHA256:"
106 "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4";
107
108static const EXPECTED_CIPHER kExpected6[] = {
109 { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
110 { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
111 { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
112 { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
113 { -1, -1 },
114};
115
116/* Square brackets specify equi-preference groups. */
117static const char kRule7[] =
118 "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:"
119 "[ECDHE-RSA-CHACHA20-POLY1305]:"
120 "ECDHE-RSA-AES128-GCM-SHA256";
121
122static const EXPECTED_CIPHER kExpected7[] = {
123 { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 1 },
124 { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
125 { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
126 { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
127 { -1, -1 },
128};
129
130/* @STRENGTH performs a stable strength-sort of the selected
131 * ciphers and only the selected ciphers. */
132static const char kRule8[] =
133 /* To simplify things, banish all but {ECDHE_RSA,RSA} x
134 * {CHACHA20,AES_256_CBC,AES_128_CBC,RC4} x SHA1. */
135 "!kEDH:!AESGCM:!3DES:!SHA256:!MD5:!SHA384:"
136 /* Order some ciphers backwards by strength. */
137 "ALL:-CHACHA20:-AES256:-AES128:-RC4:-ALL:"
138 /* Select ECDHE ones and sort them by strength. Ties should resolve
139 * based on the order above. */
140 "kEECDH:@STRENGTH:-ALL:"
141 /* Now bring back everything uses RSA. ECDHE_RSA should be first,
142 * sorted by strength. Then RSA, backwards by strength. */
143 "aRSA";
144
145static const EXPECTED_CIPHER kExpected8[] = {
146 { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 },
147 { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
148 { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 },
149 { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 },
150 { SSL3_CK_RSA_RC4_128_SHA, 0 },
151 { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
152 { TLS1_CK_RSA_WITH_AES_256_SHA, 0 },
153 { -1, -1 },
154};
155
156static CIPHER_TEST kCipherTests[] = {
157 { kRule1, kExpected1 },
158 { kRule2, kExpected2 },
159 { kRule3, kExpected3 },
160 { kRule4, kExpected4 },
161 { kRule5, kExpected5 },
162 { kRule6, kExpected6 },
163 { kRule7, kExpected7 },
164 { kRule8, kExpected8 },
165 { NULL, NULL },
166};
167
168static const char *kBadRules[] = {
169 /* Invalid brackets. */
170 "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256",
171 "RSA]",
172 "[[RSA]]",
173 /* Operators inside brackets */
174 "[+RSA]",
175 /* Unknown directive. */
176 "@BOGUS",
177 /* Empty cipher lists error at SSL_CTX_set_cipher_list. */
178 "",
179 "BOGUS",
180 /* Invalid command. */
181 "?BAR",
182 NULL,
183};
184
185static void print_cipher_preference_list(
186 struct ssl_cipher_preference_list_st *list) {
187 size_t i;
188 int in_group = 0;
189 for (i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
190 const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i);
191 if (!in_group && list->in_group_flags[i]) {
192 fprintf(stderr, "\t[\n");
193 in_group = 1;
194 }
195 fprintf(stderr, "\t");
196 if (in_group) {
197 fprintf(stderr, " ");
198 }
199 fprintf(stderr, "%s\n", SSL_CIPHER_get_name(cipher));
200 if (in_group && !list->in_group_flags[i]) {
201 fprintf(stderr, "\t]\n");
202 in_group = 0;
203 }
204 }
205}
206
207static int test_cipher_rule(CIPHER_TEST *t) {
208 int ret = 0;
209 SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
210 size_t i;
211
212 if (!SSL_CTX_set_cipher_list(ctx, t->rule)) {
213 fprintf(stderr, "Error testing cipher rule '%s'\n", t->rule);
214 BIO_print_errors_fp(stderr);
215 goto done;
216 }
217
218 /* Compare the two lists. */
219 for (i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
220 const SSL_CIPHER *cipher =
221 sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i);
222 if (t->expected[i].id != SSL_CIPHER_get_id(cipher) ||
223 t->expected[i].in_group_flag != ctx->cipher_list->in_group_flags[i]) {
224 fprintf(stderr, "Error: cipher rule '%s' evaluted to:\n", t->rule);
225 print_cipher_preference_list(ctx->cipher_list);
226 goto done;
227 }
228 }
229
230 if (t->expected[i].id != -1) {
231 fprintf(stderr, "Error: cipher rule '%s' evaluted to:\n", t->rule);
232 print_cipher_preference_list(ctx->cipher_list);
233 goto done;
234 }
235
236 ret = 1;
237done:
238 SSL_CTX_free(ctx);
239 return ret;
240}
241
242static int test_cipher_rules(void) {
243 size_t i;
244 for (i = 0; kCipherTests[i].rule != NULL; i++) {
245 if (!test_cipher_rule(&kCipherTests[i])) {
246 return 0;
247 }
248 }
249
250 for (i = 0; kBadRules[i] != NULL; i++) {
251 SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
252 if (SSL_CTX_set_cipher_list(ctx, kBadRules[i])) {
253 fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", kBadRules[i]);
254 return 0;
255 }
256 ERR_clear_error();
257 SSL_CTX_free(ctx);
258 }
259
260 return 1;
261}
David Benjamin2e521212014-07-16 14:37:51 -0400262
David Benjaminc44d2f42014-08-20 16:24:00 -0400263int main(void) {
David Benjaminbb0a17c2014-09-20 15:35:39 -0400264 SSL_library_init();
265
266 if (!test_cipher_rules()) {
267 return 1;
268 }
269
David Benjamin2e521212014-07-16 14:37:51 -0400270 printf("PASS\n");
271 return 0;
272}