Added macro that indicates presence of the time callback feature. Also added C++...
[BearSSL] / src / ssl / ssl_hs_common.t0
index aa67ee8..4674891 100644 (file)
@@ -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
@@ -275,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
@@ -330,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
@@ -369,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
@@ -553,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
@@ -735,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
@@ -743,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
 
@@ -785,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
 
@@ -847,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 }
@@ -914,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
        ;
 
@@ -982,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
@@ -1008,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.