OPENSSL源码阅读(6)
前言
上一回说了非复用状态/完整握手状态下发送EncryptedExtension消息时自动机的变化,这次我们来说说完整握手情况下发送CertificateVerify时发生了什么,这里我们跳过了证书消息发送阶段。
从EncryptedExtension发送完毕说起
我们上次讲了,发送EncryptedExtension之后post_work
啥都没干,然后Certificate消息发送阶段也没做啥有趣的事情。这次就要发送CertificateVerify消息了,因为上次最后修改写自动机状态为写初始状态st->write_state = WRITE_STATE_TRANSITION
,所以还要从while循环说起,啊,需要记住这个时候的握手状态是写证书阶段,即st->hand_state = TLS_ST_SW_CERT
。
写自动机transition函数流程
此次进入transition
函数,当前的握手状态为st->hand_state == TLS_ST_SW_CERT
,因此,新的状态是”服务端写CertificateVerify”阶段,即st->hand_state = TLS_ST_SW_CERT_VRFY;
。返回之后,修改写自动机的写状态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_CERT:
st->hand_state = TLS_ST_SW_CERT_VRFY;
return WRITE_TRAN_CONTINUE;
写自动机pre_work函数流程和CertificateVerify构建流程
进入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;
构建CertificateVerify消息的流程就比较有趣了,ossl_statem_server_construct_message
指定调用的函数tls_construct_cert_verify
函数之后,调用的函数还是tls_construct_cert_verify
,Certificate Verify消息构建完成就执行发送和post_work函数。我们先来看看tls_construct_cert_verify
函数到底干了些什么。
- 首先调用
get_cert_verify_tbs_data
函数获取要签名的内容,根据RFC8446 SECTION4.4.3,我们清楚要签名的内容为‘64字节0x20+字符串”TLS 1.3, server CertificateVerify”+一字节0x00+Transcript-Hash(Handshake Context, Certificate)’,这里的Handshake Context就是Certificate消息之前的所有握手消息。而get_cert_verify_tbs_data
函数就是按这个过程写的,比较简单,不赘述了。 - 获取证书的私钥
pkey = s->s3->tmp.cert->privatekey;
,调用WPACKET_put_bytes_u16(pkt, lu->sigalg)
写入签名算法,接着调用EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey)
初始化签名ctx,即ctx中已经获得了证书的私钥了。 - 如果签名算法是RSA_PSS算法,那么需要加上RSA_PSS的padding,然后加盐,RSA_PSS的流程和RSA_PKCS的计算过程的区别我下回单独开一篇文章写。
- 如果签名算法是ECDSA签名算法,那么不需要加什么多余步骤
- 最后调用
EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen)
函数,获得Certificate Verify内容,返回1。
int tls_construct_cert_verify(SSL *s, WPACKET *pkt)
{
EVP_PKEY *pkey = NULL;
const EVP_MD *md = NULL;
EVP_MD_CTX *mctx = NULL;
EVP_PKEY_CTX *pctx = NULL;
size_t hdatalen = 0, siglen = 0;
void *hdata;
unsigned char *sig = NULL;
unsigned char tls13tbs[TLS13_TBS_PREAMBLE_SIZE + EVP_MAX_MD_SIZE];
const SIGALG_LOOKUP *lu = s->s3->tmp.sigalg;
if (lu == NULL || s->s3->tmp.cert == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_INTERNAL_ERROR);
goto err;
}
pkey = s->s3->tmp.cert->privatekey;
if (pkey == NULL || !tls1_lookup_md(lu, &md)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_INTERNAL_ERROR);
goto err;
}
mctx = EVP_MD_CTX_new();
if (mctx == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_MALLOC_FAILURE);
goto err;
}
/* Get the data to be signed */
if (!get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)) {
/* SSLfatal() already called */
goto err;
}
if (SSL_USE_SIGALGS(s) && !WPACKET_put_bytes_u16(pkt, lu->sigalg)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_INTERNAL_ERROR);
goto err;
}
siglen = EVP_PKEY_size(pkey);
sig = OPENSSL_malloc(siglen);
if (sig == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_MALLOC_FAILURE);
goto err;
}
if (EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_EVP_LIB);
goto err;
}
if (lu->sig == EVP_PKEY_RSA_PSS) {
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0
|| EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx,
RSA_PSS_SALTLEN_DIGEST) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_EVP_LIB);
goto err;
}
}
if (s->version == SSL3_VERSION) {
if (EVP_DigestSignUpdate(mctx, hdata, hdatalen) <= 0
|| !EVP_MD_CTX_ctrl(mctx, EVP_CTRL_SSL3_MASTER_SECRET,
(int)s->session->master_key_length,
s->session->master_key)
|| EVP_DigestSignFinal(mctx, sig, &siglen) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_EVP_LIB);
goto err;
}
} else if (EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
ERR_R_EVP_LIB);
goto err;
}
}
写自动机发送阶段
没啥好说的,写CTX把消息发送出去。
写自动机post_work阶段
post_work函数实际上也啥都没做,直接返回,并将写自动机状态改为写初始状态st->write_state = WRITE_STATE_TRANSITION;
。直接蹦到下个阶段。
结语
这次实际上没咋写,写CTX初始化好了,CertificateVerify构建好了直接发出去,pre_work和post_work啥都没干,construct_func函数构建CertificateVerify消息。
结尾的闲言碎语
写到这里差不多就可以结束了,就不多说了。TLS这块还有啥不明白的直接告诉我就成了