1#! /usr/bin/env perl
2# Copyright 2017-2021 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
9use strict;
10use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file srctop_dir bldtop_dir/;
11use OpenSSL::Test::Utils;
12use File::Temp qw(tempfile);
13use TLSProxy::Proxy;
14use checkhandshake qw(checkhandshake @handmessages @extensions);
15
16my $test_name = "test_tls13kexmodes";
17setup($test_name);
18
19plan skip_all => "TLSProxy isn't usable on $^O"
20    if $^O =~ /^(VMS)$/;
21
22plan skip_all => "$test_name needs the dynamic engine feature enabled"
23    if disabled("engine") || disabled("dynamic-engine");
24
25plan skip_all => "$test_name needs the sock feature enabled"
26    if disabled("sock");
27
28plan skip_all => "$test_name needs TLSv1.3 enabled"
29    if disabled("tls1_3") || (disabled("ec") && disabled("dh"));
30
31plan skip_all => "$test_name needs EC enabled"
32    if disabled("ec");
33
34$ENV{OPENSSL_ia32cap} = '~0x200000200000000';
35
36
37@handmessages = (
38    [TLSProxy::Message::MT_CLIENT_HELLO,
39        checkhandshake::ALL_HANDSHAKES],
40    [TLSProxy::Message::MT_SERVER_HELLO,
41        checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
42    [TLSProxy::Message::MT_CLIENT_HELLO,
43        checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
44    [TLSProxy::Message::MT_SERVER_HELLO,
45        checkhandshake::ALL_HANDSHAKES],
46    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS,
47        checkhandshake::ALL_HANDSHAKES],
48    [TLSProxy::Message::MT_CERTIFICATE_REQUEST,
49        checkhandshake::CLIENT_AUTH_HANDSHAKE],
50    [TLSProxy::Message::MT_CERTIFICATE,
51        checkhandshake::ALL_HANDSHAKES & ~(checkhandshake::RESUME_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE)],
52    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
53        checkhandshake::ALL_HANDSHAKES & ~(checkhandshake::RESUME_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE)],
54    [TLSProxy::Message::MT_FINISHED,
55        checkhandshake::ALL_HANDSHAKES],
56    [TLSProxy::Message::MT_CERTIFICATE,
57        checkhandshake::CLIENT_AUTH_HANDSHAKE],
58    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
59        checkhandshake::CLIENT_AUTH_HANDSHAKE],
60    [TLSProxy::Message::MT_FINISHED,
61        checkhandshake::ALL_HANDSHAKES],
62    [0, 0]
63);
64
65@extensions = (
66    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
67        TLSProxy::Message::CLIENT,
68        checkhandshake::SERVER_NAME_CLI_EXTENSION],
69    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
70        TLSProxy::Message::CLIENT,
71        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
72    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
73        TLSProxy::Message::CLIENT,
74        checkhandshake::DEFAULT_EXTENSIONS],
75    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
76        TLSProxy::Message::CLIENT,
77        checkhandshake::DEFAULT_EXTENSIONS],
78    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
79        TLSProxy::Message::CLIENT,
80        checkhandshake::DEFAULT_EXTENSIONS],
81    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
82        TLSProxy::Message::CLIENT,
83        checkhandshake::ALPN_CLI_EXTENSION],
84    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
85        TLSProxy::Message::CLIENT,
86        checkhandshake::SCT_CLI_EXTENSION],
87    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
88        TLSProxy::Message::CLIENT,
89        checkhandshake::DEFAULT_EXTENSIONS],
90    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
91        TLSProxy::Message::CLIENT,
92        checkhandshake::DEFAULT_EXTENSIONS],
93    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
94        TLSProxy::Message::CLIENT,
95        checkhandshake::DEFAULT_EXTENSIONS],
96    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
97        TLSProxy::Message::CLIENT,
98        checkhandshake::DEFAULT_EXTENSIONS],
99    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
100        TLSProxy::Message::CLIENT,
101        checkhandshake::DEFAULT_EXTENSIONS],
102    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
103        TLSProxy::Message::CLIENT,
104        checkhandshake::PSK_KEX_MODES_EXTENSION],
105    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
106        TLSProxy::Message::CLIENT,
107        checkhandshake::PSK_CLI_EXTENSION],
108
109    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
110        TLSProxy::Message::SERVER,
111        checkhandshake::DEFAULT_EXTENSIONS],
112    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
113        TLSProxy::Message::SERVER,
114        checkhandshake::KEY_SHARE_HRR_EXTENSION],
115
116    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
117        TLSProxy::Message::CLIENT,
118        checkhandshake::SERVER_NAME_CLI_EXTENSION],
119    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
120        TLSProxy::Message::CLIENT,
121        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
122    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
123        TLSProxy::Message::CLIENT,
124        checkhandshake::DEFAULT_EXTENSIONS],
125    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
126        TLSProxy::Message::CLIENT,
127        checkhandshake::DEFAULT_EXTENSIONS],
128    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
129        TLSProxy::Message::CLIENT,
130        checkhandshake::DEFAULT_EXTENSIONS],
131    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
132        TLSProxy::Message::CLIENT,
133        checkhandshake::ALPN_CLI_EXTENSION],
134    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
135        TLSProxy::Message::CLIENT,
136        checkhandshake::SCT_CLI_EXTENSION],
137    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
138        TLSProxy::Message::CLIENT,
139        checkhandshake::DEFAULT_EXTENSIONS],
140    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
141        TLSProxy::Message::CLIENT,
142        checkhandshake::DEFAULT_EXTENSIONS],
143    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
144        TLSProxy::Message::CLIENT,
145        checkhandshake::DEFAULT_EXTENSIONS],
146    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
147        TLSProxy::Message::CLIENT,
148        checkhandshake::DEFAULT_EXTENSIONS],
149    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
150        TLSProxy::Message::CLIENT,
151        checkhandshake::DEFAULT_EXTENSIONS],
152    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
153        TLSProxy::Message::CLIENT,
154        checkhandshake::PSK_KEX_MODES_EXTENSION],
155    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
156        TLSProxy::Message::CLIENT,
157        checkhandshake::PSK_CLI_EXTENSION],
158
159    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
160        TLSProxy::Message::SERVER,
161        checkhandshake::DEFAULT_EXTENSIONS],
162    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
163        TLSProxy::Message::SERVER,
164        checkhandshake::KEY_SHARE_SRV_EXTENSION],
165    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_PSK,
166        TLSProxy::Message::SERVER,
167        checkhandshake::PSK_SRV_EXTENSION],
168
169    [TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_STATUS_REQUEST,
170        TLSProxy::Message::SERVER,
171        checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
172    [0,0,0,0]
173);
174
175use constant {
176    DELETE_EXTENSION => 0,
177    EMPTY_EXTENSION => 1,
178    NON_DHE_KEX_MODE_ONLY => 2,
179    DHE_KEX_MODE_ONLY => 3,
180    UNKNOWN_KEX_MODES => 4,
181    BOTH_KEX_MODES => 5
182};
183
184my $proxy = TLSProxy::Proxy->new(
185    undef,
186    cmdstr(app(["openssl"]), display => 1),
187    srctop_file("apps", "server.pem"),
188    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
189);
190
191#Test 1: First get a session
192(undef, my $session) = tempfile();
193$proxy->clientflags("-sess_out ".$session);
194$proxy->serverflags("-servername localhost");
195$proxy->sessionfile($session);
196$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
197plan tests => 11;
198ok(TLSProxy::Message->success(), "Initial connection");
199
200#Test 2: Attempt a resume with no kex modes extension. Should fail (server
201#        MUST abort handshake with pre_shared key and no psk_kex_modes)
202$proxy->clear();
203$proxy->clientflags("-sess_in ".$session);
204my $testtype = DELETE_EXTENSION;
205$proxy->filter(\&modify_kex_modes_filter);
206$proxy->start();
207ok(TLSProxy::Message->fail(), "Resume with no kex modes");
208
209#Test 3: Attempt a resume with empty kex modes extension. Should fail (empty
210#        extension is invalid)
211$proxy->clear();
212$proxy->clientflags("-sess_in ".$session);
213$testtype = EMPTY_EXTENSION;
214$proxy->start();
215ok(TLSProxy::Message->fail(), "Resume with empty kex modes");
216
217#Test 4: Attempt a resume with non-dhe kex mode only. Should resume without a
218#        key_share
219$proxy->clear();
220$proxy->clientflags("-allow_no_dhe_kex -sess_in ".$session);
221$proxy->serverflags("-allow_no_dhe_kex");
222$testtype = NON_DHE_KEX_MODE_ONLY;
223$proxy->start();
224checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
225               checkhandshake::DEFAULT_EXTENSIONS
226               | checkhandshake::PSK_KEX_MODES_EXTENSION
227               | checkhandshake::PSK_CLI_EXTENSION
228               | checkhandshake::PSK_SRV_EXTENSION,
229               "Resume with non-dhe kex mode");
230
231#Test 5: Attempt a resume with dhe kex mode only. Should resume with a key_share
232$proxy->clear();
233$proxy->clientflags("-sess_in ".$session);
234$testtype = DHE_KEX_MODE_ONLY;
235$proxy->start();
236checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
237               checkhandshake::DEFAULT_EXTENSIONS
238               | checkhandshake::PSK_KEX_MODES_EXTENSION
239               | checkhandshake::KEY_SHARE_SRV_EXTENSION
240               | checkhandshake::PSK_CLI_EXTENSION
241               | checkhandshake::PSK_SRV_EXTENSION,
242               "Resume with non-dhe kex mode");
243
244#Test 6: Attempt a resume with only unrecognised kex modes. Should not resume
245#        but rather fall back to full handshake
246$proxy->clear();
247$proxy->clientflags("-sess_in ".$session);
248$testtype = UNKNOWN_KEX_MODES;
249$proxy->start();
250checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
251               checkhandshake::DEFAULT_EXTENSIONS
252               | checkhandshake::PSK_KEX_MODES_EXTENSION
253               | checkhandshake::KEY_SHARE_SRV_EXTENSION
254               | checkhandshake::PSK_CLI_EXTENSION,
255               "Resume with unrecognized kex mode");
256
257#Test 7: Attempt a resume with both non-dhe and dhe kex mode. Should resume with
258#        a key_share
259$proxy->clear();
260$proxy->clientflags("-sess_in ".$session);
261$testtype = BOTH_KEX_MODES;
262$proxy->start();
263checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
264               checkhandshake::DEFAULT_EXTENSIONS
265               | checkhandshake::PSK_KEX_MODES_EXTENSION
266               | checkhandshake::KEY_SHARE_SRV_EXTENSION
267               | checkhandshake::PSK_CLI_EXTENSION
268               | checkhandshake::PSK_SRV_EXTENSION,
269               "Resume with non-dhe kex mode");
270
271#Test 8: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
272#        initial key_share. Should resume with a key_share following an HRR
273$proxy->clear();
274$proxy->clientflags("-sess_in ".$session);
275$proxy->serverflags("-curves P-256");
276$testtype = BOTH_KEX_MODES;
277$proxy->start();
278checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
279               checkhandshake::DEFAULT_EXTENSIONS
280               | checkhandshake::PSK_KEX_MODES_EXTENSION
281               | checkhandshake::KEY_SHARE_SRV_EXTENSION
282               | checkhandshake::KEY_SHARE_HRR_EXTENSION
283               | checkhandshake::PSK_CLI_EXTENSION
284               | checkhandshake::PSK_SRV_EXTENSION,
285               "Resume with both kex modes and HRR");
286
287#Test 9: Attempt a resume with dhe kex mode only and an unacceptable initial
288#        key_share. Should resume with a key_share following an HRR
289$proxy->clear();
290$proxy->clientflags("-sess_in ".$session);
291$proxy->serverflags("-curves P-256");
292$testtype = DHE_KEX_MODE_ONLY;
293$proxy->start();
294checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
295               checkhandshake::DEFAULT_EXTENSIONS
296               | checkhandshake::PSK_KEX_MODES_EXTENSION
297               | checkhandshake::KEY_SHARE_SRV_EXTENSION
298               | checkhandshake::KEY_SHARE_HRR_EXTENSION
299               | checkhandshake::PSK_CLI_EXTENSION
300               | checkhandshake::PSK_SRV_EXTENSION,
301               "Resume with dhe kex mode and HRR");
302
303#Test 10: Attempt a resume with both non-dhe and dhe kex mode, unacceptable
304#         initial key_share and no overlapping groups. Should resume without a
305#         key_share
306$proxy->clear();
307$proxy->clientflags("-allow_no_dhe_kex -curves P-384 -sess_in ".$session);
308$proxy->serverflags("-allow_no_dhe_kex -curves P-256");
309$testtype = BOTH_KEX_MODES;
310$proxy->start();
311checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
312               checkhandshake::DEFAULT_EXTENSIONS
313               | checkhandshake::PSK_KEX_MODES_EXTENSION
314               | checkhandshake::PSK_CLI_EXTENSION
315               | checkhandshake::PSK_SRV_EXTENSION,
316               "Resume with both kex modes, no overlapping groups");
317
318#Test 11: Attempt a resume with dhe kex mode only, unacceptable
319#         initial key_share and no overlapping groups. Should fail
320$proxy->clear();
321$proxy->clientflags("-curves P-384 -sess_in ".$session);
322$proxy->serverflags("-curves P-256");
323$testtype = DHE_KEX_MODE_ONLY;
324$proxy->start();
325ok(TLSProxy::Message->fail(), "Resume with dhe kex mode, no overlapping groups");
326
327unlink $session;
328
329sub modify_kex_modes_filter
330{
331    my $proxy = shift;
332
333    # We're only interested in the initial ClientHello
334    return if ($proxy->flight != 0);
335
336    foreach my $message (@{$proxy->message_list}) {
337        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
338            my $ext;
339
340            if ($testtype == EMPTY_EXTENSION) {
341                $ext = pack "C",
342                    0x00;       #List length
343            } elsif ($testtype == NON_DHE_KEX_MODE_ONLY) {
344                $ext = pack "C2",
345                    0x01,       #List length
346                    0x00;       #psk_ke
347            } elsif ($testtype == DHE_KEX_MODE_ONLY) {
348                $ext = pack "C2",
349                    0x01,       #List length
350                    0x01;       #psk_dhe_ke
351            } elsif ($testtype == UNKNOWN_KEX_MODES) {
352                $ext = pack "C3",
353                    0x02,       #List length
354                    0xfe,       #unknown
355                    0xff;       #unknown
356            } elsif ($testtype == BOTH_KEX_MODES) {
357                #We deliberately list psk_ke first...should still use psk_dhe_ke
358                $ext = pack "C3",
359                    0x02,       #List length
360                    0x00,       #psk_ke
361                    0x01;       #psk_dhe_ke
362            }
363
364            if ($testtype == DELETE_EXTENSION) {
365                $message->delete_extension(
366                    TLSProxy::Message::EXT_PSK_KEX_MODES);
367            } else {
368                $message->set_extension(
369                    TLSProxy::Message::EXT_PSK_KEX_MODES, $ext);
370            }
371
372            $message->repack();
373        }
374    }
375}
376