1eb534764bc6363a89a7fda1a00f3b1dc41ef9e6
[BearSSL] / ssl_hs_common.t0
1 \ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
2 \
3 \ Permission is hereby granted, free of charge, to any person obtaining
4 \ a copy of this software and associated documentation files (the
5 \ "Software"), to deal in the Software without restriction, including
6 \ without limitation the rights to use, copy, modify, merge, publish,
7 \ distribute, sublicense, and/or sell copies of the Software, and to
8 \ permit persons to whom the Software is furnished to do so, subject to
9 \ the following conditions:
10 \
11 \ The above copyright notice and this permission notice shall be
12 \ included in all copies or substantial portions of the Software.
13 \
14 \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 \ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 \ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 \ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18 \ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19 \ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 \ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 \ SOFTWARE.
22
23 \ ----------------------------------------------------------------------
24 \ This is the common T0 code for processing handshake messages (code that
25 \ is used by both client and server).
26
27 preamble {
28
29 #include <stddef.h>
30 #include <string.h>
31
32 #include "inner.h"
33
34 /*
35 * This macro evaluates to a pointer to the current engine context.
36 */
37 #define ENG ((br_ssl_engine_context *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
38
39 }
40
41 \ IMPLEMENTATION NOTES
42 \ ====================
43 \
44 \ This code handles all records except application data records.
45 \ Application data is accepted (incoming records, outgoing payload data)
46 \ only when the application_data flag is set, which is done at the end
47 \ of the handshake; and it is cleared whenever a renegotiation or a
48 \ closure takes place.
49 \
50 \ Incoming alerts are processed on the fly; fatal alerts terminate the
51 \ context, while warnings are ignored, except for close_notify, which
52 \ triggers the closure procedure. That procedure never returns (it ends
53 \ with an 'ERR_OK fail' call). We can thus make this processing right
54 \ into the read functions.
55 \
56 \ Specific actions from the caller (closure or renegotiation) may happen
57 \ only when jumping back into the T0 code, i.e. just after a 'co' call.
58 \ Similarly, incoming record type may change only while the caller has
59 \ control, so we need to check that type only when returning from a 'co'.
60 \
61 \ The handshake processor needs to defer back to the caller ('co') only
62 \ in one of the following situations:
63 \
64 \ -- Some handshake data is expected.
65 \
66 \ -- The handshake is finished, and application data may flow. There may
67 \ be some incoming handshake data (HelloRequest from the server). This
68 \ is the only situation where a renegotiation call won't be ignored.
69 \
70 \ -- Some change-cipher-spec data is expected.
71 \
72 \ -- An alert record is expected. Other types of incoming records will be
73 \ skipped.
74 \
75 \ -- Waiting for the currently accumulated record to be sent and the
76 \ output buffer to become free again for another record.
77
78 \ Placeholder for handling not yet implemented functionalities.
79 : NYI ( -- ! )
80 "NOT YET IMPLEMENTED!" puts cr -1 fail ;
81
82 \ Mark the context as failed with a specific error code. This also
83 \ returns control to the caller.
84 cc: fail ( err -- ! ) {
85 br_ssl_engine_fail(ENG, (int)T0_POPi());
86 T0_CO();
87 }
88
89 \ Read a byte from the context (address is offset in context).
90 cc: get8 ( addr -- val ) {
91 size_t addr = (size_t)T0_POP();
92 T0_PUSH(*((unsigned char *)ENG + addr));
93 }
94
95 \ Read a 16-bit word from the context (address is offset in context).
96 cc: get16 ( addr -- val ) {
97 size_t addr = (size_t)T0_POP();
98 T0_PUSH(*(uint16_t *)((unsigned char *)ENG + addr));
99 }
100
101 \ Read a 32-bit word from the context (address is offset in context).
102 cc: get32 ( addr -- val ) {
103 size_t addr = (size_t)T0_POP();
104 T0_PUSH(*(uint32_t *)((unsigned char *)ENG + addr));
105 }
106
107 \ Set a byte in the context (address is offset in context).
108 cc: set8 ( val addr -- ) {
109 size_t addr = (size_t)T0_POP();
110 *((unsigned char *)ENG + addr) = (unsigned char)T0_POP();
111 }
112
113 \ Set a 16-bit word in the context (address is offset in context).
114 cc: set16 ( val addr -- ) {
115 size_t addr = (size_t)T0_POP();
116 *(uint16_t *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
117 }
118
119 \ Set a 32-bit word in the context (address is offset in context).
120 cc: set32 ( val addr -- ) {
121 size_t addr = (size_t)T0_POP();
122 *(uint32_t *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
123 }
124
125 \ Define a word that evaluates as an address of a field within the
126 \ engine context. The field name (C identifier) must follow in the
127 \ source. For field 'foo', the defined word is 'addr-foo'.
128 : addr-eng:
129 next-word { field }
130 "addr-" field + 0 1 define-word
131 0 8191 "offsetof(br_ssl_engine_context, " field + ")" + make-CX
132 postpone literal postpone ; ;
133
134 addr-eng: max_frag_len
135 addr-eng: log_max_frag_len
136 addr-eng: peer_log_max_frag_len
137 addr-eng: shutdown_recv
138 addr-eng: record_type_in
139 addr-eng: record_type_out
140 addr-eng: version_in
141 addr-eng: version_out
142 addr-eng: application_data
143 addr-eng: version_min
144 addr-eng: version_max
145 addr-eng: suites_buf
146 addr-eng: suites_num
147 addr-eng: server_name
148 \ addr-eng: version
149 \ addr-eng: cipher_suite
150 addr-eng: client_random
151 addr-eng: server_random
152 \ addr-eng: session_id_len
153 \ addr-eng: session_id
154 addr-eng: ecdhe_curve
155 addr-eng: ecdhe_point
156 addr-eng: ecdhe_point_len
157 addr-eng: reneg
158 addr-eng: saved_finished
159 addr-eng: flags
160 addr-eng: pad
161 addr-eng: action
162 addr-eng: alert
163 addr-eng: close_received
164
165 \ Similar to 'addr-eng:', for fields in the 'session' substructure.
166 : addr-session-field:
167 next-word { field }
168 "addr-" field + 0 1 define-word
169 0 8191 "offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, " field + ")" + make-CX
170 postpone literal postpone ; ;
171
172 addr-session-field: session_id
173 addr-session-field: session_id_len
174 addr-session-field: version
175 addr-session-field: cipher_suite
176 addr-session-field: master_secret
177
178 \ Check a server flag by index.
179 : flag? ( index -- bool )
180 addr-flags get32 swap >> 1 and neg ;
181
182 \ Define a word that evaluates to an error constant. This assumes that
183 \ all relevant error codes are in the 0..63 range.
184 : err:
185 next-word { name }
186 name 0 1 define-word
187 0 63 "BR_" name + make-CX postpone literal postpone ; ;
188
189 err: ERR_OK
190 err: ERR_BAD_PARAM
191 err: ERR_BAD_STATE
192 err: ERR_UNSUPPORTED_VERSION
193 err: ERR_BAD_VERSION
194 err: ERR_BAD_LENGTH
195 err: ERR_TOO_LARGE
196 err: ERR_BAD_MAC
197 err: ERR_NO_RANDOM
198 err: ERR_UNKNOWN_TYPE
199 err: ERR_UNEXPECTED
200 err: ERR_BAD_CCS
201 err: ERR_BAD_ALERT
202 err: ERR_BAD_HANDSHAKE
203 err: ERR_OVERSIZED_ID
204 err: ERR_BAD_CIPHER_SUITE
205 err: ERR_BAD_COMPRESSION
206 err: ERR_BAD_FRAGLEN
207 err: ERR_BAD_SECRENEG
208 err: ERR_EXTRA_EXTENSION
209 err: ERR_BAD_SNI
210 err: ERR_BAD_HELLO_DONE
211 err: ERR_LIMIT_EXCEEDED
212 err: ERR_BAD_FINISHED
213 err: ERR_RESUME_MISMATCH
214 err: ERR_INVALID_ALGORITHM
215
216 \ Get supported curves (bit mask).
217 cc: supported-curves ( -- x ) {
218 uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves;
219 T0_PUSH(x);
220 }
221
222 \ Get supported hash functions (bit mask and number).
223 cc: supported-hash-functions ( -- x num ) {
224 int i;
225 unsigned x, num;
226
227 x = 0;
228 num = 0;
229 for (i = br_sha1_ID; i <= br_sha512_ID; i ++) {
230 if (br_multihash_getimpl(&ENG->mhash, i)) {
231 x |= 1U << i;
232 num ++;
233 }
234 }
235 T0_PUSH(x);
236 T0_PUSH(num);
237 }
238
239 \ (Re)initialise the multihasher.
240 cc: multihash-init ( -- ) {
241 br_multihash_init(&ENG->mhash);
242 }
243
244 \ Flush the current record: if some payload data has been accumulated,
245 \ close the record and schedule it for sending. If there is no such data,
246 \ this function does nothing.
247 cc: flush-record ( -- ) {
248 br_ssl_engine_flush_record(ENG);
249 }
250
251 \ Yield control to the caller.
252 \ When the control is returned to us, react to the new context. Returned
253 \ value is a bitwise combination of the following:
254 \ 0x01 handshake data is available
255 \ 0x02 change-cipher-spec data is available
256 \ 0x04 some data other than handshake or change-cipher-spec is available
257 \ 0x08 output buffer is ready for a new outgoing record
258 \ 0x10 renegotiation is requested and not to be ignored
259 \ Flags 0x01, 0x02 and 0x04 are mutually exclusive.
260 : wait-co ( -- state )
261 co
262 0
263 addr-action get8 dup if
264 case
265 1 of 0 do-close endof
266 2 of addr-application_data get8 if 0x10 or then endof
267 endcase
268 else
269 drop
270 then
271 addr-close_received get8 ifnot
272 has-input? if
273 addr-record_type_in get8 case
274
275 \ ChangeCipherSpec
276 20 of 0x02 or endof
277
278 \ Alert -- if close_notify received, trigger
279 \ the closure sequence.
280 21 of process-alerts if -1 do-close then endof
281
282 \ Handshake
283 22 of 0x01 or endof
284
285 \ Not CCS, Alert or Handshake.
286 drop 0x04 or 0
287 endcase
288 then
289 then
290 can-output? if 0x08 or then ;
291
292 \ Send an alert message. This shall be called only when there is room for
293 \ an outgoing record.
294 : send-alert ( level alert -- )
295 21 addr-record_type_out set8
296 swap write8-native drop write8-native drop
297 flush-record ;
298
299 \ Send an alert message of level "warning". This shall be called only when
300 \ there is room for an outgoing record.
301 : send-warning ( alert -- )
302 1 swap send-alert ;
303
304 \ Fail by sending a fatal alert.
305 : fail-alert ( alert -- ! )
306 { alert }
307 flush-record
308 begin can-output? not while wait-co drop repeat
309 2 alert send-alert
310 begin can-output? not while wait-co drop repeat
311 alert 512 + fail ;
312
313 \ Perform the close operation:
314 \ -- Prevent new application data from the caller.
315 \ -- Incoming data is discarded (except alerts).
316 \ -- Outgoing data is flushed.
317 \ -- A close_notify alert is sent.
318 \ -- If 'cnr' is zero, then incoming data is discarded until a close_notify
319 \ is received.
320 \ -- At the end, the context is terminated.
321 : do-close ( cnr -- ! )
322 \ 'cnr' is set to non-zero when a close_notify is received from
323 \ the peer.
324 { cnr }
325
326 \ Get out of application data state.
327 0 addr-application_data set8
328
329 \ Flush existing payload if any.
330 flush-record
331
332 \ Wait for room to send the close_notify. Since individual records
333 \ can always hold at least 512 bytes, we know that when there is
334 \ room, then there is room for a complete close_notify (two bytes).
335 begin can-output? not while cnr wait-for-close >cnr repeat
336
337 \ Write the close_notify and flush it.
338 \ 21 addr-record_type_out set8
339 \ 1 write8-native 0 write8-native 2drop
340 \ flush-record
341 0 send-warning
342
343 \ Loop until our record has been sent (we know it's gone when
344 \ writing is again possible) and a close_notify has been received.
345 cnr
346 begin
347 dup can-output? and if ERR_OK fail then
348 wait-for-close
349 again ;
350
351 \ Yield control to the engine, with a possible flush. If 'cnr' is 0,
352 \ then input is analysed: all input is discarded, until a close_notify
353 \ is received.
354 : wait-for-close ( cnr -- cnr )
355 co
356 dup ifnot
357 has-input? if
358 addr-record_type_in get8 21 = if
359 drop process-alerts
360 else
361 discard-input
362 then
363 then
364 then ;
365
366 \ Test whether there is some accumulated payload that still needs to be
367 \ sent.
368 cc: payload-to-send? ( -- bool ) {
369 T0_PUSHi(-br_ssl_engine_has_pld_to_send(ENG));
370 }
371
372 \ Test whether there is some available input data.
373 cc: has-input? ( -- bool ) {
374 T0_PUSHi(-(ENG->hlen_in != 0));
375 }
376
377 \ Test whether some payload bytes may be written.
378 cc: can-output? ( -- bool ) {
379 T0_PUSHi(-(ENG->hlen_out > 0));
380 }
381
382 \ Discard current input entirely.
383 cc: discard-input ( -- ) {
384 ENG->hlen_in = 0;
385 }
386
387 \ Low-level read for one byte. If there is no available byte right
388 \ away, then -1 is returned. Otherwise, the byte value is returned.
389 \ If the current record type is "handshake" then the read byte is also
390 \ injected in the multi-hasher.
391 cc: read8-native ( -- x ) {
392 if (ENG->hlen_in > 0) {
393 unsigned char x;
394
395 x = *ENG->hbuf_in ++;
396 if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
397 br_multihash_update(&ENG->mhash, &x, 1);
398 }
399 T0_PUSH(x);
400 ENG->hlen_in --;
401 } else {
402 T0_PUSHi(-1);
403 }
404 }
405
406 \ Low-level read for several bytes. On entry, this expects an address
407 \ (offset in the engine context) and a length; these values designate
408 \ where the chunk should go. Upon exit, the new address and length
409 \ are pushed; that output length contains how many bytes could not be
410 \ read. If there is no available byte for reading, the address and
411 \ length are unchanged.
412 \ If the current record type is "handshake" then the read bytes are
413 \ injected in the multi-hasher.
414 cc: read-chunk-native ( addr len -- addr len ) {
415 size_t clen = ENG->hlen_in;
416 if (clen > 0) {
417 uint32_t addr, len;
418
419 len = T0_POP();
420 addr = T0_POP();
421 if ((size_t)len < clen) {
422 clen = (size_t)len;
423 }
424 memcpy((unsigned char *)ENG + addr, ENG->hbuf_in, clen);
425 if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
426 br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen);
427 }
428 T0_PUSH(addr + (uint32_t)clen);
429 T0_PUSH(len - (uint32_t)clen);
430 ENG->hbuf_in += clen;
431 ENG->hlen_in -= clen;
432 }
433 }
434
435 \ Process available alert bytes. If a fatal alert is received, then the
436 \ context is terminated; otherwise, this returns either true (-1) if a
437 \ close_notify was received, false (0) otherwise.
438 : process-alerts ( -- bool )
439 0
440 begin has-input? while read8-native process-alert-byte or repeat
441 dup if 1 addr-shutdown_recv set8 then ;
442
443 \ Process an alert byte. Returned value is non-zero if this is a close_notify,
444 \ zero otherwise.
445 : process-alert-byte ( x -- bool )
446 addr-alert get8 case
447 0 of
448 \ 'alert' field is 0, so this byte shall be a level.
449 \ Levels shall be 1 (warning) or 2 (fatal); we convert
450 \ all other values to "fatal".
451 dup 1 <> if drop 2 then
452 addr-alert set8 0
453 endof
454 1 of
455 0 addr-alert set8
456 \ close_notify has value 0.
457 \ no_renegotiation has value 100, and we treat it
458 \ as a fatal alert.
459 dup 100 = if 256 + fail then
460 0= ret
461 endof
462 \ Fatal alert implies context termination.
463 drop 256 + fail
464 endcase ;
465
466 \ In general we only deal with handshake data here. Alerts are processed
467 \ in specific code right when they are received, and ChangeCipherSpec has
468 \ its own handling code. So we need to check that the data is "handshake"
469 \ only when returning from a coroutine call.
470
471 \ Yield control to the engine. Alerts are processed; if incoming data is
472 \ neither handshake or alert, then an error is triggered.
473 : wait-for-handshake ( -- )
474 wait-co 0x07 and 0x01 > if ERR_UNEXPECTED fail then ;
475
476 \ Flush outgoing data (if any), then wait for the output buffer to be
477 \ clear; when this is done, set the output record type to the specified
478 \ value.
479 : wait-rectype-out ( rectype -- )
480 { rectype }
481 flush-record
482 begin
483 can-output? if rectype addr-record_type_out set8 ret then
484 wait-co drop
485 again ;
486
487 \ Read one byte of handshake data. Block until that byte is available.
488 \ This does not check any length.
489 : read8-nc ( -- x )
490 begin
491 read8-native dup 0< ifnot ret then
492 drop wait-for-handshake
493 again ;
494
495 \ Test whether there are some more bytes in the current record. These
496 \ bytes have not necessarily been received yet (processing of unencrypted
497 \ records may begin before all bytes are received).
498 cc: more-incoming-bytes? ( -- bool ) {
499 T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG));
500 }
501
502 \ For reading functions, the TOS is supposed to contain the number of bytes
503 \ that can still be read (from encapsulating structure header), and it is
504 \ updated.
505
506 : check-len ( lim len -- lim )
507 - dup 0< if ERR_BAD_PARAM fail then ;
508
509 \ Read one byte of handshake data. This pushes an integer in the 0..255 range.
510 : read8 ( lim -- lim x )
511 1 check-len read8-nc ;
512
513 \ Read a 16-bit value (in the 0..65535 range)
514 : read16 ( lim -- lim n )
515 2 check-len read8-nc 8 << read8-nc + ;
516
517 \ Read a 24-bit value (in the 0..16777215 range)
518 : read24 ( lim -- lim n )
519 3 check-len read8-nc 8 << read8-nc + 8 << read8-nc + ;
520
521 \ Read some bytes. The "address" is an offset within the context
522 \ structure.
523 : read-blob ( lim addr len -- lim )
524 { addr len }
525 len check-len
526 addr len
527 begin
528 read-chunk-native
529 dup 0 = if 2drop ret then
530 wait-for-handshake
531 again ;
532
533 \ Read some bytes and drop them.
534 : skip-blob ( lim len -- lim )
535 swap over check-len swap
536 begin dup while read8-nc drop 1- repeat
537 drop ;
538
539 \ Read a 16-bit length, then skip exactly that many bytes.
540 : read-ignore-16 ( lim -- lim )
541 read16 skip-blob ;
542
543 \ Open a substructure: the inner structure length is checked against,
544 \ and substracted, from the output structure current limit.
545 : open-elt ( lim len -- lim-outer lim-inner )
546 dup { len }
547 - dup 0< if ERR_BAD_PARAM fail then
548 len ;
549
550 \ Close the current structure. This checks that the limit is 0.
551 : close-elt ( lim -- )
552 if ERR_BAD_PARAM fail then ;
553
554 \ Write one byte of handshake data.
555 : write8 ( n -- )
556 begin
557 dup write8-native if drop ret then
558 wait-co drop
559 again ;
560
561 \ Low-level write for one byte. On exit, it pushes either -1 (byte was
562 \ written) or 0 (no room in output buffer).
563 cc: write8-native ( x -- bool ) {
564 unsigned char x;
565
566 x = (unsigned char)T0_POP();
567 if (ENG->hlen_out > 0) {
568 if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
569 br_multihash_update(&ENG->mhash, &x, 1);
570 }
571 *ENG->hbuf_out ++ = x;
572 ENG->hlen_out --;
573 T0_PUSHi(-1);
574 } else {
575 T0_PUSHi(0);
576 }
577 }
578
579 \ Write a 16-bit value.
580 : write16 ( n -- )
581 dup 8 u>> write8 write8 ;
582
583 \ Write a 24-bit value.
584 : write24 ( n -- )
585 dup 16 u>> write8 write16 ;
586
587 \ Write some bytes. The "address" is an offset within the context
588 \ structure.
589 : write-blob ( addr len -- )
590 begin
591 write-blob-chunk
592 dup 0 = if 2drop ret then
593 wait-co drop
594 again ;
595
596 cc: write-blob-chunk ( addr len -- addr len ) {
597 size_t clen = ENG->hlen_out;
598 if (clen > 0) {
599 uint32_t addr, len;
600
601 len = T0_POP();
602 addr = T0_POP();
603 if ((size_t)len < clen) {
604 clen = (size_t)len;
605 }
606 memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen);
607 if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
608 br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen);
609 }
610 T0_PUSH(addr + (uint32_t)clen);
611 T0_PUSH(len - (uint32_t)clen);
612 ENG->hbuf_out += clen;
613 ENG->hlen_out -= clen;
614 }
615 }
616
617 \ Write a blob with the length as header (over one byte)
618 : write-blob-head8 ( addr len -- )
619 dup write8 write-blob ;
620
621 \ Write a blob with the length as header (over two bytes)
622 : write-blob-head16 ( addr len -- )
623 dup write16 write-blob ;
624
625 \ Perform a byte-to-byte comparison between two blobs. Each blob is
626 \ provided as an "address" (offset in the context structure); the
627 \ length is common. Returned value is true (-1) if the two blobs are
628 \ equal, false (0) otherwise.
629 cc: memcmp ( addr1 addr2 len -- bool ) {
630 size_t len = (size_t)T0_POP();
631 void *addr2 = (unsigned char *)ENG + (size_t)T0_POP();
632 void *addr1 = (unsigned char *)ENG + (size_t)T0_POP();
633 int x = memcmp(addr1, addr2, len);
634 T0_PUSH((uint32_t)-(x == 0));
635 }
636
637 \ Copy bytes between two areas, whose addresses are provided as
638 \ offsets in the context structure.
639 cc: memcpy ( dst src len -- ) {
640 size_t len = (size_t)T0_POP();
641 void *src = (unsigned char *)ENG + (size_t)T0_POP();
642 void *dst = (unsigned char *)ENG + (size_t)T0_POP();
643 memcpy(dst, src, len);
644 }
645
646 \ Get string length (zero-terminated). The string address is provided as
647 \ an offset relative to the context start. Returned length does not include
648 \ the terminated 0.
649 cc: strlen ( str -- len ) {
650 void *str = (unsigned char *)ENG + (size_t)T0_POP();
651 T0_PUSH((uint32_t)strlen(str));
652 }
653
654 \ Fill a buffer with zeros. The buffer address is an offset in the context.
655 cc: bzero ( addr len -- ) {
656 size_t len = (size_t)T0_POP();
657 void *addr = (unsigned char *)ENG + (size_t)T0_POP();
658 memset(addr, 0, len);
659 }
660
661 \ Scan the list of supported cipher suites for a given value. If found,
662 \ then the list index at which it was found is returned; otherwise, -1
663 \ is returned.
664 : scan-suite ( suite -- index )
665 { suite }
666 addr-suites_num get8 { num }
667 0
668 begin dup num < while
669 dup 1 << addr-suites_buf + get16 suite = if ret then
670 1+
671 repeat
672 drop -1 ;
673
674 \ =======================================================================
675
676 \ Generate random bytes into buffer (address is offset in context).
677 cc: mkrand ( addr len -- ) {
678 size_t len = (size_t)T0_POP();
679 void *addr = (unsigned char *)ENG + (size_t)T0_POP();
680 br_hmac_drbg_generate(&ENG->rng, addr, len);
681 }
682
683 \ Read a handshake message header: type and length. These are returned
684 \ in reverse order (type is TOS, length is below it).
685 : read-handshake-header-core ( -- lim type )
686 read8-nc 3 read24 swap drop swap ;
687
688 \ Read a handshake message header: type and length. If the header is for
689 \ a HelloRequest message, then it is discarded and a new header is read
690 \ (repeatedly if necessary).
691 : read-handshake-header ( -- lim type )
692 begin
693 read-handshake-header-core dup 0= while
694 drop if ERR_BAD_HANDSHAKE fail then
695 repeat ;
696
697 \ =======================================================================
698
699 \ Cipher suite processing.
700 \
701 \ Unfortunately, cipher suite identifiers are attributed mostly arbitrary,
702 \ so we have to map the cipher suite numbers we support into aggregate
703 \ words that encode the information we need. Table below is organized
704 \ as a sequence of pairs of 16-bit words, the first being the cipher suite
705 \ identifier, the second encoding the algorithm elements. The suites are
706 \ ordered by increasing cipher suite ID, so that fast lookups may be
707 \ performed with a binary search (not implemented for the moment, since it
708 \ does not appear to matter much in practice).
709 \
710 \ Algorithm elements are encoded over 4 bits each, in the following order
711 \ (most significant to least significant):
712 \
713 \ -- Server key type:
714 \ 0 RSA (RSA key exchange)
715 \ 1 ECDHE-RSA (ECDHE key exchange, RSA signature)
716 \ 2 ECDHE-ECDSA (ECDHE key exchange, ECDSA signature)
717 \ 3 ECDH-RSA (ECDH key exchange, certificate is RSA-signed)
718 \ 4 ECDH-ECDSA (ECDH key exchange, certificate is ECDSA-signed)
719 \ -- Encryption algorithm:
720 \ 0 3DES/CBC
721 \ 1 AES-128/CBC
722 \ 2 AES-256/CBC
723 \ 3 AES-128/GCM
724 \ 4 AES-256/GCM
725 \ 5 ChaCha20/Poly1305
726 \ -- MAC algorithm:
727 \ 0 none (for suites with AEAD encryption)
728 \ 2 HMAC/SHA-1
729 \ 4 HMAC/SHA-256
730 \ 5 HMAC/SHA-384
731 \ -- PRF for TLS-1.2:
732 \ 4 with SHA-256
733 \ 5 with SHA-384
734
735 data: cipher-suite-def
736
737 hexb| 000A 0024 | \ TLS_RSA_WITH_3DES_EDE_CBC_SHA
738 hexb| 002F 0124 | \ TLS_RSA_WITH_AES_128_CBC_SHA
739 hexb| 0035 0224 | \ TLS_RSA_WITH_AES_256_CBC_SHA
740 hexb| 003C 0144 | \ TLS_RSA_WITH_AES_128_CBC_SHA256
741 hexb| 003D 0244 | \ TLS_RSA_WITH_AES_256_CBC_SHA256
742
743 hexb| 009C 0304 | \ TLS_RSA_WITH_AES_128_GCM_SHA256
744 hexb| 009D 0405 | \ TLS_RSA_WITH_AES_256_GCM_SHA384
745
746 hexb| C003 4024 | \ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
747 hexb| C004 4124 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
748 hexb| C005 4224 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
749 hexb| C008 2024 | \ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
750 hexb| C009 2124 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
751 hexb| C00A 2224 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
752 hexb| C00D 3024 | \ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
753 hexb| C00E 3124 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
754 hexb| C00F 3224 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
755 hexb| C012 1024 | \ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
756 hexb| C013 1124 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
757 hexb| C014 1224 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
758
759 hexb| C023 2144 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
760 hexb| C024 2255 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
761 hexb| C025 4144 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
762 hexb| C026 4255 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
763 hexb| C027 1144 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
764 hexb| C028 1255 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
765 hexb| C029 3144 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
766 hexb| C02A 3255 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
767 hexb| C02B 2304 | \ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
768 hexb| C02C 2405 | \ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
769 hexb| C02D 4304 | \ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
770 hexb| C02E 4405 | \ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
771 hexb| C02F 1304 | \ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
772 hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
773 hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
774 hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
775
776 hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
777 hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
778
779 hexb| 0000 | \ List terminator.
780
781 \ Convert cipher suite identifier to element words. This returns 0 if
782 \ the cipher suite is not known.
783 : cipher-suite-to-elements ( suite -- elts )
784 { id }
785 cipher-suite-def
786 begin
787 dup 2+ swap data-get16
788 dup ifnot 2drop 0 ret then
789 id = if data-get16 ret then
790 2+
791 again ;
792
793 \ Check that a given cipher suite is supported. Note that this also
794 \ returns true (-1) for the TLS_FALLBACK_SCSV pseudo-ciphersuite.
795 : suite-supported? ( suite -- bool )
796 dup 0x5600 = if drop -1 ret then
797 cipher-suite-to-elements 0<> ;
798
799 \ Get expected key type for cipher suite. The key type is one of
800 \ BR_KEYTYPE_RSA or BR_KEYTYPE_EC, combined with either BR_KEYTYPE_KEYX
801 \ (RSA encryption or static ECDH) or BR_KEYTYPE_SIGN (RSA or ECDSA
802 \ signature, for ECDHE cipher suites).
803 : expected-key-type ( suite -- key-type )
804 cipher-suite-to-elements 12 >>
805 case
806 0 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX } endof
807 1 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN } endof
808 2 of CX 0 63 { BR_KEYTYPE_EC | BR_KEYTYPE_SIGN } endof
809 3 of CX 0 63 { BR_KEYTYPE_EC | BR_KEYTYPE_KEYX } endof
810 4 of CX 0 63 { BR_KEYTYPE_EC | BR_KEYTYPE_KEYX } endof
811 0 swap
812 endcase ;
813
814 \ Test whether the cipher suite uses RSA key exchange.
815 : use-rsa-keyx? ( suite -- bool )
816 cipher-suite-to-elements 12 >> 0= ;
817
818 \ Test whether the cipher suite uses ECDHE key exchange, signed with RSA.
819 : use-rsa-ecdhe? ( suite -- bool )
820 cipher-suite-to-elements 12 >> 1 = ;
821
822 \ Test whether the cipher suite uses ECDHE key exchange, signed with ECDSA.
823 : use-ecdsa-ecdhe? ( suite -- bool )
824 cipher-suite-to-elements 12 >> 2 = ;
825
826 \ Test whether the cipher suite uses ECDHE key exchange (with RSA or ECDSA).
827 : use-ecdhe? ( suite -- bool )
828 cipher-suite-to-elements 12 >> dup 0> swap 3 < and ;
829
830 \ Test whether the cipher suite uses ECDH (static) key exchange.
831 : use-ecdh? ( suite -- bool )
832 cipher-suite-to-elements 12 >> 2 > ;
833
834 \ Get identifier for the PRF (TLS 1.2).
835 : prf-id ( suite -- id )
836 cipher-suite-to-elements 15 and ;
837
838 \ Switch to negotiated security parameters for input or output.
839 : switch-encryption ( is-client for-input -- )
840 { for-input }
841 addr-cipher_suite get16 cipher-suite-to-elements { elts }
842
843 \ prf_id
844 elts 15 and
845
846 \ mac_id
847 elts 4 >> 15 and
848
849 \ cipher type and key length
850 elts 8 >> 15 and case
851 \ 3DES/CBC
852 0 of 0 24
853 for-input if
854 switch-cbc-in
855 else
856 switch-cbc-out
857 then
858 endof
859
860 \ AES-128/CBC
861 1 of 1 16
862 for-input if
863 switch-cbc-in
864 else
865 switch-cbc-out
866 then
867 endof
868
869 \ AES-256/CBC
870 2 of 1 32
871 for-input if
872 switch-cbc-in
873 else
874 switch-cbc-out
875 then
876 endof
877
878 \ AES-128/GCM
879 3 of drop 16
880 for-input if
881 switch-aesgcm-in
882 else
883 switch-aesgcm-out
884 then
885 endof
886
887 \ AES-256/GCM
888 4 of drop 32
889 for-input if
890 switch-aesgcm-in
891 else
892 switch-aesgcm-out
893 then
894 endof
895
896 \ ChaCha20/Poly1305
897 \ 5 of endof
898
899 ERR_BAD_PARAM fail
900 endcase
901 ;
902
903 cc: switch-cbc-out ( is_client prf_id mac_id aes cipher_key_len -- ) {
904 int is_client, prf_id, mac_id, aes;
905 unsigned cipher_key_len;
906
907 cipher_key_len = T0_POP();
908 aes = T0_POP();
909 mac_id = T0_POP();
910 prf_id = T0_POP();
911 is_client = T0_POP();
912 br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id,
913 aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len);
914 }
915
916 cc: switch-cbc-in ( is_client prf_id mac_id aes cipher_key_len -- ) {
917 int is_client, prf_id, mac_id, aes;
918 unsigned cipher_key_len;
919
920 cipher_key_len = T0_POP();
921 aes = T0_POP();
922 mac_id = T0_POP();
923 prf_id = T0_POP();
924 is_client = T0_POP();
925 br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id,
926 aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len);
927 }
928
929 cc: switch-aesgcm-out ( is_client prf_id cipher_key_len -- ) {
930 int is_client, prf_id;
931 unsigned cipher_key_len;
932
933 cipher_key_len = T0_POP();
934 prf_id = T0_POP();
935 is_client = T0_POP();
936 br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id,
937 ENG->iaes_ctr, cipher_key_len);
938 }
939
940 cc: switch-aesgcm-in ( is_client prf_id cipher_key_len -- ) {
941 int is_client, prf_id;
942 unsigned cipher_key_len;
943
944 cipher_key_len = T0_POP();
945 prf_id = T0_POP();
946 is_client = T0_POP();
947 br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id,
948 ENG->iaes_ctr, cipher_key_len);
949 }
950
951 \ Write Finished message.
952 : write-Finished ( from_client -- )
953 compute-Finished
954 20 write8 12 write24 addr-pad 12 write-blob ;
955
956 \ Read Finished message.
957 : read-Finished ( from_client -- )
958 compute-Finished
959 read-handshake-header 20 <> if ERR_UNEXPECTED fail then
960 addr-pad 12 + 12 read-blob
961 close-elt
962 addr-pad dup 12 + 12 memcmp ifnot ERR_BAD_FINISHED fail then ;
963
964 \ Compute the "Finished" contents (either the value to send, or the
965 \ expected value). The 12-byte string is written in the pad. The
966 \ "from_client" value is non-zero for the Finished sent by the client.
967 \ The computed value is also saved in the relevant buffer for handling
968 \ secure renegotiation.
969 : compute-Finished ( from_client -- )
970 dup addr-saved_finished swap ifnot 12 + then swap
971 addr-cipher_suite get16 prf-id compute-Finished-inner
972 addr-pad 12 memcpy ;
973
974 cc: compute-Finished-inner ( from_client prf_id -- ) {
975 int prf_id = T0_POP();
976 int from_client = T0_POPi();
977 unsigned char seed[48];
978 size_t seed_len;
979
980 br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
981 if (ENG->session.version >= BR_TLS12) {
982 seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
983 } else {
984 br_multihash_out(&ENG->mhash, br_md5_ID, seed);
985 br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
986 seed_len = 36;
987 }
988 prf(ENG->pad, 12, ENG->session.master_secret,
989 sizeof ENG->session.master_secret,
990 from_client ? "client finished" : "server finished",
991 seed, seed_len);
992 }
993
994 \ Receive ChangeCipherSpec and Finished from the peer.
995 : read-CCS-Finished ( is-client -- )
996 has-input? if
997 addr-record_type_in get8 20 <> if ERR_UNEXPECTED fail then
998 else
999 begin
1000 wait-co 0x07 and dup 0x02 <> while
1001 if ERR_UNEXPECTED fail then
1002 repeat
1003 drop
1004 then
1005 read8-nc 1 <> more-incoming-bytes? or if ERR_BAD_CCS fail then
1006 dup 1 switch-encryption
1007
1008 \ Read and verify Finished from peer.
1009 not read-Finished ;
1010
1011 \ Send ChangeCipherSpec and Finished to the peer.
1012 : write-CCS-Finished ( is-client -- )
1013 \ Flush and wait for output buffer to be clear, so that we may
1014 \ write our ChangeCipherSpec. We must switch immediately after
1015 \ triggering the flush.
1016 20 wait-rectype-out
1017 1 write8
1018 flush-record
1019 dup 0 switch-encryption
1020 22 wait-rectype-out
1021 write-Finished
1022 flush-record ;