xref: /freebsd/contrib/bearssl/src/ssl/ssl_hs_common.t0 (revision 02e9120893770924227138ba49df1edb3896112a)
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
27preamble {
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.
83cc: 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.
91cc: 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.
101cc: 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).
107cc: 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).
113cc: 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).
119cc: 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).
125cc: 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).
131cc: 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).
137cc: 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
151addr-eng: max_frag_len
152addr-eng: log_max_frag_len
153addr-eng: peer_log_max_frag_len
154addr-eng: shutdown_recv
155addr-eng: record_type_in
156addr-eng: record_type_out
157addr-eng: version_in
158addr-eng: version_out
159addr-eng: application_data
160addr-eng: version_min
161addr-eng: version_max
162addr-eng: suites_buf
163addr-eng: suites_num
164addr-eng: server_name
165addr-eng: client_random
166addr-eng: server_random
167addr-eng: ecdhe_curve
168addr-eng: ecdhe_point
169addr-eng: ecdhe_point_len
170addr-eng: reneg
171addr-eng: saved_finished
172addr-eng: flags
173addr-eng: pad
174addr-eng: action
175addr-eng: alert
176addr-eng: close_received
177addr-eng: protocol_names_num
178addr-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
187addr-session-field: session_id
188addr-session-field: session_id_len
189addr-session-field: version
190addr-session-field: cipher_suite
191addr-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
204err: ERR_OK
205err: ERR_BAD_PARAM
206err: ERR_BAD_STATE
207err: ERR_UNSUPPORTED_VERSION
208err: ERR_BAD_VERSION
209err: ERR_BAD_LENGTH
210err: ERR_TOO_LARGE
211err: ERR_BAD_MAC
212err: ERR_NO_RANDOM
213err: ERR_UNKNOWN_TYPE
214err: ERR_UNEXPECTED
215err: ERR_BAD_CCS
216err: ERR_BAD_ALERT
217err: ERR_BAD_HANDSHAKE
218err: ERR_OVERSIZED_ID
219err: ERR_BAD_CIPHER_SUITE
220err: ERR_BAD_COMPRESSION
221err: ERR_BAD_FRAGLEN
222err: ERR_BAD_SECRENEG
223err: ERR_EXTRA_EXTENSION
224err: ERR_BAD_SNI
225err: ERR_BAD_HELLO_DONE
226err: ERR_LIMIT_EXCEEDED
227err: ERR_BAD_FINISHED
228err: ERR_RESUME_MISMATCH
229err: ERR_INVALID_ALGORITHM
230err: ERR_BAD_SIGNATURE
231err: ERR_WRONG_KEY_USAGE
232err: ERR_NO_CLIENT_AUTH
233
234\ Get supported curves (bit mask).
235cc: 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.
242cc: 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.
259cc: supports-rsa-sign? ( -- bool ) {
260	T0_PUSHi(-(ENG->irsavrfy != 0));
261}
262
263\ Test support for ECDSA signatures.
264cc: supports-ecdsa? ( -- bool ) {
265	T0_PUSHi(-(ENG->iecdsa != 0));
266}
267
268\ (Re)initialise the multihasher.
269cc: 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.
276cc: 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.
408cc: 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.
413cc: has-input? ( -- bool ) {
414	T0_PUSHi(-(ENG->hlen_in != 0));
415}
416
417\ Test whether some payload bytes may be written.
418cc: can-output? ( -- bool ) {
419	T0_PUSHi(-(ENG->hlen_out > 0));
420}
421
422\ Discard current input entirely.
423cc: 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.
431cc: 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.
454cc: 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).
538cc: 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).
603cc: 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
636cc: 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.
669cc: 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.
679cc: 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.
689cc: 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.
695cc: 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).
717cc: 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
783data: cipher-suite-def
784
785hexb| 000A 0024 | \ TLS_RSA_WITH_3DES_EDE_CBC_SHA
786hexb| 002F 0124 | \ TLS_RSA_WITH_AES_128_CBC_SHA
787hexb| 0035 0224 | \ TLS_RSA_WITH_AES_256_CBC_SHA
788hexb| 003C 0144 | \ TLS_RSA_WITH_AES_128_CBC_SHA256
789hexb| 003D 0244 | \ TLS_RSA_WITH_AES_256_CBC_SHA256
790
791hexb| 009C 0304 | \ TLS_RSA_WITH_AES_128_GCM_SHA256
792hexb| 009D 0405 | \ TLS_RSA_WITH_AES_256_GCM_SHA384
793
794hexb| C003 4024 | \ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
795hexb| C004 4124 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
796hexb| C005 4224 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
797hexb| C008 2024 | \ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
798hexb| C009 2124 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
799hexb| C00A 2224 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
800hexb| C00D 3024 | \ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
801hexb| C00E 3124 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
802hexb| C00F 3224 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
803hexb| C012 1024 | \ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
804hexb| C013 1124 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
805hexb| C014 1224 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
806
807hexb| C023 2144 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
808hexb| C024 2255 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
809hexb| C025 4144 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
810hexb| C026 4255 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
811hexb| C027 1144 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
812hexb| C028 1255 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
813hexb| C029 3144 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
814hexb| C02A 3255 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
815hexb| C02B 2304 | \ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
816hexb| C02C 2405 | \ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
817hexb| C02D 4304 | \ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
818hexb| C02E 4405 | \ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
819hexb| C02F 1304 | \ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
820hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
821hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
822hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
823
824hexb| C09C 0604 | \ TLS_RSA_WITH_AES_128_CCM
825hexb| C09D 0704 | \ TLS_RSA_WITH_AES_256_CCM
826hexb| C0A0 0804 | \ TLS_RSA_WITH_AES_128_CCM_8
827hexb| C0A1 0904 | \ TLS_RSA_WITH_AES_256_CCM_8
828hexb| C0AC 2604 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM
829hexb| C0AD 2704 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM
830hexb| C0AE 2804 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
831hexb| C0AF 2904 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
832
833hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
834hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
835
836hexb| 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
1000cc: 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
1013cc: 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
1026cc: 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
1037cc: 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
1048cc: 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
1056cc: 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
1064cc: 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
1076cc: 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
1111cc: 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.
1195cc: 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.
1208cc: 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.
1222cc: 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
1250cc: 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
1259cc: 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
1266cc: 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
1275cc: x509-end-cert ( -- ) {
1276	const br_x509_class *xc;
1277
1278	xc = *(ENG->x509ctx);
1279	xc->end_cert(ENG->x509ctx);
1280}
1281
1282cc: 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
1289cc: 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.
1358cc: 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.
1368cc: 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}
1383