/*
* This macro evaluates to a pointer to the current engine context.
*/
-#define ENG ((br_ssl_engine_context *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
+#define ENG ((br_ssl_engine_context *)(void *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu)))
}
: NYI ( -- ! )
"NOT YET IMPLEMENTED!" puts cr -1 fail ;
+\ Debug function that prints a string (and a newline) on stderr.
+cc: DBG ( addr -- ) {
+ extern void *stderr;
+ extern int fprintf(void *, const char *, ...);
+ fprintf(stderr, "%s\n", &t0_datablock[T0_POPi()]);
+}
+
+\ Debug function that prints a string and an integer value (followed
+\ by a newline) on stderr.
+cc: DBG2 ( addr x -- ) {
+ extern void *stderr;
+ extern int fprintf(void *, const char *, ...);
+ int32_t x = T0_POPi();
+ fprintf(stderr, "%s: %ld (0x%08lX)\n",
+ &t0_datablock[T0_POPi()], (long)x, (unsigned long)(uint32_t)x);
+}
+
\ Mark the context as failed with a specific error code. This also
\ returns control to the caller.
cc: fail ( err -- ! ) {
\ Read a 16-bit word from the context (address is offset in context).
cc: get16 ( addr -- val ) {
size_t addr = (size_t)T0_POP();
- T0_PUSH(*(uint16_t *)((unsigned char *)ENG + addr));
+ T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr));
}
\ Read a 32-bit word from the context (address is offset in context).
cc: get32 ( addr -- val ) {
size_t addr = (size_t)T0_POP();
- T0_PUSH(*(uint32_t *)((unsigned char *)ENG + addr));
+ T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr));
}
\ Set a byte in the context (address is offset in context).
\ Set a 16-bit word in the context (address is offset in context).
cc: set16 ( val addr -- ) {
size_t addr = (size_t)T0_POP();
- *(uint16_t *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
+ *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP();
}
\ Set a 32-bit word in the context (address is offset in context).
cc: set32 ( val addr -- ) {
size_t addr = (size_t)T0_POP();
- *(uint32_t *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
+ *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP();
}
\ Define a word that evaluates as an address of a field within the
addr-eng: action
addr-eng: alert
addr-eng: close_received
+addr-eng: protocol_names_num
+addr-eng: selected_protocol
\ Similar to 'addr-eng:', for fields in the 'session' substructure.
: addr-session-field:
addr-action get8 dup if
case
1 of 0 do-close endof
- 2 of addr-application_data get8 if 0x10 or then endof
+ 2 of addr-application_data get8 1 = if
+ 0x10 or
+ then endof
endcase
else
drop
\ -- If 'cnr' is zero, then incoming data is discarded until a close_notify
\ is received.
\ -- At the end, the context is terminated.
+\
+\ cnr shall be either 0 or -1.
: do-close ( cnr -- ! )
\ 'cnr' is set to non-zero when a close_notify is received from
\ the peer.
{ cnr }
- \ Get out of application data state.
- 0 addr-application_data set8
+ \ Get out of application data state. If we were accepting
+ \ application data (flag is 1), and we still expect a close_notify
+ \ from the peer (cnr is 0), then we should set the flag to 2.
+ \ In all other cases, flag should be set to 0.
+ addr-application_data get8 cnr not and 1 << addr-application_data set8
\ Flush existing payload if any.
flush-record
has-input? if
addr-record_type_in get8 21 = if
drop process-alerts
+ \ If we received a close_notify then we
+ \ no longer accept incoming application
+ \ data records.
+ 0 addr-application_data set8
else
discard-input
then
\ no_renegotiation has value 100, and we treat it
\ as a fatal alert.
dup 100 = if 256 + fail then
- 0= ret
+ 0=
endof
\ Fatal alert implies context termination.
drop 256 + fail
read16 skip-blob ;
\ Open a substructure: the inner structure length is checked against,
-\ and substracted, from the output structure current limit.
+\ and subtracted, from the output structure current limit.
: open-elt ( lim len -- lim-outer lim-inner )
dup { len }
- dup 0< if ERR_BAD_PARAM fail then
\ 3 AES-128/GCM
\ 4 AES-256/GCM
\ 5 ChaCha20/Poly1305
+\ 6 AES-128/CCM
+\ 7 AES-256/CCM
+\ 8 AES-128/CCM8
+\ 9 AES-256/CCM8
\ -- MAC algorithm:
\ 0 none (for suites with AEAD encryption)
\ 2 HMAC/SHA-1
\ -- PRF for TLS-1.2:
\ 4 with SHA-256
\ 5 with SHA-384
+\
+\ WARNING: if adding a new cipher suite that does not use SHA-256 for the
+\ PRF (with TLS 1.2), be sure to check the suites_sha384[] array defined
+\ in ssl/ssl_keyexport.c
data: cipher-suite-def
hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+hexb| C09C 0604 | \ TLS_RSA_WITH_AES_128_CCM
+hexb| C09D 0704 | \ TLS_RSA_WITH_AES_256_CCM
+hexb| C0A0 0804 | \ TLS_RSA_WITH_AES_128_CCM_8
+hexb| C0A1 0904 | \ TLS_RSA_WITH_AES_256_CCM_8
+hexb| C0AC 2604 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM
+hexb| C0AD 2704 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM
+hexb| C0AE 2804 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
+hexb| C0AF 2904 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
+
hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
: prf-id ( suite -- id )
cipher-suite-to-elements 15 and ;
+\ Test whether a cipher suite is only for TLS-1.2. Cipher suites that
+\ can be used with TLS-1.0 or 1.1 use HMAC/SHA-1. RFC do not formally
+\ forbid using a CBC-based TLS-1.2 cipher suite, e.g. based on HMAC/SHA-256,
+\ with older protocol versions; however, servers should not do that, since
+\ it may confuse clients. Since the server code does not try such games,
+\ for consistency, the client should reject it as well (normal servers
+\ don't do that, so any attempt is a sign of foul play).
+: use-tls12? ( suite -- bool )
+ cipher-suite-to-elements 0xF0 and 0x20 <> ;
+
\ Switch to negotiated security parameters for input or output.
: switch-encryption ( is-client for-input -- )
{ for-input }
then
endof
- \ ChaCha20/Poly1305
- \ 5 of endof
+ \ ChaCha20+Poly1305
+ 5 of drop
+ for-input if
+ switch-chapol-in
+ else
+ switch-chapol-out
+ then
+ endof
+
+ \ Now we only have AES/CCM suites (6 to 9). Since the
+ \ input is between 0 and 15, and we checked values 0 to 5,
+ \ we only need to reject values larger than 9.
+ dup 9 > if
+ ERR_BAD_PARAM fail
+ then
- ERR_BAD_PARAM fail
+ \ Stack: is_client prf_id mac_id cipher_id
+ \ We want to remove the mac_id (it is zero for CCM suites)
+ \ and replace the cipher_id with the key and tag lengths.
+ \ The following table applies:
+ \ id key length tag length
+ \ 6 16 16
+ \ 7 32 16
+ \ 8 16 8
+ \ 9 32 8
+ swap drop
+ dup 1 and 4 << 16 + swap
+ 8 and 16 swap -
+ for-input if
+ switch-aesccm-in
+ else
+ switch-aesccm-out
+ then
+ ret
endcase
;
ENG->iaes_ctr, cipher_key_len);
}
+cc: switch-chapol-out ( is_client prf_id -- ) {
+ int is_client, prf_id;
+
+ prf_id = T0_POP();
+ is_client = T0_POP();
+ br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id);
+}
+
+cc: switch-chapol-in ( is_client prf_id -- ) {
+ int is_client, prf_id;
+
+ prf_id = T0_POP();
+ is_client = T0_POP();
+ br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id);
+}
+
+cc: switch-aesccm-out ( is_client prf_id cipher_key_len tag_len -- ) {
+ int is_client, prf_id;
+ unsigned cipher_key_len, tag_len;
+
+ tag_len = T0_POP();
+ cipher_key_len = T0_POP();
+ prf_id = T0_POP();
+ is_client = T0_POP();
+ br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id,
+ ENG->iaes_ctrcbc, cipher_key_len, tag_len);
+}
+
+cc: switch-aesccm-in ( is_client prf_id cipher_key_len tag_len -- ) {
+ int is_client, prf_id;
+ unsigned cipher_key_len, tag_len;
+
+ tag_len = T0_POP();
+ cipher_key_len = T0_POP();
+ prf_id = T0_POP();
+ is_client = T0_POP();
+ br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id,
+ ENG->iaes_ctrcbc, cipher_key_len, tag_len);
+}
+
\ Write Finished message.
: write-Finished ( from_client -- )
compute-Finished
cc: compute-Finished-inner ( from_client prf_id -- ) {
int prf_id = T0_POP();
int from_client = T0_POPi();
- unsigned char seed[48];
- size_t seed_len;
+ unsigned char tmp[48];
+ br_tls_prf_seed_chunk seed;
br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
+ seed.data = tmp;
if (ENG->session.version >= BR_TLS12) {
- seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
+ seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
} else {
- br_multihash_out(&ENG->mhash, br_md5_ID, seed);
- br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
- seed_len = 36;
+ br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
+ br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
+ seed.len = 36;
}
prf(ENG->pad, 12, ENG->session.master_secret,
sizeof ENG->session.master_secret,
from_client ? "client finished" : "server finished",
- seed, seed_len);
+ 1, &seed);
}
\ Receive ChangeCipherSpec and Finished from the peer.
read16 open-elt
begin dup while
read8 { hash } read8 { sign }
- \ We keep the value if the signature is either 1 (RSA)
- \ or 3 (ECDSA), and the hash is one of the SHA-* functions
- \ (2 to 6, from SHA-1 to SHA-512); we reject MD5.
- hash 2 >= hash 6 <= and
- sign 1 = sign 3 = or
- and if
- hashes 1 sign 1- 2 << hash + << or >hashes
+
+ \ If hash is 0x08 then this is a "new algorithm" identifier,
+ \ and we set the corresponding bit if it is in the 0..15
+ \ range. Otherwise, we keep the value only if the signature
+ \ is either 1 (RSA) or 3 (ECDSA), and the hash is one of the
+ \ SHA-* functions (2 to 6). Note that we reject MD5.
+ hash 8 = if
+ sign 15 <= if
+ 1 sign 16 + << hashes or >hashes
+ then
+ else
+ hash 2 >= hash 6 <= and
+ sign 1 = sign 3 = or
+ and if
+ hashes 1 sign 1- 2 << hash + << or >hashes
+ then
then
repeat
close-elt
\ Return key type and usages.
get-key-type-usages ;
+
+\ =======================================================================
+
+\ Copy a specific protocol name from the list to the pad. The byte
+\ length is returned.
+cc: copy-protocol-name ( idx -- len ) {
+ size_t idx = T0_POP();
+ size_t len = strlen(ENG->protocol_names[idx]);
+ memcpy(ENG->pad, ENG->protocol_names[idx], len);
+ T0_PUSH(len);
+}
+
+\ Compare name in pad with the configured list of protocol names.
+\ If a match is found, then the index is returned; otherwise, -1
+\ is returned.
+cc: test-protocol-name ( len -- n ) {
+ size_t len = T0_POP();
+ size_t u;
+
+ for (u = 0; u < ENG->protocol_names_num; u ++) {
+ const char *name;
+
+ name = ENG->protocol_names[u];
+ if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) {
+ T0_PUSH(u);
+ T0_RET();
+ }
+ }
+ T0_PUSHi(-1);
+}