X-Git-Url: https://www.bearssl.org/gitweb//home/git/?p=BearSSL;a=blobdiff_plain;f=src%2Fssl%2Fssl_hs_common.t0;h=4674891c412f22a6b861611ae43eeae2f01c1951;hp=bbd37ac8a4ac7cea27c0b18bd279282c54223d15;hb=c6ffcd29381f555783a75d957c28c0ff861cd33a;hpb=b42bd5972f935ffc32019acac6f8a07ae08ae9c2 diff --git a/src/ssl/ssl_hs_common.t0 b/src/ssl/ssl_hs_common.t0 index bbd37ac..4674891 100644 --- a/src/ssl/ssl_hs_common.t0 +++ b/src/ssl/ssl_hs_common.t0 @@ -34,7 +34,7 @@ preamble { /* * 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))) } @@ -79,6 +79,23 @@ preamble { : 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 -- ! ) { @@ -95,13 +112,13 @@ cc: get8 ( addr -- val ) { \ 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). @@ -113,13 +130,13 @@ cc: set8 ( val addr -- ) { \ 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 @@ -157,6 +174,8 @@ addr-eng: pad 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: @@ -273,7 +292,9 @@ cc: flush-record ( -- ) { 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 @@ -328,13 +349,18 @@ cc: flush-record ( -- ) { \ -- 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 @@ -367,6 +393,10 @@ cc: 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 @@ -467,7 +497,7 @@ cc: read-chunk-native ( addr len -- addr len ) { \ 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 @@ -551,7 +581,7 @@ cc: more-incoming-bytes? ( -- bool ) { 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 @@ -733,6 +763,10 @@ cc: mkrand ( addr len -- ) { \ 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 @@ -741,6 +775,10 @@ cc: mkrand ( addr len -- ) { \ -- 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 @@ -783,6 +821,15 @@ hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 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 @@ -845,6 +892,16 @@ hexb| 0000 | \ List terminator. : 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 } @@ -912,7 +969,31 @@ hexb| 0000 | \ List terminator. then endof - ERR_BAD_PARAM fail + \ 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 + + \ 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 ; @@ -980,6 +1061,30 @@ cc: switch-chapol-in ( is_client prf_id -- ) { 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 @@ -1006,21 +1111,22 @@ cc: switch-chapol-in ( is_client prf_id -- ) { 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. @@ -1060,13 +1166,22 @@ cc: compute-Finished-inner ( from_client prf_id -- ) { 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 @@ -1235,3 +1350,33 @@ cc: get-key-type-usages ( -- key-type-usages ) { \ 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); +}