]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bearssl/src/ssl/ssl_hs_common.t0
Merge ^/vendor/lldb/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / bearssl / src / ssl / 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 *)(void *)((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 \ Debug function that prints a string (and a newline) on stderr.
83 cc: DBG ( addr -- ) {
84         extern void *stderr;
85         extern int fprintf(void *, const char *, ...);
86         fprintf(stderr, "%s\n", &t0_datablock[T0_POPi()]);
87 }
88
89 \ Debug function that prints a string and an integer value (followed
90 \ by a newline) on stderr.
91 cc: DBG2 ( addr x -- ) {
92         extern void *stderr;
93         extern int fprintf(void *, const char *, ...);
94         int32_t x = T0_POPi();
95         fprintf(stderr, "%s: %ld (0x%08lX)\n",
96                 &t0_datablock[T0_POPi()], (long)x, (unsigned long)(uint32_t)x);
97 }
98
99 \ Mark the context as failed with a specific error code. This also
100 \ returns control to the caller.
101 cc: fail ( err -- ! ) {
102         br_ssl_engine_fail(ENG, (int)T0_POPi());
103         T0_CO();
104 }
105
106 \ Read a byte from the context (address is offset in context).
107 cc: get8 ( addr -- val ) {
108         size_t addr = (size_t)T0_POP();
109         T0_PUSH(*((unsigned char *)ENG + addr));
110 }
111
112 \ Read a 16-bit word from the context (address is offset in context).
113 cc: get16 ( addr -- val ) {
114         size_t addr = (size_t)T0_POP();
115         T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr));
116 }
117
118 \ Read a 32-bit word from the context (address is offset in context).
119 cc: get32 ( addr -- val ) {
120         size_t addr = (size_t)T0_POP();
121         T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr));
122 }
123
124 \ Set a byte in the context (address is offset in context).
125 cc: set8 ( val addr -- ) {
126         size_t addr = (size_t)T0_POP();
127         *((unsigned char *)ENG + addr) = (unsigned char)T0_POP();
128 }
129
130 \ Set a 16-bit word in the context (address is offset in context).
131 cc: set16 ( val addr -- ) {
132         size_t addr = (size_t)T0_POP();
133         *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
134 }
135
136 \ Set a 32-bit word in the context (address is offset in context).
137 cc: set32 ( val addr -- ) {
138         size_t addr = (size_t)T0_POP();
139         *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
140 }
141
142 \ Define a word that evaluates as an address of a field within the
143 \ engine context. The field name (C identifier) must follow in the
144 \ source. For field 'foo', the defined word is 'addr-foo'.
145 : addr-eng:
146         next-word { field }
147         "addr-" field + 0 1 define-word
148         0 8191 "offsetof(br_ssl_engine_context, " field + ")" + make-CX
149         postpone literal postpone ; ;
150
151 addr-eng: max_frag_len
152 addr-eng: log_max_frag_len
153 addr-eng: peer_log_max_frag_len
154 addr-eng: shutdown_recv
155 addr-eng: record_type_in
156 addr-eng: record_type_out
157 addr-eng: version_in
158 addr-eng: version_out
159 addr-eng: application_data
160 addr-eng: version_min
161 addr-eng: version_max
162 addr-eng: suites_buf
163 addr-eng: suites_num
164 addr-eng: server_name
165 addr-eng: client_random
166 addr-eng: server_random
167 addr-eng: ecdhe_curve
168 addr-eng: ecdhe_point
169 addr-eng: ecdhe_point_len
170 addr-eng: reneg
171 addr-eng: saved_finished
172 addr-eng: flags
173 addr-eng: pad
174 addr-eng: action
175 addr-eng: alert
176 addr-eng: close_received
177 addr-eng: protocol_names_num
178 addr-eng: selected_protocol
179
180 \ Similar to 'addr-eng:', for fields in the 'session' substructure.
181 : addr-session-field:
182         next-word { field }
183         "addr-" field + 0 1 define-word
184         0 8191 "offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, " field + ")" + make-CX
185         postpone literal postpone ; ;
186
187 addr-session-field: session_id
188 addr-session-field: session_id_len
189 addr-session-field: version
190 addr-session-field: cipher_suite
191 addr-session-field: master_secret
192
193 \ Check a server flag by index.
194 : flag? ( index -- bool )
195         addr-flags get32 swap >> 1 and neg ;
196
197 \ Define a word that evaluates to an error constant. This assumes that
198 \ all relevant error codes are in the 0..63 range.
199 : err:
200         next-word { name }
201         name 0 1 define-word
202         0 63 "BR_" name + make-CX postpone literal postpone ; ;
203
204 err: ERR_OK
205 err: ERR_BAD_PARAM
206 err: ERR_BAD_STATE
207 err: ERR_UNSUPPORTED_VERSION
208 err: ERR_BAD_VERSION
209 err: ERR_BAD_LENGTH
210 err: ERR_TOO_LARGE
211 err: ERR_BAD_MAC
212 err: ERR_NO_RANDOM
213 err: ERR_UNKNOWN_TYPE
214 err: ERR_UNEXPECTED
215 err: ERR_BAD_CCS
216 err: ERR_BAD_ALERT
217 err: ERR_BAD_HANDSHAKE
218 err: ERR_OVERSIZED_ID
219 err: ERR_BAD_CIPHER_SUITE
220 err: ERR_BAD_COMPRESSION
221 err: ERR_BAD_FRAGLEN
222 err: ERR_BAD_SECRENEG
223 err: ERR_EXTRA_EXTENSION
224 err: ERR_BAD_SNI
225 err: ERR_BAD_HELLO_DONE
226 err: ERR_LIMIT_EXCEEDED
227 err: ERR_BAD_FINISHED
228 err: ERR_RESUME_MISMATCH
229 err: ERR_INVALID_ALGORITHM
230 err: ERR_BAD_SIGNATURE
231 err: ERR_WRONG_KEY_USAGE
232 err: ERR_NO_CLIENT_AUTH
233
234 \ Get supported curves (bit mask).
235 cc: supported-curves ( -- x ) {
236         uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves;
237         T0_PUSH(x);
238 }
239
240 \ Get supported hash functions (bit mask and number).
241 \ Note: this (on purpose) skips MD5.
242 cc: supported-hash-functions ( -- x num ) {
243         int i;
244         unsigned x, num;
245
246         x = 0;
247         num = 0;
248         for (i = br_sha1_ID; i <= br_sha512_ID; i ++) {
249                 if (br_multihash_getimpl(&ENG->mhash, i)) {
250                         x |= 1U << i;
251                         num ++;
252                 }
253         }
254         T0_PUSH(x);
255         T0_PUSH(num);
256 }
257
258 \ Test support for RSA signatures.
259 cc: supports-rsa-sign? ( -- bool ) {
260         T0_PUSHi(-(ENG->irsavrfy != 0));
261 }
262
263 \ Test support for ECDSA signatures.
264 cc: supports-ecdsa? ( -- bool ) {
265         T0_PUSHi(-(ENG->iecdsa != 0));
266 }
267
268 \ (Re)initialise the multihasher.
269 cc: multihash-init ( -- ) {
270         br_multihash_init(&ENG->mhash);
271 }
272
273 \ Flush the current record: if some payload data has been accumulated,
274 \ close the record and schedule it for sending. If there is no such data,
275 \ this function does nothing.
276 cc: flush-record ( -- ) {
277         br_ssl_engine_flush_record(ENG);
278 }
279
280 \ Yield control to the caller.
281 \ When the control is returned to us, react to the new context. Returned
282 \ value is a bitwise combination of the following:
283 \   0x01   handshake data is available
284 \   0x02   change-cipher-spec data is available
285 \   0x04   some data other than handshake or change-cipher-spec is available
286 \   0x08   output buffer is ready for a new outgoing record
287 \   0x10   renegotiation is requested and not to be ignored
288 \ Flags 0x01, 0x02 and 0x04 are mutually exclusive.
289 : wait-co ( -- state )
290         co
291         0
292         addr-action get8 dup if
293                 case
294                         1 of 0 do-close endof
295                         2 of addr-application_data get8 1 = if
296                                 0x10 or
297                         then endof
298                 endcase
299         else
300                 drop
301         then
302         addr-close_received get8 ifnot
303                 has-input? if
304                         addr-record_type_in get8 case
305
306                                 \ ChangeCipherSpec
307                                 20 of 0x02 or endof
308
309                                 \ Alert -- if close_notify received, trigger
310                                 \ the closure sequence.
311                                 21 of process-alerts if -1 do-close then endof
312
313                                 \ Handshake
314                                 22 of 0x01 or endof
315
316                                 \ Not CCS, Alert or Handshake.
317                                 drop 0x04 or 0
318                         endcase
319                 then
320         then
321         can-output? if 0x08 or then ;
322
323 \ Send an alert message. This shall be called only when there is room for
324 \ an outgoing record.
325 : send-alert ( level alert -- )
326         21 addr-record_type_out set8
327         swap write8-native drop write8-native drop
328         flush-record ;
329
330 \ Send an alert message of level "warning". This shall be called only when
331 \ there is room for an outgoing record.
332 : send-warning ( alert -- )
333         1 swap send-alert ;
334
335 \ Fail by sending a fatal alert.
336 : fail-alert ( alert -- ! )
337         { alert }
338         flush-record
339         begin can-output? not while wait-co drop repeat
340         2 alert send-alert
341         begin can-output? not while wait-co drop repeat
342         alert 512 + fail ;
343
344 \ Perform the close operation:
345 \ -- Prevent new application data from the caller.
346 \ -- Incoming data is discarded (except alerts).
347 \ -- Outgoing data is flushed.
348 \ -- A close_notify alert is sent.
349 \ -- If 'cnr' is zero, then incoming data is discarded until a close_notify
350 \    is received.
351 \ -- At the end, the context is terminated.
352 \
353 \ cnr shall be either 0 or -1.
354 : do-close ( cnr -- ! )
355         \ 'cnr' is set to non-zero when a close_notify is received from
356         \ the peer.
357         { cnr }
358
359         \ Get out of application data state. If we were accepting
360         \ application data (flag is 1), and we still expect a close_notify
361         \ from the peer (cnr is 0), then we should set the flag to 2.
362         \ In all other cases, flag should be set to 0.
363         addr-application_data get8 cnr not and 1 << addr-application_data set8
364
365         \ Flush existing payload if any.
366         flush-record
367
368         \ Wait for room to send the close_notify. Since individual records
369         \ can always hold at least 512 bytes, we know that when there is
370         \ room, then there is room for a complete close_notify (two bytes).
371         begin can-output? not while cnr wait-for-close >cnr repeat
372
373         \ Write the close_notify and flush it.
374         \ 21 addr-record_type_out set8
375         \ 1 write8-native 0 write8-native 2drop
376         \ flush-record
377         0 send-warning
378
379         \ Loop until our record has been sent (we know it's gone when
380         \ writing is again possible) and a close_notify has been received.
381         cnr
382         begin
383                 dup can-output? and if ERR_OK fail then
384                 wait-for-close
385         again ;
386
387 \ Yield control to the engine, with a possible flush. If 'cnr' is 0,
388 \ then input is analysed: all input is discarded, until a close_notify
389 \ is received.
390 : wait-for-close ( cnr -- cnr )
391         co
392         dup ifnot
393                 has-input? if
394                         addr-record_type_in get8 21 = if
395                                 drop process-alerts
396                                 \ If we received a close_notify then we
397                                 \ no longer accept incoming application
398                                 \ data records.
399                                 0 addr-application_data set8
400                         else
401                                 discard-input
402                         then
403                 then
404         then ;
405
406 \ Test whether there is some accumulated payload that still needs to be
407 \ sent.
408 cc: payload-to-send? ( -- bool ) {
409         T0_PUSHi(-br_ssl_engine_has_pld_to_send(ENG));
410 }
411
412 \ Test whether there is some available input data.
413 cc: has-input? ( -- bool ) {
414         T0_PUSHi(-(ENG->hlen_in != 0));
415 }
416
417 \ Test whether some payload bytes may be written.
418 cc: can-output? ( -- bool ) {
419         T0_PUSHi(-(ENG->hlen_out > 0));
420 }
421
422 \ Discard current input entirely.
423 cc: discard-input ( -- ) {
424         ENG->hlen_in = 0;
425 }
426
427 \ Low-level read for one byte. If there is no available byte right
428 \ away, then -1 is returned. Otherwise, the byte value is returned.
429 \ If the current record type is "handshake" then the read byte is also
430 \ injected in the multi-hasher.
431 cc: read8-native ( -- x ) {
432         if (ENG->hlen_in > 0) {
433                 unsigned char x;
434
435                 x = *ENG->hbuf_in ++;
436                 if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
437                         br_multihash_update(&ENG->mhash, &x, 1);
438                 }
439                 T0_PUSH(x);
440                 ENG->hlen_in --;
441         } else {
442                 T0_PUSHi(-1);
443         }
444 }
445
446 \ Low-level read for several bytes. On entry, this expects an address
447 \ (offset in the engine context) and a length; these values designate
448 \ where the chunk should go. Upon exit, the new address and length
449 \ are pushed; that output length contains how many bytes could not be
450 \ read. If there is no available byte for reading, the address and
451 \ length are unchanged.
452 \ If the current record type is "handshake" then the read bytes are
453 \ injected in the multi-hasher.
454 cc: read-chunk-native ( addr len -- addr len ) {
455         size_t clen = ENG->hlen_in;
456         if (clen > 0) {
457                 uint32_t addr, len;
458
459                 len = T0_POP();
460                 addr = T0_POP();
461                 if ((size_t)len < clen) {
462                         clen = (size_t)len;
463                 }
464                 memcpy((unsigned char *)ENG + addr, ENG->hbuf_in, clen);
465                 if (ENG->record_type_in == BR_SSL_HANDSHAKE) {
466                         br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen);
467                 }
468                 T0_PUSH(addr + (uint32_t)clen);
469                 T0_PUSH(len - (uint32_t)clen);
470                 ENG->hbuf_in += clen;
471                 ENG->hlen_in -= clen;
472         }
473 }
474
475 \ Process available alert bytes. If a fatal alert is received, then the
476 \ context is terminated; otherwise, this returns either true (-1) if a
477 \ close_notify was received, false (0) otherwise.
478 : process-alerts ( -- bool )
479         0
480         begin has-input? while read8-native process-alert-byte or repeat
481         dup if 1 addr-shutdown_recv set8 then ;
482
483 \ Process an alert byte. Returned value is non-zero if this is a close_notify,
484 \ zero otherwise.
485 : process-alert-byte ( x -- bool )
486         addr-alert get8 case
487                 0 of
488                         \ 'alert' field is 0, so this byte shall be a level.
489                         \ Levels shall be 1 (warning) or 2 (fatal); we convert
490                         \ all other values to "fatal".
491                         dup 1 <> if drop 2 then
492                         addr-alert set8 0
493                 endof
494                 1 of
495                         0 addr-alert set8
496                         \ close_notify has value 0.
497                         \ no_renegotiation has value 100, and we treat it
498                         \ as a fatal alert.
499                         dup 100 = if 256 + fail then
500                         0=
501                 endof
502                 \ Fatal alert implies context termination.
503                 drop 256 + fail
504         endcase ;
505
506 \ In general we only deal with handshake data here. Alerts are processed
507 \ in specific code right when they are received, and ChangeCipherSpec has
508 \ its own handling code. So we need to check that the data is "handshake"
509 \ only when returning from a coroutine call.
510
511 \ Yield control to the engine. Alerts are processed; if incoming data is
512 \ neither handshake or alert, then an error is triggered.
513 : wait-for-handshake ( -- )
514         wait-co 0x07 and 0x01 > if ERR_UNEXPECTED fail then ;
515
516 \ Flush outgoing data (if any), then wait for the output buffer to be
517 \ clear; when this is done, set the output record type to the specified
518 \ value.
519 : wait-rectype-out ( rectype -- )
520         { rectype }
521         flush-record
522         begin
523                 can-output? if rectype addr-record_type_out set8 ret then
524                 wait-co drop
525         again ;
526
527 \ Read one byte of handshake data. Block until that byte is available.
528 \ This does not check any length.
529 : read8-nc ( -- x )
530         begin
531                 read8-native dup 0< ifnot ret then
532                 drop wait-for-handshake
533         again ;
534
535 \ Test whether there are some more bytes in the current record. These
536 \ bytes have not necessarily been received yet (processing of unencrypted
537 \ records may begin before all bytes are received).
538 cc: more-incoming-bytes? ( -- bool ) {
539         T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG));
540 }
541
542 \ For reading functions, the TOS is supposed to contain the number of bytes
543 \ that can still be read (from encapsulating structure header), and it is
544 \ updated.
545
546 : check-len ( lim len -- lim )
547         - dup 0< if ERR_BAD_PARAM fail then ;
548
549 \ Read one byte of handshake data. This pushes an integer in the 0..255 range.
550 : read8 ( lim -- lim x )
551         1 check-len read8-nc ;
552
553 \ Read a 16-bit value (in the 0..65535 range)
554 : read16 ( lim -- lim n )
555         2 check-len read8-nc 8 << read8-nc + ;
556
557 \ Read a 24-bit value (in the 0..16777215 range)
558 : read24 ( lim -- lim n )
559         3 check-len read8-nc 8 << read8-nc + 8 << read8-nc + ;
560
561 \ Read some bytes. The "address" is an offset within the context
562 \ structure.
563 : read-blob ( lim addr len -- lim )
564         { addr len }
565         len check-len
566         addr len
567         begin
568                 read-chunk-native
569                 dup 0 = if 2drop ret then
570                 wait-for-handshake
571         again ;
572
573 \ Read some bytes and drop them.
574 : skip-blob ( lim len -- lim )
575         swap over check-len swap
576         begin dup while read8-nc drop 1- repeat
577         drop ;
578
579 \ Read a 16-bit length, then skip exactly that many bytes.
580 : read-ignore-16 ( lim -- lim )
581         read16 skip-blob ;
582
583 \ Open a substructure: the inner structure length is checked against,
584 \ and subtracted, from the output structure current limit.
585 : open-elt ( lim len -- lim-outer lim-inner )
586         dup { len }
587         - dup 0< if ERR_BAD_PARAM fail then
588         len ;
589
590 \ Close the current structure. This checks that the limit is 0.
591 : close-elt ( lim -- )
592         if ERR_BAD_PARAM fail then ;
593
594 \ Write one byte of handshake data.
595 : write8 ( n -- )
596         begin
597                 dup write8-native if drop ret then
598                 wait-co drop
599         again ;
600
601 \ Low-level write for one byte. On exit, it pushes either -1 (byte was
602 \ written) or 0 (no room in output buffer).
603 cc: write8-native ( x -- bool ) {
604         unsigned char x;
605
606         x = (unsigned char)T0_POP();
607         if (ENG->hlen_out > 0) {
608                 if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
609                         br_multihash_update(&ENG->mhash, &x, 1);
610                 }
611                 *ENG->hbuf_out ++ = x;
612                 ENG->hlen_out --;
613                 T0_PUSHi(-1);
614         } else {
615                 T0_PUSHi(0);
616         }
617 }
618
619 \ Write a 16-bit value.
620 : write16 ( n -- )
621         dup 8 u>> write8 write8 ;
622
623 \ Write a 24-bit value.
624 : write24 ( n -- )
625         dup 16 u>> write8 write16 ;
626
627 \ Write some bytes. The "address" is an offset within the context
628 \ structure.
629 : write-blob ( addr len -- )
630         begin
631                 write-blob-chunk
632                 dup 0 = if 2drop ret then
633                 wait-co drop
634         again ;
635
636 cc: write-blob-chunk ( addr len -- addr len ) {
637         size_t clen = ENG->hlen_out;
638         if (clen > 0) {
639                 uint32_t addr, len;
640
641                 len = T0_POP();
642                 addr = T0_POP();
643                 if ((size_t)len < clen) {
644                         clen = (size_t)len;
645                 }
646                 memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen);
647                 if (ENG->record_type_out == BR_SSL_HANDSHAKE) {
648                         br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen);
649                 }
650                 T0_PUSH(addr + (uint32_t)clen);
651                 T0_PUSH(len - (uint32_t)clen);
652                 ENG->hbuf_out += clen;
653                 ENG->hlen_out -= clen;
654         }
655 }
656
657 \ Write a blob with the length as header (over one byte)
658 : write-blob-head8 ( addr len -- )
659         dup write8 write-blob ;
660
661 \ Write a blob with the length as header (over two bytes)
662 : write-blob-head16 ( addr len -- )
663         dup write16 write-blob ;
664
665 \ Perform a byte-to-byte comparison between two blobs. Each blob is
666 \ provided as an "address" (offset in the context structure); the
667 \ length is common. Returned value is true (-1) if the two blobs are
668 \ equal, false (0) otherwise.
669 cc: memcmp ( addr1 addr2 len -- bool ) {
670         size_t len = (size_t)T0_POP();
671         void *addr2 = (unsigned char *)ENG + (size_t)T0_POP();
672         void *addr1 = (unsigned char *)ENG + (size_t)T0_POP();
673         int x = memcmp(addr1, addr2, len);
674         T0_PUSH((uint32_t)-(x == 0));
675 }
676
677 \ Copy bytes between two areas, whose addresses are provided as
678 \ offsets in the context structure.
679 cc: memcpy ( dst src len -- ) {
680         size_t len = (size_t)T0_POP();
681         void *src = (unsigned char *)ENG + (size_t)T0_POP();
682         void *dst = (unsigned char *)ENG + (size_t)T0_POP();
683         memcpy(dst, src, len);
684 }
685
686 \ Get string length (zero-terminated). The string address is provided as
687 \ an offset relative to the context start. Returned length does not include
688 \ the terminated 0.
689 cc: strlen ( str -- len ) {
690         void *str = (unsigned char *)ENG + (size_t)T0_POP();
691         T0_PUSH((uint32_t)strlen(str));
692 }
693
694 \ Fill a buffer with zeros. The buffer address is an offset in the context.
695 cc: bzero ( addr len -- ) {
696         size_t len = (size_t)T0_POP();
697         void *addr = (unsigned char *)ENG + (size_t)T0_POP();
698         memset(addr, 0, len);
699 }
700
701 \ Scan the list of supported cipher suites for a given value. If found,
702 \ then the list index at which it was found is returned; otherwise, -1
703 \ is returned.
704 : scan-suite ( suite -- index )
705         { suite }
706         addr-suites_num get8 { num }
707         0
708         begin dup num < while
709                 dup 1 << addr-suites_buf + get16 suite = if ret then
710                 1+
711         repeat
712         drop -1 ;
713
714 \ =======================================================================
715
716 \ Generate random bytes into buffer (address is offset in context).
717 cc: mkrand ( addr len -- ) {
718         size_t len = (size_t)T0_POP();
719         void *addr = (unsigned char *)ENG + (size_t)T0_POP();
720         br_hmac_drbg_generate(&ENG->rng, addr, len);
721 }
722
723 \ Read a handshake message header: type and length. These are returned
724 \ in reverse order (type is TOS, length is below it).
725 : read-handshake-header-core ( -- lim type )
726         read8-nc 3 read24 swap drop swap ;
727
728 \ Read a handshake message header: type and length. If the header is for
729 \ a HelloRequest message, then it is discarded and a new header is read
730 \ (repeatedly if necessary).
731 : read-handshake-header ( -- lim type )
732         begin
733                 read-handshake-header-core dup 0= while
734                 drop if ERR_BAD_HANDSHAKE fail then
735         repeat ;
736
737 \ =======================================================================
738
739 \ Cipher suite processing.
740 \
741 \ Unfortunately, cipher suite identifiers are attributed mostly arbitrary,
742 \ so we have to map the cipher suite numbers we support into aggregate
743 \ words that encode the information we need. Table below is organized
744 \ as a sequence of pairs of 16-bit words, the first being the cipher suite
745 \ identifier, the second encoding the algorithm elements. The suites are
746 \ ordered by increasing cipher suite ID, so that fast lookups may be
747 \ performed with a binary search (not implemented for the moment, since it
748 \ does not appear to matter much in practice).
749 \
750 \ Algorithm elements are encoded over 4 bits each, in the following order
751 \ (most significant to least significant):
752
753 \ -- Server key type:
754 \       0  RSA           (RSA key exchange)
755 \       1  ECDHE-RSA     (ECDHE key exchange, RSA signature)
756 \       2  ECDHE-ECDSA   (ECDHE key exchange, ECDSA signature)
757 \       3  ECDH-RSA      (ECDH key exchange, certificate is RSA-signed)
758 \       4  ECDH-ECDSA    (ECDH key exchange, certificate is ECDSA-signed)
759 \ -- Encryption algorithm:
760 \       0  3DES/CBC
761 \       1  AES-128/CBC
762 \       2  AES-256/CBC
763 \       3  AES-128/GCM
764 \       4  AES-256/GCM
765 \       5  ChaCha20/Poly1305
766 \       6  AES-128/CCM
767 \       7  AES-256/CCM
768 \       8  AES-128/CCM8
769 \       9  AES-256/CCM8
770 \ -- MAC algorithm:
771 \       0  none         (for suites with AEAD encryption)
772 \       2  HMAC/SHA-1
773 \       4  HMAC/SHA-256
774 \       5  HMAC/SHA-384
775 \ -- PRF for TLS-1.2:
776 \       4  with SHA-256
777 \       5  with SHA-384
778 \
779 \ WARNING: if adding a new cipher suite that does not use SHA-256 for the
780 \ PRF (with TLS 1.2), be sure to check the suites_sha384[] array defined
781 \ in ssl/ssl_keyexport.c
782
783 data: cipher-suite-def
784
785 hexb| 000A 0024 | \ TLS_RSA_WITH_3DES_EDE_CBC_SHA
786 hexb| 002F 0124 | \ TLS_RSA_WITH_AES_128_CBC_SHA
787 hexb| 0035 0224 | \ TLS_RSA_WITH_AES_256_CBC_SHA
788 hexb| 003C 0144 | \ TLS_RSA_WITH_AES_128_CBC_SHA256
789 hexb| 003D 0244 | \ TLS_RSA_WITH_AES_256_CBC_SHA256
790
791 hexb| 009C 0304 | \ TLS_RSA_WITH_AES_128_GCM_SHA256
792 hexb| 009D 0405 | \ TLS_RSA_WITH_AES_256_GCM_SHA384
793
794 hexb| C003 4024 | \ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
795 hexb| C004 4124 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
796 hexb| C005 4224 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
797 hexb| C008 2024 | \ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
798 hexb| C009 2124 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
799 hexb| C00A 2224 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
800 hexb| C00D 3024 | \ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
801 hexb| C00E 3124 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
802 hexb| C00F 3224 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
803 hexb| C012 1024 | \ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
804 hexb| C013 1124 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
805 hexb| C014 1224 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
806
807 hexb| C023 2144 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
808 hexb| C024 2255 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
809 hexb| C025 4144 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
810 hexb| C026 4255 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
811 hexb| C027 1144 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
812 hexb| C028 1255 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
813 hexb| C029 3144 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
814 hexb| C02A 3255 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
815 hexb| C02B 2304 | \ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
816 hexb| C02C 2405 | \ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
817 hexb| C02D 4304 | \ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
818 hexb| C02E 4405 | \ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
819 hexb| C02F 1304 | \ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
820 hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
821 hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
822 hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
823
824 hexb| C09C 0604 | \ TLS_RSA_WITH_AES_128_CCM
825 hexb| C09D 0704 | \ TLS_RSA_WITH_AES_256_CCM
826 hexb| C0A0 0804 | \ TLS_RSA_WITH_AES_128_CCM_8
827 hexb| C0A1 0904 | \ TLS_RSA_WITH_AES_256_CCM_8
828 hexb| C0AC 2604 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM
829 hexb| C0AD 2704 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM
830 hexb| C0AE 2804 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
831 hexb| C0AF 2904 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
832
833 hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
834 hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
835
836 hexb| 0000 | \ List terminator.
837
838 \ Convert cipher suite identifier to element words. This returns 0 if
839 \ the cipher suite is not known.
840 : cipher-suite-to-elements ( suite -- elts )
841         { id }
842         cipher-suite-def
843         begin
844                 dup 2+ swap data-get16
845                 dup ifnot 2drop 0 ret then
846                 id = if data-get16 ret then
847                 2+
848         again ;
849
850 \ Check that a given cipher suite is supported. Note that this also
851 \ returns true (-1) for the TLS_FALLBACK_SCSV pseudo-ciphersuite.
852 : suite-supported? ( suite -- bool )
853         dup 0x5600 = if drop -1 ret then
854         cipher-suite-to-elements 0<> ;
855
856 \ Get expected key type for cipher suite. The key type is one of
857 \ BR_KEYTYPE_RSA or BR_KEYTYPE_EC, combined with either BR_KEYTYPE_KEYX
858 \ (RSA encryption or static ECDH) or BR_KEYTYPE_SIGN (RSA or ECDSA
859 \ signature, for ECDHE cipher suites).
860 : expected-key-type ( suite -- key-type )
861         cipher-suite-to-elements 12 >>
862         case
863                 0 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX } endof
864                 1 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN } endof
865                 2 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_SIGN } endof
866                 3 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
867                 4 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
868                 0 swap
869         endcase ;
870
871 \ Test whether the cipher suite uses RSA key exchange.
872 : use-rsa-keyx? ( suite -- bool )
873         cipher-suite-to-elements 12 >> 0= ;
874
875 \ Test whether the cipher suite uses ECDHE key exchange, signed with RSA.
876 : use-rsa-ecdhe? ( suite -- bool )
877         cipher-suite-to-elements 12 >> 1 = ;
878
879 \ Test whether the cipher suite uses ECDHE key exchange, signed with ECDSA.
880 : use-ecdsa-ecdhe? ( suite -- bool )
881         cipher-suite-to-elements 12 >> 2 = ;
882
883 \ Test whether the cipher suite uses ECDHE key exchange (with RSA or ECDSA).
884 : use-ecdhe? ( suite -- bool )
885         cipher-suite-to-elements 12 >> dup 0> swap 3 < and ;
886
887 \ Test whether the cipher suite uses ECDH (static) key exchange.
888 : use-ecdh? ( suite -- bool )
889         cipher-suite-to-elements 12 >> 2 > ;
890
891 \ Get identifier for the PRF (TLS 1.2).
892 : prf-id ( suite -- id )
893         cipher-suite-to-elements 15 and ;
894
895 \ Test whether a cipher suite is only for TLS-1.2. Cipher suites that
896 \ can be used with TLS-1.0 or 1.1 use HMAC/SHA-1. RFC do not formally
897 \ forbid using a CBC-based TLS-1.2 cipher suite, e.g. based on HMAC/SHA-256,
898 \ with older protocol versions; however, servers should not do that, since
899 \ it may confuse clients. Since the server code does not try such games,
900 \ for consistency, the client should reject it as well (normal servers
901 \ don't do that, so any attempt is a sign of foul play).
902 : use-tls12? ( suite -- bool )
903         cipher-suite-to-elements 0xF0 and 0x20 <> ;
904
905 \ Switch to negotiated security parameters for input or output.
906 : switch-encryption ( is-client for-input -- )
907         { for-input }
908         addr-cipher_suite get16 cipher-suite-to-elements { elts }
909
910         \ prf_id
911         elts 15 and
912
913         \ mac_id
914         elts 4 >> 15 and
915
916         \ cipher type and key length
917         elts 8 >> 15 and case
918                 \ 3DES/CBC
919                 0 of 0 24
920                         for-input if
921                                 switch-cbc-in
922                         else
923                                 switch-cbc-out
924                         then
925                 endof
926
927                 \ AES-128/CBC
928                 1 of 1 16
929                         for-input if
930                                 switch-cbc-in
931                         else
932                                 switch-cbc-out
933                         then
934                 endof
935
936                 \ AES-256/CBC
937                 2 of 1 32
938                         for-input if
939                                 switch-cbc-in
940                         else
941                                 switch-cbc-out
942                         then
943                 endof
944
945                 \ AES-128/GCM
946                 3 of drop 16
947                         for-input if
948                                 switch-aesgcm-in
949                         else
950                                 switch-aesgcm-out
951                         then
952                 endof
953
954                 \ AES-256/GCM
955                 4 of drop 32
956                         for-input if
957                                 switch-aesgcm-in
958                         else
959                                 switch-aesgcm-out
960                         then
961                 endof
962
963                 \ ChaCha20+Poly1305
964                 5 of drop
965                         for-input if
966                                 switch-chapol-in
967                         else
968                                 switch-chapol-out
969                         then
970                 endof
971
972                 \ Now we only have AES/CCM suites (6 to 9). Since the
973                 \ input is between 0 and 15, and we checked values 0 to 5,
974                 \ we only need to reject values larger than 9.
975                 dup 9 > if
976                         ERR_BAD_PARAM fail
977                 then
978
979                 \ Stack: is_client prf_id mac_id cipher_id
980                 \ We want to remove the mac_id (it is zero for CCM suites)
981                 \ and replace the cipher_id with the key and tag lengths.
982                 \ The following table applies:
983                 \  id   key length   tag length
984                 \   6       16          16
985                 \   7       32          16
986                 \   8       16           8
987                 \   9       32           8
988                 swap drop
989                 dup 1 and 4 << 16 + swap
990                 8 and 16 swap -
991                 for-input if
992                         switch-aesccm-in
993                 else
994                         switch-aesccm-out
995                 then
996                 ret
997         endcase
998         ;
999
1000 cc: switch-cbc-out ( is_client prf_id mac_id aes cipher_key_len -- ) {
1001         int is_client, prf_id, mac_id, aes;
1002         unsigned cipher_key_len;
1003
1004         cipher_key_len = T0_POP();
1005         aes = T0_POP();
1006         mac_id = T0_POP();
1007         prf_id = T0_POP();
1008         is_client = T0_POP();
1009         br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id,
1010                 aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len);
1011 }
1012
1013 cc: switch-cbc-in ( is_client prf_id mac_id aes cipher_key_len -- ) {
1014         int is_client, prf_id, mac_id, aes;
1015         unsigned cipher_key_len;
1016
1017         cipher_key_len = T0_POP();
1018         aes = T0_POP();
1019         mac_id = T0_POP();
1020         prf_id = T0_POP();
1021         is_client = T0_POP();
1022         br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id,
1023                 aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len);
1024 }
1025
1026 cc: switch-aesgcm-out ( is_client prf_id cipher_key_len -- ) {
1027         int is_client, prf_id;
1028         unsigned cipher_key_len;
1029
1030         cipher_key_len = T0_POP();
1031         prf_id = T0_POP();
1032         is_client = T0_POP();
1033         br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id,
1034                 ENG->iaes_ctr, cipher_key_len);
1035 }
1036
1037 cc: switch-aesgcm-in ( is_client prf_id cipher_key_len -- ) {
1038         int is_client, prf_id;
1039         unsigned cipher_key_len;
1040
1041         cipher_key_len = T0_POP();
1042         prf_id = T0_POP();
1043         is_client = T0_POP();
1044         br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id,
1045                 ENG->iaes_ctr, cipher_key_len);
1046 }
1047
1048 cc: switch-chapol-out ( is_client prf_id -- ) {
1049         int is_client, prf_id;
1050
1051         prf_id = T0_POP();
1052         is_client = T0_POP();
1053         br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id);
1054 }
1055
1056 cc: switch-chapol-in ( is_client prf_id -- ) {
1057         int is_client, prf_id;
1058
1059         prf_id = T0_POP();
1060         is_client = T0_POP();
1061         br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id);
1062 }
1063
1064 cc: switch-aesccm-out ( is_client prf_id cipher_key_len tag_len -- ) {
1065         int is_client, prf_id;
1066         unsigned cipher_key_len, tag_len;
1067
1068         tag_len = T0_POP();
1069         cipher_key_len = T0_POP();
1070         prf_id = T0_POP();
1071         is_client = T0_POP();
1072         br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id,
1073                 ENG->iaes_ctrcbc, cipher_key_len, tag_len);
1074 }
1075
1076 cc: switch-aesccm-in ( is_client prf_id cipher_key_len tag_len -- ) {
1077         int is_client, prf_id;
1078         unsigned cipher_key_len, tag_len;
1079
1080         tag_len = T0_POP();
1081         cipher_key_len = T0_POP();
1082         prf_id = T0_POP();
1083         is_client = T0_POP();
1084         br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id,
1085                 ENG->iaes_ctrcbc, cipher_key_len, tag_len);
1086 }
1087
1088 \ Write Finished message.
1089 : write-Finished ( from_client -- )
1090         compute-Finished
1091         20 write8 12 write24 addr-pad 12 write-blob ;
1092
1093 \ Read Finished message.
1094 : read-Finished ( from_client -- )
1095         compute-Finished
1096         read-handshake-header 20 <> if ERR_UNEXPECTED fail then
1097         addr-pad 12 + 12 read-blob
1098         close-elt
1099         addr-pad dup 12 + 12 memcmp ifnot ERR_BAD_FINISHED fail then ;
1100
1101 \ Compute the "Finished" contents (either the value to send, or the
1102 \ expected value). The 12-byte string is written in the pad. The
1103 \ "from_client" value is non-zero for the Finished sent by the client.
1104 \ The computed value is also saved in the relevant buffer for handling
1105 \ secure renegotiation.
1106 : compute-Finished ( from_client -- )
1107         dup addr-saved_finished swap ifnot 12 + then swap
1108         addr-cipher_suite get16 prf-id compute-Finished-inner
1109         addr-pad 12 memcpy ;
1110
1111 cc: compute-Finished-inner ( from_client prf_id -- ) {
1112         int prf_id = T0_POP();
1113         int from_client = T0_POPi();
1114         unsigned char tmp[48];
1115         br_tls_prf_seed_chunk seed;
1116
1117         br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
1118         seed.data = tmp;
1119         if (ENG->session.version >= BR_TLS12) {
1120                 seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
1121         } else {
1122                 br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
1123                 br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
1124                 seed.len = 36;
1125         }
1126         prf(ENG->pad, 12, ENG->session.master_secret,
1127                 sizeof ENG->session.master_secret,
1128                 from_client ? "client finished" : "server finished",
1129                 1, &seed);
1130 }
1131
1132 \ Receive ChangeCipherSpec and Finished from the peer.
1133 : read-CCS-Finished ( is-client -- )
1134         has-input? if
1135                 addr-record_type_in get8 20 <> if ERR_UNEXPECTED fail then
1136         else
1137                 begin
1138                         wait-co 0x07 and dup 0x02 <> while
1139                         if ERR_UNEXPECTED fail then
1140                 repeat
1141                 drop
1142         then
1143         read8-nc 1 <> more-incoming-bytes? or if ERR_BAD_CCS fail then
1144         dup 1 switch-encryption
1145
1146         \ Read and verify Finished from peer.
1147         not read-Finished ;
1148
1149 \ Send ChangeCipherSpec and Finished to the peer.
1150 : write-CCS-Finished ( is-client -- )
1151         \ Flush and wait for output buffer to be clear, so that we may
1152         \ write our ChangeCipherSpec. We must switch immediately after
1153         \ triggering the flush.
1154         20 wait-rectype-out
1155         1 write8
1156         flush-record
1157         dup 0 switch-encryption
1158         22 wait-rectype-out
1159         write-Finished
1160         flush-record ;
1161
1162 \ Read and parse a list of supported signature algorithms (with hash
1163 \ functions). The resulting bit field is returned.
1164 : read-list-sign-algos ( lim -- lim value )
1165         0 { hashes }
1166         read16 open-elt
1167         begin dup while
1168                 read8 { hash } read8 { sign }
1169
1170                 \ If hash is 0x08 then this is a "new algorithm" identifier,
1171                 \ and we set the corresponding bit if it is in the 0..15
1172                 \ range. Otherwise, we keep the value only if the signature
1173                 \ is either 1 (RSA) or 3 (ECDSA), and the hash is one of the
1174                 \ SHA-* functions (2 to 6). Note that we reject MD5.
1175                 hash 8 = if
1176                         sign 15 <= if
1177                                 1 sign 16 + << hashes or >hashes
1178                         then
1179                 else
1180                         hash 2 >= hash 6 <= and
1181                         sign 1 = sign 3 = or
1182                         and if
1183                                 hashes 1 sign 1- 2 << hash + << or >hashes
1184                         then
1185                 then
1186         repeat
1187         close-elt
1188         hashes ;
1189
1190 \ =======================================================================
1191
1192 \ Compute total chain length. This includes the individual certificate
1193 \ headers, but not the total chain header. This also sets the cert_cur,
1194 \ cert_len and chain_len context fields.
1195 cc: total-chain-length ( -- len ) {
1196         size_t u;
1197         uint32_t total;
1198
1199         total = 0;
1200         for (u = 0; u < ENG->chain_len; u ++) {
1201                 total += 3 + (uint32_t)ENG->chain[u].data_len;
1202         }
1203         T0_PUSH(total);
1204 }
1205
1206 \ Get length for current certificate in the chain; if the chain end was
1207 \ reached, then this returns -1.
1208 cc: begin-cert ( -- len ) {
1209         if (ENG->chain_len == 0) {
1210                 T0_PUSHi(-1);
1211         } else {
1212                 ENG->cert_cur = ENG->chain->data;
1213                 ENG->cert_len = ENG->chain->data_len;
1214                 ENG->chain ++;
1215                 ENG->chain_len --;
1216                 T0_PUSH(ENG->cert_len);
1217         }
1218 }
1219
1220 \ Copy a chunk of certificate data into the pad. Returned value is the
1221 \ chunk length, or 0 if the certificate end is reached.
1222 cc: copy-cert-chunk ( -- len ) {
1223         size_t clen;
1224
1225         clen = ENG->cert_len;
1226         if (clen > sizeof ENG->pad) {
1227                 clen = sizeof ENG->pad;
1228         }
1229         memcpy(ENG->pad, ENG->cert_cur, clen);
1230         ENG->cert_cur += clen;
1231         ENG->cert_len -= clen;
1232         T0_PUSH(clen);
1233 }
1234
1235 \ Write a Certificate message. Total chain length (excluding the 3-byte
1236 \ header) is returned; it is 0 if the chain is empty.
1237 : write-Certificate ( -- total_chain_len )
1238         11 write8
1239         total-chain-length dup
1240         dup 3 + write24 write24
1241         begin
1242                 begin-cert
1243                 dup 0< if drop ret then write24
1244                 begin copy-cert-chunk dup while
1245                         addr-pad swap write-blob
1246                 repeat
1247                 drop
1248         again ;
1249
1250 cc: x509-start-chain ( by_client -- ) {
1251         const br_x509_class *xc;
1252         uint32_t bc;
1253
1254         bc = T0_POP();
1255         xc = *(ENG->x509ctx);
1256         xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL);
1257 }
1258
1259 cc: x509-start-cert ( length -- ) {
1260         const br_x509_class *xc;
1261
1262         xc = *(ENG->x509ctx);
1263         xc->start_cert(ENG->x509ctx, T0_POP());
1264 }
1265
1266 cc: x509-append ( length -- ) {
1267         const br_x509_class *xc;
1268         size_t len;
1269
1270         xc = *(ENG->x509ctx);
1271         len = T0_POP();
1272         xc->append(ENG->x509ctx, ENG->pad, len);
1273 }
1274
1275 cc: x509-end-cert ( -- ) {
1276         const br_x509_class *xc;
1277
1278         xc = *(ENG->x509ctx);
1279         xc->end_cert(ENG->x509ctx);
1280 }
1281
1282 cc: x509-end-chain ( -- err ) {
1283         const br_x509_class *xc;
1284
1285         xc = *(ENG->x509ctx);
1286         T0_PUSH(xc->end_chain(ENG->x509ctx));
1287 }
1288
1289 cc: get-key-type-usages ( -- key-type-usages ) {
1290         const br_x509_class *xc;
1291         const br_x509_pkey *pk;
1292         unsigned usages;
1293
1294         xc = *(ENG->x509ctx);
1295         pk = xc->get_pkey(ENG->x509ctx, &usages);
1296         if (pk == NULL) {
1297                 T0_PUSH(0);
1298         } else {
1299                 T0_PUSH(pk->key_type | usages);
1300         }
1301 }
1302
1303 \ Read a Certificate message.
1304 \ Parameter: non-zero if this is a read by the client of a certificate
1305 \ sent by the server; zero otherwise.
1306 \ Returned value:
1307 \   - Empty: 0
1308 \   - Valid: combination of key type and allowed key usages.
1309 \   - Invalid: negative (-x for error code x)
1310 : read-Certificate ( by_client -- key-type-usages )
1311         \ Get header, and check message type.
1312         read-handshake-header 11 = ifnot ERR_UNEXPECTED fail then
1313
1314         \ If the chain is empty, do some special processing.
1315         dup 3 = if
1316                 read24 if ERR_BAD_PARAM fail then
1317                 swap drop ret
1318         then
1319
1320         \ Start processing the chain through the X.509 engine.
1321         swap x509-start-chain
1322
1323         \ Total chain length is a 24-bit integer.
1324         read24 open-elt
1325         begin
1326                 dup while
1327                 read24 open-elt
1328                 dup x509-start-cert
1329
1330                 \ We read the certificate by chunks through the pad, so
1331                 \ as to use the existing reading function (read-blob)
1332                 \ that also ensures proper hashing.
1333                 begin
1334                         dup while
1335                         dup 256 > if 256 else dup then { len }
1336                         addr-pad len read-blob
1337                         len x509-append
1338                 repeat
1339                 close-elt
1340                 x509-end-cert
1341         repeat
1342
1343         \ We must close the chain AND the handshake message.
1344         close-elt
1345         close-elt
1346
1347         \ Chain processing is finished; get the error code.
1348         x509-end-chain
1349         dup if neg ret then drop
1350
1351         \ Return key type and usages.
1352         get-key-type-usages ;
1353
1354 \ =======================================================================
1355
1356 \ Copy a specific protocol name from the list to the pad. The byte
1357 \ length is returned.
1358 cc: copy-protocol-name ( idx -- len ) {
1359         size_t idx = T0_POP();
1360         size_t len = strlen(ENG->protocol_names[idx]);
1361         memcpy(ENG->pad, ENG->protocol_names[idx], len);
1362         T0_PUSH(len);
1363 }
1364
1365 \ Compare name in pad with the configured list of protocol names.
1366 \ If a match is found, then the index is returned; otherwise, -1
1367 \ is returned.
1368 cc: test-protocol-name ( len -- n ) {
1369         size_t len = T0_POP();
1370         size_t u;
1371
1372         for (u = 0; u < ENG->protocol_names_num; u ++) {
1373                 const char *name;
1374
1375                 name = ENG->protocol_names[u];
1376                 if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) {
1377                         T0_PUSH(u);
1378                         T0_RET();
1379                 }
1380         }
1381         T0_PUSHi(-1);
1382 }