TLS1.3 OPENSSL 的 EARLY DATA 处理流程 - V2EX
hxndg
V2EX    SSL

TLS1.3 OPENSSL 的 EARLY DATA 处理流程

  •  
  •   hxndg 2019-01-26 01:53:25 +08:00 4026 次点击
    这是一个创建于 2450 天前的主题,其中的信息可能已经有所发展或是发生改变。

    OPENSSL 的代码写的很乱,流程不是十分清楚,TLS1.3 的 EARLY DATA 又是一个穿插错乱的流程,所以写个东西来简单介绍。针对的代码是 S_SERVER 的流程。

    如果启动 S_SERVER 时,支持 early_data,那么 OPENSSL 会调用 SSL_read_early_data 函数:

    int SSL_read_early_data(SSL *s, void *buf, size_t num, size_t *readbytes) { int ret; if (!s->server) { SSLerr(SSL_F_SSL_READ_EARLY_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return SSL_READ_EARLY_DATA_ERROR; } switch (s->early_data_state) { case SSL_EARLY_DATA_NONE: if (!SSL_in_before(s)) { SSLerr(SSL_F_SSL_READ_EARLY_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return SSL_READ_EARLY_DATA_ERROR; } /* fall through */ case SSL_EARLY_DATA_ACCEPT_RETRY: s->early_data_state = SSL_EARLY_DATA_ACCEPTING; ret = SSL_accept(s); if (ret <= 0) { /* NBIO or error */ s->early_data_state = SSL_EARLY_DATA_ACCEPT_RETRY; return SSL_READ_EARLY_DATA_ERROR; } /* fall through */ case SSL_EARLY_DATA_READ_RETRY: if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { s->early_data_state = SSL_EARLY_DATA_READING; ret = SSL_read_ex(s, buf, num, readbytes); /* * State machine will update early_data_state to * SSL_EARLY_DATA_FINISHED_READING if we get an EndOfEarlyData * message */ 

    很有趣,openssl 先是在 SSL_read_early_data 中调用了 SSL_accept 函数,这个函数本质上就是 state_machine,也就是 read_state_machine 和 write_state_machine,read_state_machine 负责处理读取到的 handshake 握手消息,比方说 client_hello。server 转完了 client_hello 的分析过程,并且把参数都保存下来就转入 write_state_machine,write_state_machine 要做的事情比较多,write_state_machine 分别要 construct_server_hello,construct_change_spec(处于兼容性),construct_encrypted_extension。并且调用 ssl->method->do_write 函数,这个函数模板真正指向 ssl3_do_write 函数。这一大段过程就是 SSL_accept 的过程。也就是说 SSL_accept 函数就是服务端的整个握手流程,请参看 TLS1.3 RFC 的 SERVER STATE MACHINE 流程图 visit https://tools.ietf.org/html/rfc8446#page-121

    SSL_accept 握手完成之后,服务端调用 SSL_read_ex(s, buf, num, readbytes)函数读取 early_data 的内容,SSL_read_ex 内部包裹 ssl_read_internal 函数,ssl_read_internal 包裹 s->method->ssl_read,这个函数指针就是 ssl3_read,包裹 ssl3_read_internal 函数,而 ssl3_read_internal 又包裹了 s->method->ssl_read_bytes 也就是 ssl3_read_bytes 函数

    int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, size_t len, int peek, size_t *readbytes) 

    根据 type 的类型读取 payload,存储长度到 len,只有三种 type:SSL3_RT_HANDSHAKE (when ssl3_get_message calls us),SSL3_RT_APPLICATION_DATA (when ssl3_read calls us),0 (during a shutdown, no data has to be returned)。也就是说这个函数只负责读取,更多的内容会由上层负责。值得注意的是,为了能在本层分析报文的 payload,也就是报文的种类( alert,handshake protocol ),该函数会把 payload 的头四个字节存储到 s->rlayer.handshake_fragment 中,然后根据预先假定的 type 类型来分析读到的报文。

    回到刚才的场景,SSL_read_ex 的报文类型是 SSL3_RT_APPLICATION_DATA,下面的代码时调用 SSL3_read_bytes 的函数:

    static int ssl3_read_internal(SSL *s, void *buf, size_t len, int peek, size_t *readbytes) { int ret; clear_sys_error(); if (s->s3->renegotiate) ssl3_renegotiate_check(s, 0); s->s3->in_read_app_data = 1; ret = s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len, peek, readbytes); ... 

    ssl3_read_bytes 函数会不断的读取 payload 到传进去的参数 buf 中,如果这个过程读不到数据,该函数会再次尝试读取出来 pay_load,下面代码在 SSL3_read_bytes 内部:

     do { if (len - totalbytes > SSL3_RECORD_get_length(rr)) n = SSL3_RECORD_get_length(rr); else n = len - totalbytes; memcpy(buf, &(rr->data[rr->off]), n); buf += n; if (peek) { /* Mark any zero length record as consumed CVE-2016-6305 */ if (SSL3_RECORD_get_length(rr) == 0) SSL3_RECORD_set_read(rr); } else { SSL3_RECORD_sub_length(rr, n); SSL3_RECORD_add_off(rr, n); if (SSL3_RECORD_get_length(rr) == 0) { s->rlayer.rstate = SSL_ST_READ_HEADER; SSL3_RECORD_set_off(rr, 0); SSL3_RECORD_set_read(rr); } } if (SSL3_RECORD_get_length(rr) == 0 || (peek && n == SSL3_RECORD_get_length(rr))) { curr_rec++; rr++; } totalbytes += n; } while (type == SSL3_RT_APPLICATION_DATA && curr_rec < num_recs && totalbytes < len); if (totalbytes == 0) { /* We must have read empty records. Get more data */ goto start; } if (!peek && curr_rec == num_recs && (s->mode & SSL_MODE_RELEASE_BUFFERS) && SSL3_BUFFER_get_left(rbuf) == 0) ssl3_release_read_buffer(s); *readbytes = totalbytes; return 1; 

    如果服务端收到一个 SSL3_RT_APPLICATION_DATA,该消息可能史哥 ALERT 消息,或者 HANDSHAKE 消息,如果是 ALERT 消息,ssl3_read_bytes 会利用 PACKET_get_1 来获取具体的 alert number,alert content。非当服务端收到 End of Early Data 报文时,该报文类型为 SSL3_RT_HANDSHAKE 而不是 SSL3_RT_APPLICATION_DATA 报文,说明我们收到了一个握手报文。服务端会尝试收取到足够长度的握手报文,并在此进入握手函数 s->handshake_func 恢复握手过程,下面的代码在 SSL3_read_bytes 内部。

     if ((s->rlayer.handshake_fragment_len >= 4) && !ossl_statem_get_in_handshake(s)) { int ined = (s->early_data_state == SSL_EARLY_DATA_READING); /* We found handshake data, so we're going back into init */ ossl_statem_set_in_init(s, 1); i = s->handshake_func(s); /* SSLfatal() already called if appropriate */ if (i < 0) return i; if (i == 0) { return -1; } 

    进入 handshake_func 也就是 state_machine 的后续流程不再赘述,参照流程图即可。

    握手的函数流程,和一些函数的 API 写不写看心情吧,加上一些链接可以用来学习。 https://www.cnblogs.com/hrhguanli/p/3834585.html https://blog.csdn.net/cwg2552298/article/details/83045448 https://security.stackexchange.com/questions/55667/tls-sequence-number https://tools.ietf.org/html/rfc8446

    第 1 条附言    2019-01-31 14:08:14 +08:00
    忽然想到好多公众号都会干这种事情:随便写点垃圾,然后宣传自己。
    那我宣传一下我司好了( LUL ),ArrayNetworks 公司,负载均衡专家,F 5不敢比,PK   A10 还是没问题的,联通集采中标公司。。。。。哈哈哈
    2 条回复    2019-01-30 11:01:40 +08:00
    isCyan
        1
    isCyan  
       2019-01-29 23:12:36 +08:00 via Android
    可能是干货,虽然看不懂,先挽尊
    hxndg
        2
    hxndg  
    OP
       2019-01-30 11:01:40 +08:00
    @isCyan
    简单的代码分析而已,主要的新东西都在 1.3 的 rfc 里面。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5546 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 08:52 PVG 16:52 LAX 01:52 JFK 04:52
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86