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