VMS: Redefine _XOPEN_SOURCE_EXTENDED with the value 1
[openssl.git] / test / recipes / 70-test_sslsigalgs.t
1 #! /usr/bin/env perl
2 # Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the Apache License 2.0 (the "License").  You may not use
5 # this file except in compliance with the License.  You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
8
9 use strict;
10 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11 use OpenSSL::Test::Utils;
12 use TLSProxy::Proxy;
13
14 my $test_name = "test_sslsigalgs";
15 setup($test_name);
16
17 plan skip_all => "TLSProxy isn't usable on $^O"
18     if $^O =~ /^(VMS)$/;
19
20 plan skip_all => "$test_name needs the dynamic engine feature enabled"
21     if disabled("engine") || disabled("dynamic-engine");
22
23 plan skip_all => "$test_name needs the sock feature enabled"
24     if disabled("sock");
25
26 plan skip_all => "$test_name needs TLS1.2 or TLS1.3 enabled"
27     if disabled("tls1_2") && disabled("tls1_3");
28
29 $ENV{OPENSSL_ia32cap} = '~0x200000200000000';
30 my $proxy = TLSProxy::Proxy->new(
31     undef,
32     cmdstr(app(["openssl"]), display => 1),
33     srctop_file("apps", "server.pem"),
34     (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
35 );
36
37 use constant {
38     NO_SIG_ALGS_EXT => 0,
39     EMPTY_SIG_ALGS_EXT => 1,
40     NO_KNOWN_SIG_ALGS => 2,
41     NO_PSS_SIG_ALGS => 3,
42     PSS_ONLY_SIG_ALGS => 4,
43     PURE_SIGALGS => 5,
44     COMPAT_SIGALGS => 6,
45     SIGALGS_CERT_ALL => 7,
46     SIGALGS_CERT_PKCS => 8,
47     SIGALGS_CERT_INVALID => 9,
48     UNRECOGNIZED_SIGALGS_CERT => 10,
49     UNRECOGNIZED_SIGALG => 11
50 };
51
52 #Note: Throughout this test we override the default ciphersuites where TLSv1.2
53 #      is expected to ensure that a ServerKeyExchange message is sent that uses
54 #      the sigalgs
55
56 #Test 1: Default sig algs should succeed
57 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
58 plan tests => 26;
59 ok(TLSProxy::Message->success, "Default sigalgs");
60 my $testtype;
61
62 SKIP: {
63     skip "TLSv1.3 disabled", 6 if disabled("tls1_3");
64
65     $proxy->filter(\&sigalgs_filter);
66
67     #Test 2: Sending no sig algs extension in TLSv1.3 should fail
68     $proxy->clear();
69     $testtype = NO_SIG_ALGS_EXT;
70     $proxy->start();
71     ok(TLSProxy::Message->fail, "No TLSv1.3 sigalgs");
72
73     #Test 3: Sending an empty sig algs extension in TLSv1.3 should fail
74     $proxy->clear();
75     $testtype = EMPTY_SIG_ALGS_EXT;
76     $proxy->start();
77     ok(TLSProxy::Message->fail, "Empty TLSv1.3 sigalgs");
78
79     #Test 4: Sending a list with no recognised sig algs in TLSv1.3 should fail
80     $proxy->clear();
81     $testtype = NO_KNOWN_SIG_ALGS;
82     $proxy->start();
83     ok(TLSProxy::Message->fail, "No known TLSv1.3 sigalgs");
84
85     #Test 5: Sending a sig algs list without pss for an RSA cert in TLSv1.3
86     #        should fail
87     $proxy->clear();
88     $testtype = NO_PSS_SIG_ALGS;
89     $proxy->start();
90     ok(TLSProxy::Message->fail, "No PSS TLSv1.3 sigalgs");
91
92     #Test 6: Sending only TLSv1.3 PSS sig algs in TLSv1.3 should succeed
93     #TODO(TLS1.3): Do we need to verify the cert to make sure its a PSS only
94     #cert in this case?
95     $proxy->clear();
96     $testtype = PSS_ONLY_SIG_ALGS;
97     $proxy->start();
98     ok(TLSProxy::Message->success, "PSS only sigalgs in TLSv1.3");
99
100     #Test 7: Modify the CertificateVerify sigalg from rsa_pss_rsae_sha256 to
101     #        rsa_pss_pss_sha256. This should fail because the public key OID
102     #        in the certificate is rsaEncryption and not rsassaPss
103     $proxy->filter(\&modify_cert_verify_sigalg);
104     $proxy->clear();
105     $proxy->start();
106     ok(TLSProxy::Message->fail,
107        "Mismatch between CertVerify sigalg and public key OID");
108 }
109
110 SKIP: {
111     skip "EC or TLSv1.3 disabled", 1
112         if disabled("tls1_3") || disabled("ec");
113     #Test 8: Sending a valid sig algs list but not including a sig type that
114     #        matches the certificate should fail in TLSv1.3.
115     $proxy->clear();
116     $proxy->clientflags("-sigalgs ECDSA+SHA256");
117     $proxy->filter(undef);
118     $proxy->start();
119     ok(TLSProxy::Message->fail, "No matching TLSv1.3 sigalgs");
120 }
121
122 SKIP: {
123     skip "EC, TLSv1.3 or TLSv1.2 disabled", 1
124         if disabled("tls1_2") || disabled("tls1_3") || disabled("ec");
125
126     #Test 9: Sending a full list of TLSv1.3 sig algs but negotiating TLSv1.2
127     #        should succeed
128     $proxy->clear();
129     $proxy->serverflags("-no_tls1_3");
130     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
131     $proxy->filter(undef);
132     $proxy->start();
133     ok(TLSProxy::Message->success, "TLSv1.3 client TLSv1.2 server");
134 }
135
136 SKIP: {
137     skip "EC or TLSv1.2 disabled", 10 if disabled("tls1_2") || disabled("ec");
138
139     $proxy->filter(\&sigalgs_filter);
140
141     #Test 10: Sending no sig algs extension in TLSv1.2 will make it use
142     #         SHA1, which is only supported at security level 0.
143     $proxy->clear();
144     $testtype = NO_SIG_ALGS_EXT;
145     $proxy->clientflags("-no_tls1_3 -cipher DEFAULT:\@SECLEVEL=0");
146     $proxy->ciphers("ECDHE-RSA-AES128-SHA:\@SECLEVEL=0");
147     $proxy->start();
148     ok(TLSProxy::Message->success, "No TLSv1.2 sigalgs seclevel 0");
149
150     #Test 11: Sending no sig algs extension in TLSv1.2 should fail at security
151     #         level 1 since it will try to use SHA1. Testing client at level 0,
152     #         server level 1.
153     $proxy->clear();
154     $testtype = NO_SIG_ALGS_EXT;
155     $proxy->clientflags("-tls1_2 -cipher DEFAULT:\@SECLEVEL=0");
156     $proxy->ciphers("DEFAULT:\@SECLEVEL=1");
157     $proxy->start();
158     ok(TLSProxy::Message->fail, "No TLSv1.2 sigalgs server seclevel 1");
159
160     #Test 12: Sending no sig algs extension in TLSv1.2 should fail at security
161     #         level 1 since it will try to use SHA1. Testing client at level 1,
162     #         server level 0.
163     $proxy->clear();
164     $testtype = NO_SIG_ALGS_EXT;
165     $proxy->clientflags("-tls1_2 -cipher DEFAULT:\@SECLEVEL=1");
166     $proxy->ciphers("DEFAULT:\@SECLEVEL=0");
167     $proxy->start();
168     ok(TLSProxy::Message->fail, "No TLSv1.2 sigalgs client seclevel 2");
169
170     #Test 13: Sending an empty sig algs extension in TLSv1.2 should fail
171     $proxy->clear();
172     $testtype = EMPTY_SIG_ALGS_EXT;
173     $proxy->clientflags("-no_tls1_3");
174     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
175     $proxy->start();
176     ok(TLSProxy::Message->fail, "Empty TLSv1.2 sigalgs");
177
178     #Test 14: Sending a list with no recognised sig algs in TLSv1.2 should fail
179     $proxy->clear();
180     $testtype = NO_KNOWN_SIG_ALGS;
181     $proxy->clientflags("-no_tls1_3");
182     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
183     $proxy->start();
184     ok(TLSProxy::Message->fail, "No known TLSv1.3 sigalgs");
185
186     #Test 15: Sending a sig algs list without pss for an RSA cert in TLSv1.2
187     #         should succeed
188     $proxy->clear();
189     $testtype = NO_PSS_SIG_ALGS;
190     $proxy->clientflags("-no_tls1_3");
191     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
192     $proxy->start();
193     ok(TLSProxy::Message->success, "No PSS TLSv1.2 sigalgs");
194
195     #Test 16: Sending only TLSv1.3 PSS sig algs in TLSv1.2 should succeed
196     $proxy->clear();
197     $testtype = PSS_ONLY_SIG_ALGS;
198     $proxy->serverflags("-no_tls1_3");
199     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
200     $proxy->start();
201     ok(TLSProxy::Message->success, "PSS only sigalgs in TLSv1.2");
202
203     #Test 17: Responding with a sig alg we did not send in TLSv1.2 should fail
204     #         We send rsa_pkcs1_sha256 and respond with rsa_pss_rsae_sha256
205     #         TODO(TLS1.3): Add a similar test to the TLSv1.3 section above
206     #         when we have an API capable of configuring the TLSv1.3 sig algs
207     $proxy->clear();
208     $testtype = PSS_ONLY_SIG_ALGS;
209     $proxy->clientflags("-no_tls1_3 -sigalgs RSA+SHA256");
210     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
211     $proxy->start();
212     ok(TLSProxy::Message->fail, "Sigalg we did not send in TLSv1.2");
213
214     #Test 18: Sending a valid sig algs list but not including a sig type that
215     #         matches the certificate should fail in TLSv1.2
216     $proxy->clear();
217     $proxy->clientflags("-no_tls1_3 -sigalgs ECDSA+SHA256");
218     $proxy->ciphers("ECDHE-RSA-AES128-SHA");
219     $proxy->filter(undef);
220     $proxy->start();
221     ok(TLSProxy::Message->fail, "No matching TLSv1.2 sigalgs");
222     $proxy->filter(\&sigalgs_filter);
223
224     #Test 19: No sig algs extension, ECDSA cert, will use SHA1,
225     #         TLSv1.2 should succeed at security level 0
226     $proxy->clear();
227     $testtype = NO_SIG_ALGS_EXT;
228     $proxy->clientflags("-no_tls1_3 -cipher DEFAULT:\@SECLEVEL=0");
229     $proxy->serverflags("-cert " . srctop_file("test", "certs",
230                                                "server-ecdsa-cert.pem") .
231                         " -key " . srctop_file("test", "certs",
232                                                "server-ecdsa-key.pem")),
233     $proxy->ciphers("ECDHE-ECDSA-AES128-SHA:\@SECLEVEL=0");
234     $proxy->start();
235     ok(TLSProxy::Message->success, "No TLSv1.2 sigalgs, ECDSA");
236 }
237
238 my ($dsa_status, $sha1_status, $sha224_status);
239 SKIP: {
240     skip "TLSv1.3 disabled", 2 if disabled("tls1_3") || disabled("dsa");
241     #Test 20: signature_algorithms with 1.3-only ClientHello
242     $testtype = PURE_SIGALGS;
243     $dsa_status = $sha1_status = $sha224_status = 0;
244     $proxy->clear();
245     $proxy->clientflags("-tls1_3");
246     $proxy->filter(\&modify_sigalgs_filter);
247     $proxy->start();
248     ok($dsa_status && $sha1_status && $sha224_status,
249        "DSA and SHA1 sigalgs not sent for 1.3-only ClientHello");
250
251     #Test 21: signature_algorithms with backwards compatible ClientHello
252     SKIP: {
253         skip "TLSv1.2 disabled", 1 if disabled("tls1_2");
254         $testtype = COMPAT_SIGALGS;
255         $dsa_status = $sha1_status = $sha224_status = 0;
256         $proxy->clear();
257         $proxy->clientflags("-cipher AES128-SHA\@SECLEVEL=0");
258         $proxy->filter(\&modify_sigalgs_filter);
259         $proxy->start();
260         ok($dsa_status && $sha1_status && $sha224_status,
261            "backwards compatible sigalg sent for compat ClientHello");
262    }
263 }
264
265 SKIP: {
266     skip "TLSv1.3 disabled", 3 if disabled("tls1_3");
267     #Test 22: Insert signature_algorithms_cert that match normal sigalgs
268     $testtype = SIGALGS_CERT_ALL;
269     $proxy->clear();
270     $proxy->filter(\&modify_sigalgs_cert_filter);
271     $proxy->start();
272     ok(TLSProxy::Message->success, "sigalgs_cert in TLSv1.3");
273
274     #Test 23: Insert signature_algorithms_cert that forces PKCS#1 cert
275     $testtype = SIGALGS_CERT_PKCS;
276     $proxy->clear();
277     $proxy->filter(\&modify_sigalgs_cert_filter);
278     $proxy->start();
279     ok(TLSProxy::Message->success, "sigalgs_cert in TLSv1.3 with PKCS#1 cert");
280
281     #Test 24: Insert signature_algorithms_cert that fails
282     $testtype = SIGALGS_CERT_INVALID;
283     $proxy->clear();
284     $proxy->filter(\&modify_sigalgs_cert_filter);
285     $proxy->start();
286     ok(TLSProxy::Message->fail, "No matching certificate for sigalgs_cert");
287 }
288
289 SKIP: {
290     skip "TLS 1.3 disabled", 2 if disabled("tls1_3");
291     #Test 25: Send an unrecognized signature_algorithms_cert
292     #        We should be able to skip over the unrecognized value and use a
293     #        valid one that appears later in the list.
294     $proxy->clear();
295     $proxy->filter(\&inject_unrecognized_sigalg);
296     $proxy->clientflags("-tls1_3");
297     # Use -xcert to get SSL_check_chain() to run in the cert_cb.  This is
298     # needed to trigger (e.g.) CVE-2020-1967
299     $proxy->serverflags("" .
300             " -xcert " . srctop_file("test", "certs", "servercert.pem") .
301             " -xkey " . srctop_file("test", "certs", "serverkey.pem") .
302             " -xchain " . srctop_file("test", "certs", "rootcert.pem"));
303     $testtype = UNRECOGNIZED_SIGALGS_CERT;
304     $proxy->start();
305     ok(TLSProxy::Message->success(), "Unrecognized sigalg_cert in ClientHello");
306
307     #Test 26: Send an unrecognized signature_algorithms
308     #        We should be able to skip over the unrecognized value and use a
309     #        valid one that appears later in the list.
310     $proxy->clear();
311     $proxy->filter(\&inject_unrecognized_sigalg);
312     $proxy->clientflags("-tls1_3");
313     $proxy->serverflags("" .
314             " -xcert " . srctop_file("test", "certs", "servercert.pem") .
315             " -xkey " . srctop_file("test", "certs", "serverkey.pem") .
316             " -xchain " . srctop_file("test", "certs", "rootcert.pem"));
317     $testtype = UNRECOGNIZED_SIGALG;
318     $proxy->start();
319     ok(TLSProxy::Message->success(), "Unrecognized sigalg in ClientHello");
320 }
321
322
323
324 sub sigalgs_filter
325 {
326     my $proxy = shift;
327
328     # We're only interested in the initial ClientHello
329     if ($proxy->flight != 0) {
330         return;
331     }
332
333     foreach my $message (@{$proxy->message_list}) {
334         if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
335             if ($testtype == NO_SIG_ALGS_EXT) {
336                 $message->delete_extension(TLSProxy::Message::EXT_SIG_ALGS);
337             } else {
338                 my $sigalg;
339                 if ($testtype == EMPTY_SIG_ALGS_EXT) {
340                     $sigalg = pack "C2", 0x00, 0x00;
341                 } elsif ($testtype == NO_KNOWN_SIG_ALGS) {
342                     $sigalg = pack "C4", 0x00, 0x02, 0xff, 0xff;
343                 } elsif ($testtype == NO_PSS_SIG_ALGS) {
344                     #No PSS sig algs - just send rsa_pkcs1_sha256
345                     $sigalg = pack "C4", 0x00, 0x02, 0x04, 0x01;
346                 } else {
347                     #PSS sig algs only - just send rsa_pss_rsae_sha256
348                     $sigalg = pack "C4", 0x00, 0x02, 0x08, 0x04;
349                 }
350                 $message->set_extension(TLSProxy::Message::EXT_SIG_ALGS, $sigalg);
351             }
352
353             $message->repack();
354         }
355     }
356 }
357
358 sub modify_sigalgs_filter
359 {
360     my $proxy = shift;
361
362     # We're only interested in the initial ClientHello
363     return if ($proxy->flight != 0);
364
365     foreach my $message (@{$proxy->message_list}) {
366         my $ext;
367         my @algs;
368
369         if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
370             if ($testtype == PURE_SIGALGS) {
371                 my $ok = 1;
372                 $ext = $message->extension_data->{TLSProxy::Message::EXT_SIG_ALGS};
373                 @algs = unpack('S>*', $ext);
374                 # unpack will unpack the length as well
375                 shift @algs;
376                 foreach (@algs) {
377                     if ($_ == TLSProxy::Message::SIG_ALG_DSA_SHA256
378                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA384
379                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA512
380                         || $_ == TLSProxy::Message::OSSL_SIG_ALG_DSA_SHA224
381                         || $_ == TLSProxy::Message::SIG_ALG_RSA_PKCS1_SHA1
382                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA1
383                         || $_ == TLSProxy::Message::SIG_ALG_ECDSA_SHA1) {
384                         $ok = 0;
385                     }
386                 }
387                 $sha1_status = $dsa_status = $sha224_status = 1 if ($ok);
388             } elsif ($testtype == COMPAT_SIGALGS) {
389                 $ext = $message->extension_data->{TLSProxy::Message::EXT_SIG_ALGS};
390                 @algs = unpack('S>*', $ext);
391                 # unpack will unpack the length as well
392                 shift @algs;
393                 foreach (@algs) {
394                     if ($_ == TLSProxy::Message::SIG_ALG_DSA_SHA256
395                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA384
396                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA512) {
397                         $dsa_status = 1;
398                     }
399                     if ($_ == TLSProxy::Message::SIG_ALG_RSA_PKCS1_SHA1
400                         || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA1
401                         || $_ == TLSProxy::Message::SIG_ALG_ECDSA_SHA1) {
402                         $sha1_status = 1;
403                     }
404                     if ($_ == TLSProxy::Message::OSSL_SIG_ALG_RSA_PKCS1_SHA224
405                         || $_ == TLSProxy::Message::OSSL_SIG_ALG_DSA_SHA224
406                         || $_ == TLSProxy::Message::OSSL_SIG_ALG_ECDSA_SHA224) {
407                         $sha224_status = 1;
408                     }
409                 }
410             }
411         }
412     }
413 }
414
415 sub modify_sigalgs_cert_filter
416 {
417     my $proxy = shift;
418
419     # We're only interested in the initial ClientHello
420     if ($proxy->flight != 0) {
421         return;
422     }
423
424     foreach my $message (@{$proxy->message_list}) {
425         if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
426             my $sigs;
427             # two byte length at front of sigs, then two-byte sigschemes
428             if ($testtype == SIGALGS_CERT_ALL) {
429                 $sigs = pack "C26", 0x00, 0x18,
430                              # rsa_pkcs_sha{256,512}  rsa_pss_rsae_sha{256,512}
431                              0x04, 0x01,  0x06, 0x01,  0x08, 0x04,  0x08, 0x06,
432                              # ed25518    ed448        rsa_pss_pss_sha{256,512}
433                              0x08, 0x07,  0x08, 0x08,  0x08, 0x09,  0x08, 0x0b,
434                              # ecdsa_secp{256,512}     rsa+sha1     ecdsa+sha1
435                              0x04, 0x03,  0x06, 0x03,  0x02, 0x01,  0x02, 0x03;
436             } elsif ($testtype == SIGALGS_CERT_PKCS) {
437                 $sigs = pack "C10", 0x00, 0x08,
438                              # rsa_pkcs_sha{256,384,512,1}
439                              0x04, 0x01,  0x05, 0x01,  0x06, 0x01,  0x02, 0x01;
440             } elsif ($testtype == SIGALGS_CERT_INVALID) {
441                 $sigs = pack "C4", 0x00, 0x02,
442                              # unregistered codepoint
443                              0xb2, 0x6f;
444             }
445             $message->set_extension(TLSProxy::Message::EXT_SIG_ALGS_CERT, $sigs);
446             $message->repack();
447         }
448     }
449 }
450
451 sub modify_cert_verify_sigalg
452 {
453     my $proxy = shift;
454
455     # We're only interested in the CertificateVerify
456     if ($proxy->flight != 1) {
457         return;
458     }
459
460     foreach my $message (@{$proxy->message_list}) {
461         if ($message->mt == TLSProxy::Message::MT_CERTIFICATE_VERIFY) {
462             $message->sigalg(TLSProxy::Message::SIG_ALG_RSA_PSS_PSS_SHA256);
463             $message->repack();
464         }
465     }
466 }
467
468 sub inject_unrecognized_sigalg
469 {
470     my $proxy = shift;
471     my $type;
472
473     # We're only interested in the initial ClientHello
474     if ($proxy->flight != 0) {
475         return;
476     }
477     if ($testtype == UNRECOGNIZED_SIGALGS_CERT) {
478         $type = TLSProxy::Message::EXT_SIG_ALGS_CERT;
479     } elsif ($testtype == UNRECOGNIZED_SIGALG) {
480         $type = TLSProxy::Message::EXT_SIG_ALGS;
481     } else {
482         return;
483     }
484
485     my $ext = pack "C8",
486         0x00, 0x06, #Extension length
487         0xfe, 0x18, #private use
488         0x04, 0x01, #rsa_pkcs1_sha256
489         0x08, 0x04; #rsa_pss_rsae_sha256;
490     my $message = ${$proxy->message_list}[0];
491     $message->set_extension($type, $ext);
492     $message->repack;
493 }