OPENSSL源码阅读(5)
前言
上一回说了非复用状态/完整握手状态下发送ChangeCipherSpec消息时自动机的变化,这次我们来说说完整握手情况下发送EncrypotedExtension时发生了什么。
从ChangeCipherSpec发送完毕说起
我们上次讲了,发送ChangeCipherSpec之后post_work
函数时计算完毕写ctx和读ctx的流程,这次就要发送EncryptedExteions函数了,因为上次最后修改写自动机状态为写初始状态st->write_state = WRITE_STATE_TRANSITION
,所以还要从while循环说起。
写自动机transition函数流程
此次进入transition
函数,当前的握手状态为st->hand_state == TLS_ST_SW_CHANGE
,因此,新的状态是”服务端写EncryptedExtension”阶段,即st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
。返回之后,修改写自动机的写状态st->write_state = WRITE_STATE_PRE_WORK;
继续向下执行,状态转变的代码为:
static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
{
OSSL_STATEM *st = &s->statem;
/*
* No case for TLS_ST_BEFORE, because at that stage we have not negotiated
* TLSv1.3 yet, so that is handled by ossl_statem_server_write_transition()
*/
switch (st->hand_state) {
...
case TLS_ST_SW_CHANGE:
if (s->hello_retry_request == SSL_HRR_PENDING)
st->hand_state = TLS_ST_EARLY_DATA;
else
st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
return WRITE_TRAN_CONTINUE;
写自动机pre_work函数流程和构建EncryptedExtension流程
进入pre_work函数之后实际上啥都没做就返回了,直接返回,继续执行。pre_work函数的代码为:
WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
{
OSSL_STATEM *st = &s->statem;
switch (st->hand_state) {
default:
/* No pre work to be done */
break;
...
}
return WORK_FINISHED_CONTINUE;
构建EncryptedExtension流程就比较有趣了,ossl_statem_server_construct_message
指定调用的函数tls_construct_encrypted_extensions
函数之后,调用的函数还是tls_construct_extensions
,即前几部发送ServerHello报文时写拓展的函数。只不过context == SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
。
分析tls_construct_extensions
函数,实际上还是对全局结构ext_defs
的for循环调用,只不过检查下EncryptedExtension函数要不要写,没什么好说的,返回后代码继续执行。
int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
X509 *x, size_t chainidx)
{
size_t i;
int min_version, max_version = 0, reason;
const EXTENSION_DEFINITION *thisexd;
...
if (!custom_ext_add(s, context, pkt, x, chainidx, max_version)) {
/* SSLfatal() already called */
return 0;
}
for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs); i++, thisexd++) {
EXT_RETURN (*construct)(SSL *s, WPACKET *pkt, unsigned int context,
X509 *x, size_t chainidx);
EXT_RETURN ret;
/* Skip if not relevant for our context */
if (!should_add_extension(s, thisexd->context, context, max_version))
continue;
construct = s->server ? thisexd->construct_stoc
: thisexd->construct_ctos;
if (construct == NULL)
continue;
ret = construct(s, pkt, context, x, chainidx);
if (ret == EXT_RETURN_FAIL) {
/* SSLfatal() already called */
return 0;
}
...
}
if (!WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_EXTENSIONS,
ERR_R_INTERNAL_ERROR);
return 0;
}
return 1;
}
写自动机发送阶段
没啥好说的,写CTX上一步初始化好了,直接发送出去。
写自动机post_work阶段
post_work函数实际上也啥都没做,直接返回,并将写自动机状态改为写初始状态st->write_state = WRITE_STATE_TRANSITION;
。直接蹦到下个阶段,发送Certificate阶段。
结语
这次实际上没咋写,写CTX初始化好了,EncryptedExtension构建好了直接发出去,比较简单,后一步发送Certificate也是一样的流程,pre_work和post_work啥都没干,construct_func函数构建Certificate消息。等到certificateVerify和Finished报文才比较有趣,所以不写Certificate发送阶段了,直接写certificateVerify和finieshed消息了。
结尾的闲言碎语
写到这里差不多就可以结束了,就不多说了。TLS这块还有啥不明白的直接告诉我就成了