File: rfc_handshake_tls13.cry

package info (click to toggle)
aws-crt-python 0.16.8%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,328 kB
  • sloc: ansic: 330,743; python: 18,949; makefile: 6,271; sh: 3,712; asm: 754; cpp: 699; ruby: 208; java: 77; perl: 73; javascript: 46; xml: 11
file content (383 lines) | stat: -rw-r--r-- 16,818 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
////////////////////////////////////////////////////////////////////////////
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
//  http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
// This module provides a specification of the TLS 1.3 handshake message
// state machine according to RFC 8446.
////////////////////////////////////////////////////////////////////////////

module rfc_handshake_tls13 where

import s2n_handshake_io

/**
 * True if the given Parameters do not match the given connection, or if
 * the sequence of handshake messages produced by s2n matches the sequence
 * produced by the cryptol RFC implementation in this file.
 *
 * Why is this True if the Parameters don't match the connection? We prove
 * this function for all possible connection and Parameter inputs. Inputs
 * where the connection doesn't match the Parameters should be ignored as
 * invalid instead of causing the proof to fail. This is basically equivalent
 * to generating the Parameters from the connection, except theoretically
 * one connection could correspond to multiple Parameters.
 */
tls13rfcSimulatesS2N : {len} (fin len, len >= 1) => connection -> Parameters -> Bit
tls13rfcSimulatesS2N conn params =
    (initial_connection conn /\ connectionParameters conn params) ==>
        tracesMatchExcludingWaits
            (map rfc2S2N (doHandshake`{len} params))
            (traceS2N`{len} conn)

type MessageType = [4]
(applicationData        : MessageType) = 0
(clientHello            : MessageType) = 1
(helloRetryRequest      : MessageType) = 2
(serverHello            : MessageType) = 3
(encryptedExtensions    : MessageType) = 4
(certificateRequest     : MessageType) = 5
(certificate            : MessageType) = 6
(certificateVerify      : MessageType) = 7
(finished               : MessageType) = 8
(endOfEarlyData         : MessageType) = 9
(changeCipherSpec       : MessageType) = 10
(error                  : MessageType) = 12

type Sender = [2]
(server   : Sender) = 0
(client   : Sender) = 1
(both     : Sender) = 2

type Message = {
    messageType : MessageType,
    sender : Sender
}

type Parameters = {
    compat_mode : [2],
    psk_mode : Bit,
    retry : Bit,
    client_auth : Bit,
    no_client_cert : Bit,
    zero_rtt : Bit,
    early_ccs : Bit
}

/**
 * True if the given Parameters are equivalent to the given s2n connection.
 * As features are added to s2n, update the conn_* variables to indicate
 * whether the feature is active for a given connection.
 */
connectionParameters : connection -> Parameters -> Bit
connectionParameters conn params = params.psk_mode == conn_psk_mode
                                   /\ params.retry == conn_retry
                                   /\ params.client_auth == (conn_client_auth /\ ~params.psk_mode)
                                   /\ params.no_client_cert == (conn_no_client_cert /\ params.client_auth)
                                   /\ params.zero_rtt == (conn_zero_rtt /\ params.psk_mode)
                                   /\ params.compat_mode ==
                                        [ when_middlebox_compat (conn.mode != S2N_CLIENT)
                                        , when_middlebox_compat (conn.mode == S2N_CLIENT) ]
                                   /\ params.early_ccs == conn_early_ccs
                                   /\ conn.actual_protocol_version == S2N_TLS13
  where conn_psk_mode =         ~conn.chosen_psk_null
        conn_retry =            (conn.handshake.handshake_type && HELLO_RETRY_REQUEST) != zero
        conn_client_auth =      conn.client_auth_flag
        conn_no_client_cert =   conn.no_client_cert
        conn_early_ccs =        (conn.handshake.handshake_type && EARLY_CLIENT_CCS) != zero
        conn_zero_rtt =         (conn.early_data_state == S2N_EARLY_DATA_ACCEPTED)

        conn_middlebox_compat   = ~conn.quic_enabled \/ ((conn.handshake.handshake_type && MIDDLEBOX_COMPAT) != zero)
        when_middlebox_compat b = conn_middlebox_compat /\ b

type Action = {
    message : Message,
    valid : Bit
}

type StateId = [5]
type State = {
    actions : [5]Action,
    id : StateId,
    next : StateId
}

/**
 * Represents the sequence of the first n handshake messages for given
 * Parameters.
 *
 * Procedural Translation: The handshake starts as n applicationData
 * messages and index==0. For the first n States in the infinite series
 * of states returned by 'stateMachine', loop over the state's list of
 * actions. While index < n, for any Action with valid==True, add the
 * Action's message to the handshake at 'index' and then increment 'index'.
 */
doHandshake : {n} (fin n, n >= 1) => Parameters -> [n]Message
doHandshake params = (foldl doState initialHandshake (take`{n} (stateMachine params))).messages
  where doState hs state = foldl doAction hs state.actions
        doAction hs action = if hs.index < `n /\ action.valid
                             then updateHandshake hs action.message
                             else hs
        updateHandshake hs value = {
            messages = update hs.messages hs.index value,
            index = hs.index + 1
        }
        initialHandshake = {
            messages = repeat { messageType = applicationData, sender = both },
            index = 0:[width n]
        }

/*
 * State machine based on RFC 8446, specifically:
 * - The basic state machines in Appendix A
 * - The middlebox compatability rules in Appendix D.4
 *
 * "*" -> Indicates optional or situation-dependent
 *        messages that are not always sent.
 *
 *                 +------------------+
 *                 |CLIENT_START_STATE|
 *                 |------------------|
 *                 |clientHello (C)   |
 *                 |* CCS (C)         |
 *                 +------------------+
 *                      |        |
 *                      |        | if retry
 *                      |        v
 *                      |    +---------------------+
 *                      |    |  HELLO_RETRY_STATE  |
 *         if not retry |    |---------------------|
 *                      |    |helloRetryRequest (S)|
 *                      |    |* CCS (S)            |
 *                      |    |* CCS (C)            |
 *                      |    |clientHello (C)      |
 *                      |    +---------------------+
 *                      |        |
 *                      v        v
 *              +-----------------------+
 *              |  SERVER_HELLO_STATE   |
 *              |-----------------------|
 *              |serverHello (S)        |
 *              |* CCS (S)              |
 *              |encryptedExtensions (S)|
 *              +-----------------------+
 *                      |        |
 *                      |        | if not psk
 *                      |        v
 *                      |    +------------------------+
 *                      |    |    SERVER_AUTH_STATE   |
 *               if psk |    |------------------------|
 *                      |    |* certificateRequest (S)|
 *                      |    |certificate (S)         |
 *                      |    |certificateVerify (S)   |
 *                      |    +------------------------+
 *                      |        |
 *                      v        v
 *                +--------------------+
 *                |SERVER_FINISH_STATE |
 *                |--------------------|
 *                |finished (S)        |
 *                |* CCS (C)           |
 *                |* endOfEarlyData (C)|
 *                +--------------------+
 *                      |        |
 *                      |        | if client_auth
 *                      |        v
 *                      |    +-----------------------+
 *                      |    |   CLIENT_AUTH_STATE   |
 *   if not client_auth |    |-----------------------|
 *                      |    |certificate (C)        |
 *                      |    |* certificateVerify (C)|
 *                      |    +-----------------------+
 *                      |        |
 *                      v        v
 *               +---------------------+
 *               |CLIENT_FINISHED_STATE|
 *               |---------------------|
 *               |finished (C)         |
 *               +---------------------+
 *                          |
 *                          v
 *                +-------------------+
 *                |     FINISHED      |
 *                |-------------------|<---+
 *                |applicationData (B)|    |
 *                +---------+---------+    |
 *                          |              |
 *                          +--------------+
 */
/**
 * Represents the infinite sequence of states that make up the state
 * machine for given Parameters.
 *
 * Procedural Translation: Starting with CLIENT_START_STATE, add the
 * State associated with the 'next' field to the sequence and then
 * repeat for that State. Eventually, we will reach FINISHED_STATE and
 * loop on it indefinitely.
 */
stateMachine : Parameters -> [inf]State
stateMachine params = iterate (\s -> stateFor s.next) CLIENT_START_STATE
  where stateFor id = find (\s -> s.id == id) ERROR_STATE states
        states = [
            CLIENT_START_STATE, HELLO_RETRY_STATE, SERVER_HELLO_STATE,
            SERVER_AUTH_STATE, SERVER_FINISH_STATE, CLIENT_AUTH_STATE,
            CLIENT_FINISH_STATE, FINISHED_STATE
        ]

        state : {n} (fin n, n <= 5) => StateId -> [n]Action -> State -> State
        state id actions next = { actions = actions # zero, id = id, next = next.id }
        action mt s v = { message = { messageType = mt, sender = s }, valid = v }
        compatMode mode = params.compat_mode @ mode

        ERROR_STATE = state 0 [
            action error both True
        ] ERROR_STATE

        CLIENT_START_STATE = state 1 [
            action clientHello client True,
            action changeCipherSpec client (params.early_ccs /\ compatMode client)
        ] (if params.retry then HELLO_RETRY_STATE else SERVER_HELLO_STATE)

        HELLO_RETRY_STATE = state 2 [
            action helloRetryRequest server True,
            action changeCipherSpec server (compatMode server),
            action changeCipherSpec client (~params.early_ccs /\ compatMode client),
            action clientHello client True
        ] SERVER_HELLO_STATE

        SERVER_HELLO_STATE = state 3 [
            action serverHello server True,
            action changeCipherSpec server (~params.retry /\ compatMode server),
            action encryptedExtensions server True
        ] (if params.psk_mode then SERVER_FINISH_STATE else SERVER_AUTH_STATE)

        SERVER_AUTH_STATE = state 4 [
            action certificateRequest server params.client_auth,
            action certificate server True,
            action certificateVerify server True
        ] SERVER_FINISH_STATE

        SERVER_FINISH_STATE = state 5 [
            action finished server True,
            action changeCipherSpec client (~params.retry /\ ~params.early_ccs /\ compatMode client),
            action endOfEarlyData client params.zero_rtt
        ] (if params.client_auth then CLIENT_AUTH_STATE else CLIENT_FINISH_STATE)

        CLIENT_AUTH_STATE = state 6 [
            action certificate client True,
            action certificateVerify client (~params.no_client_cert)
        ] CLIENT_FINISH_STATE

        CLIENT_FINISH_STATE = state 7 [
            action finished client True
        ]  FINISHED_STATE

        FINISHED_STATE = state 8 [
            action applicationData both True
        ] FINISHED_STATE

/**
 * The s2n 'handshake_action' that corresponds to the Message produced
 * by this cryptol implementation. Necessary to compare results from the
 * s2n and RFC cryptol implementations.
 */
rfc2S2N : Message -> handshake_action
rfc2S2N msg = mkAct recordType messageType writer
  where recordType = if msg.messageType == changeCipherSpec then TLS_CHANGE_CIPHER_SPEC
                     |  msg.messageType == applicationData then TLS_APPLICATION_DATA
                     |  msg.messageType == error then TLS_ALERT
                     else TLS_HANDSHAKE
        messageType = if recordType != TLS_HANDSHAKE then noMessageType
                      |  msg.messageType == clientHello then TLS_CLIENT_HELLO
                      |  msg.messageType == serverHello then TLS_SERVER_HELLO
                      |  msg.messageType == helloRetryRequest then TLS_SERVER_HELLO
                      |  msg.messageType == encryptedExtensions then TLS_ENCRYPTED_EXTENSIONS
                      |  msg.messageType == certificateRequest then TLS_CERTIFICATE_REQ
                      |  msg.messageType == certificate then TLS_CERTIFICATE
                      |  msg.messageType == certificateVerify then TLS_CERT_VERIFY
                      |  msg.messageType == finished then TLS_FINISHED
                      |  msg.messageType == endOfEarlyData then TLS_END_OF_EARLY_DATA
                      else noMessageType
        writer = if msg.sender == server then 'S'
                 | msg.sender == client then 'C'
                 | msg.sender == both then 'B'
                 else '!'
        noMessageType = TLS_HELLO_REQUEST // HelloRequests don't exist in tls1.3

/**
 * printHandshake and s2nToWords are two helper functions for debugging cryptol
 * Take the counterexample from tls13rfcSimulatesS2N and plug the connection and 
 * parameter values into testParameters and testConnection. Run printHandshake 
 * with ascii set to on. 
 *     $ :s ascii = on
 *     $ printHandshake`{16}
 * Note with ascii on, you may see actual_protocol_version = '"'. This is because
 * decimal value 34 (hex value 22) == asci character double quotes. 
 */

testParameters : Parameters
testParameters = {
            compat_mode = [False, False],
            psk_mode = False,
            retry = False,
            client_auth = False,
            no_client_cert = False,
            zero_rtt = False,
            early_ccs = False
        }

testConnection : connection
testConnection = {
            handshake = {handshake_type = 0x00000000,
                message_number = 0x00000000},
            mode = 0x00000000,
            corked_io = False,
            corked = zero,
            is_caching_enabled = False,
            resume_from_cache = False,
            server_can_send_ocsp = False,
            key_exchange_eph = False,
            client_auth_flag = False,
            actual_protocol_version = 0x22,
            no_client_cert = True,
            early_data_state = zero,
            chosen_psk_null = True,
            quic_enabled = False,
            npn_negotiated = False
        }

type Character = [8]
type MessageName = [22]Character

printHandshake : {len} (fin len, len >= 1) => ([3]Character, [len]MessageName, [3]Character, [len]MessageName)
printHandshake = ("rfc", map s2nToWords (map rfc2S2N (doHandshake`{len} params)),
                  "s2n", map s2nToWords (traceS2N`{len} conn))
  where (params : Parameters) = testParameters
        (conn : connection) = testConnection
 
s2nToWords : handshake_action -> MessageName
s2nToWords action = name # [ '(', action.writer, ')']
  where padded : {a, b} (fin a, fin b) => [b]Character -> [a+b]Character
        padded msg = repeat ' ' # msg
        name : [19]Character
        name = if action.record_type == TLS_CHANGE_CIPHER_SPEC then padded    "CCS"
               |  action.record_type == TLS_APPLICATION_DATA then padded      "Data"
               |  action.message_type == TLS_CLIENT_HELLO then padded         "ClientHello"
               |  action.message_type == TLS_SERVER_HELLO then padded         "ServerHello"
               |  action.message_type == TLS_ENCRYPTED_EXTENSIONS then padded "EncryptedExtensions"
               |  action.message_type == TLS_CERTIFICATE_REQ then padded      "CertRequest"
               |  action.message_type == TLS_CERTIFICATE then padded          "Cert"
               |  action.message_type == TLS_CERT_VERIFY then padded          "CertVerify"
               |  action.message_type == TLS_FINISHED then padded             "Finished"
               |  action.message_type == TLS_END_OF_EARLY_DATA then padded    "EndOfEarlyData"
               else padded                                                    "Error"