• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/env perl
2# Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
3#
4# Licensed under the OpenSSL license (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
9use strict;
10use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11use OpenSSL::Test::Utils;
12use TLSProxy::Proxy;
13
14my $test_name = "test_sslsigalgs";
15setup($test_name);
16
17plan skip_all => "TLSProxy isn't usable on $^O"
18    if $^O =~ /^(VMS)$/;
19
20plan skip_all => "$test_name needs the dynamic engine feature enabled"
21    if disabled("engine") || disabled("dynamic-engine");
22
23plan skip_all => "$test_name needs the sock feature enabled"
24    if disabled("sock");
25
26plan 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';
30my $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
37use 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";
58plan tests => 26;
59ok(TLSProxy::Message->success, "Default sigalgs");
60my $testtype;
61
62SKIP: {
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
110SKIP: {
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
122SKIP: {
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
136SKIP: {
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 should succeed at
142    #         security level 1
143    $proxy->clear();
144    $testtype = NO_SIG_ALGS_EXT;
145    $proxy->clientflags("-no_tls1_3 -cipher DEFAULT\@SECLEVEL=1");
146    $proxy->ciphers("ECDHE-RSA-AES128-SHA\@SECLEVEL=1");
147    $proxy->start();
148    ok(TLSProxy::Message->success, "No TLSv1.2 sigalgs seclevel 1");
149
150    #Test 11: Sending no sig algs extension in TLSv1.2 should fail at security
151    #         level 2 since it will try to use SHA1. Testing client at level 1,
152    #         server level 2.
153    $proxy->clear();
154    $testtype = NO_SIG_ALGS_EXT;
155    $proxy->clientflags("-tls1_2 -cipher DEFAULT\@SECLEVEL=1");
156    $proxy->ciphers("DEFAULT\@SECLEVEL=2");
157    $proxy->start();
158    ok(TLSProxy::Message->fail, "No TLSv1.2 sigalgs server seclevel 2");
159
160    #Test 12: Sending no sig algs extension in TLSv1.2 should fail at security
161    #         level 2 since it will try to use SHA1. Testing client at level 2,
162    #         server level 1.
163    $proxy->clear();
164    $testtype = NO_SIG_ALGS_EXT;
165    $proxy->clientflags("-tls1_2 -cipher DEFAULT\@SECLEVEL=2");
166    $proxy->ciphers("DEFAULT\@SECLEVEL=1");
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, TLSv1.2 should succeed
225    $proxy->clear();
226    $testtype = NO_SIG_ALGS_EXT;
227    $proxy->clientflags("-no_tls1_3");
228    $proxy->serverflags("-cert " . srctop_file("test", "certs",
229                                               "server-ecdsa-cert.pem") .
230                        " -key " . srctop_file("test", "certs",
231                                               "server-ecdsa-key.pem")),
232    $proxy->ciphers("ECDHE-ECDSA-AES128-SHA");
233    $proxy->start();
234    ok(TLSProxy::Message->success, "No TLSv1.2 sigalgs, ECDSA");
235}
236
237my ($dsa_status, $sha1_status, $sha224_status);
238SKIP: {
239    skip "TLSv1.3 disabled", 2 if disabled("tls1_3") || disabled("dsa");
240    #Test 20: signature_algorithms with 1.3-only ClientHello
241    $testtype = PURE_SIGALGS;
242    $dsa_status = $sha1_status = $sha224_status = 0;
243    $proxy->clear();
244    $proxy->clientflags("-tls1_3");
245    $proxy->filter(\&modify_sigalgs_filter);
246    $proxy->start();
247    ok($dsa_status && $sha1_status && $sha224_status,
248       "DSA/SHA2 sigalg sent for 1.3-only ClientHello");
249
250    #Test 21: signature_algorithms with backwards compatible ClientHello
251    SKIP: {
252        skip "TLSv1.2 disabled", 1 if disabled("tls1_2");
253        $testtype = COMPAT_SIGALGS;
254        $dsa_status = $sha1_status = $sha224_status = 0;
255        $proxy->clear();
256        $proxy->filter(\&modify_sigalgs_filter);
257        $proxy->start();
258        ok($dsa_status && $sha1_status && $sha224_status,
259           "DSA sigalg not sent for compat ClientHello");
260   }
261}
262
263SKIP: {
264    skip "TLSv1.3 disabled", 3 if disabled("tls1_3");
265    #Test 22: Insert signature_algorithms_cert that match normal sigalgs
266    $testtype = SIGALGS_CERT_ALL;
267    $proxy->clear();
268    $proxy->filter(\&modify_sigalgs_cert_filter);
269    $proxy->start();
270    ok(TLSProxy::Message->success, "sigalgs_cert in TLSv1.3");
271
272    #Test 23: Insert signature_algorithms_cert that forces PKCS#1 cert
273    $testtype = SIGALGS_CERT_PKCS;
274    $proxy->clear();
275    $proxy->filter(\&modify_sigalgs_cert_filter);
276    $proxy->start();
277    ok(TLSProxy::Message->success, "sigalgs_cert in TLSv1.3 with PKCS#1 cert");
278
279    #Test 24: Insert signature_algorithms_cert that fails
280    $testtype = SIGALGS_CERT_INVALID;
281    $proxy->clear();
282    $proxy->filter(\&modify_sigalgs_cert_filter);
283    $proxy->start();
284    ok(TLSProxy::Message->fail, "No matching certificate for sigalgs_cert");
285}
286
287SKIP: {
288    skip "TLS 1.3 disabled", 2 if disabled("tls1_3");
289    #Test 25: Send an unrecognized signature_algorithms_cert
290    #        We should be able to skip over the unrecognized value and use a
291    #        valid one that appears later in the list.
292    $proxy->clear();
293    $proxy->filter(\&inject_unrecognized_sigalg);
294    $proxy->clientflags("-tls1_3");
295    # Use -xcert to get SSL_check_chain() to run in the cert_cb.  This is
296    # needed to trigger (e.g.) CVE-2020-1967
297    $proxy->serverflags("" .
298            " -xcert " . srctop_file("test", "certs", "servercert.pem") .
299            " -xkey " . srctop_file("test", "certs", "serverkey.pem") .
300            " -xchain " . srctop_file("test", "certs", "rootcert.pem"));
301    $testtype = UNRECOGNIZED_SIGALGS_CERT;
302    $proxy->start();
303    ok(TLSProxy::Message->success(), "Unrecognized sigalg_cert in ClientHello");
304
305    #Test 26: Send an unrecognized signature_algorithms
306    #        We should be able to skip over the unrecognized value and use a
307    #        valid one that appears later in the list.
308    $proxy->clear();
309    $proxy->filter(\&inject_unrecognized_sigalg);
310    $proxy->clientflags("-tls1_3");
311    $proxy->serverflags("" .
312            " -xcert " . srctop_file("test", "certs", "servercert.pem") .
313            " -xkey " . srctop_file("test", "certs", "serverkey.pem") .
314            " -xchain " . srctop_file("test", "certs", "rootcert.pem"));
315    $testtype = UNRECOGNIZED_SIGALG;
316    $proxy->start();
317    ok(TLSProxy::Message->success(), "Unrecognized sigalg in ClientHello");
318}
319
320
321
322sub sigalgs_filter
323{
324    my $proxy = shift;
325
326    # We're only interested in the initial ClientHello
327    if ($proxy->flight != 0) {
328        return;
329    }
330
331    foreach my $message (@{$proxy->message_list}) {
332        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
333            if ($testtype == NO_SIG_ALGS_EXT) {
334                $message->delete_extension(TLSProxy::Message::EXT_SIG_ALGS);
335            } else {
336                my $sigalg;
337                if ($testtype == EMPTY_SIG_ALGS_EXT) {
338                    $sigalg = pack "C2", 0x00, 0x00;
339                } elsif ($testtype == NO_KNOWN_SIG_ALGS) {
340                    $sigalg = pack "C4", 0x00, 0x02, 0xff, 0xff;
341                } elsif ($testtype == NO_PSS_SIG_ALGS) {
342                    #No PSS sig algs - just send rsa_pkcs1_sha256
343                    $sigalg = pack "C4", 0x00, 0x02, 0x04, 0x01;
344                } else {
345                    #PSS sig algs only - just send rsa_pss_rsae_sha256
346                    $sigalg = pack "C4", 0x00, 0x02, 0x08, 0x04;
347                }
348                $message->set_extension(TLSProxy::Message::EXT_SIG_ALGS, $sigalg);
349            }
350
351            $message->repack();
352        }
353    }
354}
355
356sub modify_sigalgs_filter
357{
358    my $proxy = shift;
359
360    # We're only interested in the initial ClientHello
361    return if ($proxy->flight != 0);
362
363    foreach my $message (@{$proxy->message_list}) {
364        my $ext;
365        my @algs;
366
367        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
368            if ($testtype == PURE_SIGALGS) {
369                my $ok = 1;
370                $ext = $message->extension_data->{TLSProxy::Message::EXT_SIG_ALGS};
371                @algs = unpack('S>*', $ext);
372                # unpack will unpack the length as well
373                shift @algs;
374                foreach (@algs) {
375                    if ($_ == TLSProxy::Message::SIG_ALG_DSA_SHA256
376                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA384
377                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA512
378                        || $_ == TLSProxy::Message::OSSL_SIG_ALG_DSA_SHA224
379                        || $_ == TLSProxy::Message::SIG_ALG_RSA_PKCS1_SHA1
380                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA1
381                        || $_ == TLSProxy::Message::SIG_ALG_ECDSA_SHA1) {
382                        $ok = 0;
383                    }
384                }
385                $sha1_status = $dsa_status = $sha224_status = 1 if ($ok);
386            } elsif ($testtype == COMPAT_SIGALGS) {
387                $ext = $message->extension_data->{TLSProxy::Message::EXT_SIG_ALGS};
388                @algs = unpack('S>*', $ext);
389                # unpack will unpack the length as well
390                shift @algs;
391                foreach (@algs) {
392                    if ($_ == TLSProxy::Message::SIG_ALG_DSA_SHA256
393                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA384
394                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA512) {
395                        $dsa_status = 1;
396                    }
397                    if ($_ == TLSProxy::Message::SIG_ALG_RSA_PKCS1_SHA1
398                        || $_ == TLSProxy::Message::SIG_ALG_DSA_SHA1
399                        || $_ == TLSProxy::Message::SIG_ALG_ECDSA_SHA1) {
400                        $sha1_status = 1;
401                    }
402                    if ($_ == TLSProxy::Message::OSSL_SIG_ALG_RSA_PKCS1_SHA224
403                        || $_ == TLSProxy::Message::OSSL_SIG_ALG_DSA_SHA224
404                        || $_ == TLSProxy::Message::OSSL_SIG_ALG_ECDSA_SHA224) {
405                        $sha224_status = 1;
406                    }
407                }
408            }
409        }
410    }
411}
412
413sub modify_sigalgs_cert_filter
414{
415    my $proxy = shift;
416
417    # We're only interested in the initial ClientHello
418    if ($proxy->flight != 0) {
419        return;
420    }
421
422    foreach my $message (@{$proxy->message_list}) {
423        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
424            my $sigs;
425            # two byte length at front of sigs, then two-byte sigschemes
426            if ($testtype == SIGALGS_CERT_ALL) {
427                $sigs = pack "C26", 0x00, 0x18,
428                             # rsa_pkcs_sha{256,512}  rsa_pss_rsae_sha{256,512}
429                             0x04, 0x01,  0x06, 0x01,  0x08, 0x04,  0x08, 0x06,
430                             # ed25518    ed448        rsa_pss_pss_sha{256,512}
431                             0x08, 0x07,  0x08, 0x08,  0x08, 0x09,  0x08, 0x0b,
432                             # ecdsa_secp{256,512}     rsa+sha1     ecdsa+sha1
433                             0x04, 0x03,  0x06, 0x03,  0x02, 0x01,  0x02, 0x03;
434            } elsif ($testtype == SIGALGS_CERT_PKCS) {
435                $sigs = pack "C10", 0x00, 0x08,
436                             # rsa_pkcs_sha{256,384,512,1}
437                             0x04, 0x01,  0x05, 0x01,  0x06, 0x01,  0x02, 0x01;
438            } elsif ($testtype == SIGALGS_CERT_INVALID) {
439                $sigs = pack "C4", 0x00, 0x02,
440                             # unregistered codepoint
441                             0xb2, 0x6f;
442            }
443            $message->set_extension(TLSProxy::Message::EXT_SIG_ALGS_CERT, $sigs);
444            $message->repack();
445        }
446    }
447}
448
449sub modify_cert_verify_sigalg
450{
451    my $proxy = shift;
452
453    # We're only interested in the CertificateVerify
454    if ($proxy->flight != 1) {
455        return;
456    }
457
458    foreach my $message (@{$proxy->message_list}) {
459        if ($message->mt == TLSProxy::Message::MT_CERTIFICATE_VERIFY) {
460            $message->sigalg(TLSProxy::Message::SIG_ALG_RSA_PSS_PSS_SHA256);
461            $message->repack();
462        }
463    }
464}
465
466sub inject_unrecognized_sigalg
467{
468    my $proxy = shift;
469    my $type;
470
471    # We're only interested in the initial ClientHello
472    if ($proxy->flight != 0) {
473        return;
474    }
475    if ($testtype == UNRECOGNIZED_SIGALGS_CERT) {
476        $type = TLSProxy::Message::EXT_SIG_ALGS_CERT;
477    } elsif ($testtype == UNRECOGNIZED_SIGALG) {
478        $type = TLSProxy::Message::EXT_SIG_ALGS;
479    } else {
480        return;
481    }
482
483    my $ext = pack "C8",
484        0x00, 0x06, #Extension length
485        0xfe, 0x18, #private use
486        0x04, 0x01, #rsa_pkcs1_sha256
487        0x08, 0x04; #rsa_pss_rsae_sha256;
488    my $message = ${$proxy->message_list}[0];
489    $message->set_extension($type, $ext);
490    $message->repack;
491}
492