IKEv2协议中的EAP-TLS认证处理流程

以下根据strongswan代码中的testing/tests/ikev2/rw-eap-tls-only/中的测试环境,验证一下IKEv2协议的EAP-TLS认证过程。拓扑结构如下:

在这里插入图片描述

拓扑图中使用到的设备包括:虚拟网关moon和carol主机。

carol主机配置

carol主机的配置文件:/etc/ipsec.conf,内容如下。leftauth字段设置为eap认证,rightauth的值为any。

conn %default
        keyexchange=ikev2

conn home
        left=PH_IP_CAROL
        leftcert=carolCert.pem
        leftauth=eap
        leftfirewall=yes
        right=PH_IP_MOON
        rightid="C=CH, O=strongSwan Project, CN=moon.strongswan.org"
        rightauth=any
        rightsubnet=10.1.0.0/16
        rightsendcert=never
        auto=add

字段rightsendcert的值设置为never,对应于strongswan代码中的CERT_NEVER_SEND(2),表示不发送本端证书。如果不设置,默认值为CERT_SEND_IF_ASKED(1),当请求时发送证书。另外设置为send,对应CERT_ALWAYS_SEND(0),意味着,总是发送证书,而不管对端有没有进行请求。

文件strongswan-5.8.1/src/starter/confread.c中的函数conn_defaults负责为连接服务默认值,如下的值CERT_SEND_IF_ASKED。

static void conn_defaults(starter_conn_t *conn)
{
        conn->left.sendcert = CERT_SEND_IF_ASKED;
        conn->right.sendcert = CERT_SEND_IF_ASKED;

carol主机的配置文件:/etc/strongswan.conf,内容如下。此处需要加载eap-tls插件。字段multiple_authentication为零,表明当前仅使用EAP一种认证方式。

charon {
  load = random nonce aes md5 sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac gcm stroke kernel-netlink socket-default eap-tls updown

  multiple_authentication=no
  syslog {
    daemon {
      tls = 2
    }
  }
}

moon网关配置

moon网关的配置文件:/etc/ipsec.conf ,内容如下。认证方式leftauth和rightauth字段的值都设置为eap-tls。

conn %default
        keyexchange=ikev2

conn rw-eap
        left=PH_IP_MOON
        leftsubnet=10.1.0.0/16
        leftcert=moonCert.pem
        leftauth=eap-tls
        leftfirewall=yes
        rightauth=eap-tls
        rightsendcert=never
        right=%any
        auto=add

moon网关的配置文件:/etc/strongswan.conf,内容如下。加载eap-tls插件。

charon {
  load = random nonce aes md5 sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac gcm stroke kernel-netlink socket-default eap-tls updown

  multiple_authentication=no
  syslog {
    daemon {
      tls = 2
    }
  }
}

libtls {
  suites = TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
}

配置解析

文件strongswan-5.8.1/src/libcharon/plugins/stroke/stroke_config.c中的函数add负责处理ipsec.conf配置的添加处理,其中函数build_ike_cfg处理本地(left*)的配置;而build_peer_cfg函数处理对端(right*)的配置。

METHOD(stroke_config_t, add, void, private_stroke_config_t *this, stroke_msg_t *msg)
{
        ike_cfg_t *ike_cfg, *existing_ike;
        peer_cfg_t *peer_cfg, *existing;

        ike_cfg = build_ike_cfg(this, msg);
        if (!ike_cfg) {
                return;
        }
        peer_cfg = build_peer_cfg(this, msg, ike_cfg);
        if (!peer_cfg) {
                ike_cfg->destroy(ike_cfg);
                return;

如下函数build_ike_cfg在创建本地ike配置时,对于成员变量no_certreq的值,使用ipsec.conf配置文件中的对端的sendcert配置(rightsendcert),由于配置为never,此处将no_certreq设置为真。

static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
{
        ike_cfg_create_t ike;
        ike_cfg_t *ike_cfg;

        ike = (ike_cfg_create_t){
                ...
                .no_certreq = msg->add_conn.other.sendcert == CERT_NEVER_SEND,

如下函数build_peer_cfg在创建对端配置时,cert_policy证书策略使用本地的sendcert配置(leftsendcert)的值。由于此项未配置,使用默认的CERT_SEND_IF_ASKED。

static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, stroke_msg_t *msg, ike_cfg_t *ike_cfg)
{
        peer_cfg_t *peer_cfg;
        auth_cfg_t *auth_cfg;
        peer_cfg_create_t peer = {
                .cert_policy = msg->add_conn.me.sendcert,
                ...
        };
		
        /* build leftauth= */
        auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE);
        if (auth_cfg) {
                peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
        } else {       
                peer_cfg->destroy(peer_cfg);  /* we require at least one config on our side */
                return NULL;
        }
        /* build rightauth= */
        auth_cfg = build_auth_cfg(this, msg, FALSE, TRUE);
        if (auth_cfg) {
                peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
        }

在以上函数build_peer_cfg中,也将调用build_auth_cfg创建auth认证相关结构。其参数local标识本地或者对端,参数primary标识主认证方式或者第二认证方式。以上在ipsec.conf文件中配置了leftauth和rightauth分别为本端和对端的主认证方式。第二认证方式使用leftauth2或者rightauth2关键字配置,此处为设置。

对于本地(local为真)的情况,由于未配置leftid字段,使用本端的IP地址(PH_IP_CAROL)作为ID值。无论是本地还是对端的配置,都没有设置ca字段,此处ca为空,随后使用函数auth_cfg_create创建初始的认证配置结构体auth_cfg_t。

/* build authentication config
 */
static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, stroke_msg_t *msg, bool local, bool primary)
{
        identification_t *identity;
        certificate_t *certificate;
        char *auth, *id, *pubkey, *cert, *ca, *groups;
        stroke_end_t *end, *other_end;
        auth_cfg_t *cfg;
        bool loose = FALSE;

        /* select strings */
        if (local) {
                end = &msg->add_conn.me;
                other_end = &msg->add_conn.other;
        } else {
                end = &msg->add_conn.other;
                other_end = &msg->add_conn.me;
        }
        if (primary) {
                auth = end->auth;
                id = end->id;
                if (!id) {       /* leftid/rightid fallback to address */
                        id = end->address;
                }
                cert = end->cert;
                ca = end->ca;
        } 
        if (!auth) { if (primary) { auth = "pubkey"; } }
        cfg = auth_cfg_create();

以下的代码,如果配置文件中指定了证书,加载证书文件,位于strongswan-5.8.1/src/libcharon/plugins/stroke/stroke_cred.c中的函数load_peer负责加载证书,如果为指定证书目录,使用默认的目录CERTIFICATE_DIR(/etc//ipsec.d/certs/)。并且确认配置的ID信息,与证书中的subject identity是否一致,如果不一致,将使用证书中的subject identity作为端点的ID,而不使用leftid或者rightid配置的值。

        /* add identity and peer certificate */
        identity = identification_create_from_string(id);
        if (cert) {
                enumerator = enumerator_create_token(cert, ",", " ");
                while (enumerator->enumerate(enumerator, &cert))
                {
                        certificate = this->cred->load_peer(this->cred, cert);
                        if (certificate) {
                                if (local) { this->ca->check_for_hash_and_url(this->ca, certificate); }
                                cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
                                if (!first) 
                                        first = certificate;
                                if (identity->get_type(identity) != ID_ANY && certificate->has_subject(certificate, identity))
                                        has_subject = TRUE;
                        }
                }

                if (first && !has_subject) {
                        DBG1(DBG_CFG, "  id '%Y' not confirmed by certificate, defaulting to '%Y'", identity, first->get_subject(first));
                        identity->destroy(identity);
                        identity = first->get_subject(first);
                        identity = identity->clone(identity);
        }

对于此处的carol和moon的ipsec.conf配置,只有carol的配置明确指定了rightid字段的值。其它的配置使用证书中的subject identity的值作为id。carol和moon都将rightsendcert字段设置为never,并且都没有设置本端leftsendcert字段的值(默认值CERT_SEND_IF_ASKED),意味值auth_cfg_t结构将为本端(left方向)端点添加AUTH_RULE_CERT_POLICY规则;而不会为对端反向(right)添加此规则。

        if (identity->get_type(identity) != ID_ANY) {
                cfg->add(cfg, AUTH_RULE_IDENTITY, identity);
                if (loose) {
                        cfg->add(cfg, AUTH_RULE_IDENTITY_LOOSE, TRUE);
                }
        }

        if (end->cert_policy) {            /* certificatePolicies */
                enumerator_t *enumerator;
                char *policy;

                enumerator = enumerator_create_token(end->cert_policy, ",", " ");
                while (enumerator->enumerate(enumerator, &policy)) {
                        cfg->add(cfg, AUTH_RULE_CERT_POLICY, strdup(policy));
                }
                enumerator->destroy(enumerator);
        }

carol的leftauth设置为eap,moon的leftauth和rightauth都设置为eap-tls,唯有carol的rightauth设置为any,其将使用默认的值pubkey,此处为此增加AUTH_CLASS_PUBKEY配置。

        /* authentication method (class, actually) */
        if (strpfx(auth, "ike:") || strpfx(auth, "pubkey") || strpfx(auth, "rsa") || strpfx(auth, "ecdsa") || strpfx(auth, "bliss")) {
                cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
                build_crl_policy(cfg, local, msg->add_conn.crl_policy);
                cfg->add_pubkey_constraints(cfg, auth, TRUE);
        }

对于eap相关认证,auth_cfg_t结构增加AUTH_CLASS_EAP配置。对于carol仅指定了eap,未指定type和vendor的值。对于moon网关,指定了eap-tls,其中type的值为tls(EAP_TLS = 13),其也未指定vendor值。

最后eap_identity和aaa_identity都未在配置文件中指定。

        else if (strpfx(auth, "eap")) {
                eap_vendor_type_t *type;

                cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
                pos = strchr(auth, ':');              /* check for public key constraints for EAP-TLS etc. */
                if (pos) {
                        *pos = 0;
                        cfg->add_pubkey_constraints(cfg, pos + 1, FALSE);
                }
                type = eap_vendor_type_from_string(auth);
                if (type) {
                        cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type);
                        if (type->vendor) {  cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor); }
                        free(type);
                }

                if (msg->add_conn.eap_identity) {
                        if (streq(msg->add_conn.eap_identity, "%identity")) {
                                identity = identification_create_from_encoding(ID_ANY, chunk_empty);
                        } else {
                                identity = identification_create_from_string(msg->add_conn.eap_identity);
                        }
                        cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, identity);
                }
                if (msg->add_conn.aaa_identity)
                        cfg->add(cfg, AUTH_RULE_AAA_IDENTITY, identification_create_from_string(msg->add_conn.aaa_identity));
        } 
        return cfg;

至此,已经为carol主机创建了两个auth_cfg_t结构,对应leftauth=eap和rightauth=any。同样的在moon网关上,也为leftauth=eap-tls和rightauth=eap-tls创建了两个对应的auth_cfg_t结构。

eap-tls插件

文件strongswan-5.8.1/src/libcharon/plugins/eap_tls/eap_tls_plugin.c中函数eap_tls_plugin_create创建eap_tls_plugin_t插件结构。

plugin_t *eap_tls_plugin_create()
{
        eap_tls_plugin_t *this;

        INIT(this,
                .plugin = {
                        .get_name = _get_name,
                        .get_features = _get_features,
                        .destroy = _destroy,
                },
        );

        return &this->plugin;

EAP-TLS认证初始化

文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/ike_auth.c中函数build_i在构建initiator的IKE_AUTH第一个报文时,即MID等于1,增加了EAP_ONLY_AUTHENTICATION类型的通知载荷。

METHOD(task_t, build_i, status_t, private_ike_auth_t *this, message_t *message)
{
        auth_cfg_t *cfg;

        if (!this->peer_cfg) {
                this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
                this->peer_cfg->get_ref(this->peer_cfg);
        }

        if (message->get_message_id(message) == 1)
        {       /* in the first IKE_AUTH ... */
                if (this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH))
                {       /* indicate support for multiple authentication */
                        message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED, chunk_empty);
                }
                /* indicate support for EAP-only authentication */
                message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION, chunk_empty);

同文件中的函数process_r处理responder端接收到的IKE_AUTH消息,如果MID等于1,为第一个消息,并且其中包含有EAP_ONLY_AUTHENTICATION载荷,使能此EAP扩展。

METHOD(task_t, process_r, status_t, private_ike_auth_t *this, message_t *message)
{
        auth_cfg_t *cfg, *cand;
        id_payload_t *id_payload;
        identification_t *id;

        if (message->get_message_id(message) == 1)
        {       /* check for extensions in the first IKE_AUTH */
                if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED)) {
                        this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH);
                }
                if (message->get_notify(message, EAP_ONLY_AUTHENTICATION)) {
                        this->ike_sa->enable_extension(this->ike_sa, EXT_EAP_ONLY_AUTHENTICATION);
                }
        }

接下来,使用函数load_cfg_candidates根据获得的本端和对端的地址信息以及ID信息(参考以上接收,由证书中获得),查找本地匹配的连接配置,此处将找到moon网关的ipsec.conf文件中配置的rw-eap连接。

        if (!this->other_auth)
        {
                /* handle IDi payload */
                id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_INITIATOR);

                id = id_payload->get_identification(id_payload);
                get_reserved_id_bytes(this, id_payload);
                this->ike_sa->set_other_id(this->ike_sa, id);
                cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
                cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id));

                if (!this->peer_cfg) {
                        if (!load_cfg_candidates(this)) {
                                this->authentication_failed = TRUE;
                                return NEED_MORE;
                        }
                }

由于指定了EAP_ONLY,carol的IKE_AUTH报文中没有Authentication载荷。moon网关的auth配置指定了eap-tls,即EAP_TYPE为TLS,之后使用函数authenticator_create_verifier创建authenticator,负责认证工作。其内部调用了函数eap_authenticator_create_verifier创建了eap_authenticator_t类型的认证器(authenticator)。参见文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c中的内容。

                if (!message->get_payload(message, PLV2_AUTH))
                {       
                        cand = get_auth_cfg(this, FALSE);   /* before authenticating with EAP, we need a EAP config */

                        /* copy over the EAP specific rules for authentication */
                        cfg->add(cfg, AUTH_RULE_EAP_TYPE, cand->get(cand, AUTH_RULE_EAP_TYPE));
                        cfg->add(cfg, AUTH_RULE_EAP_VENDOR, cand->get(cand, AUTH_RULE_EAP_VENDOR));
                }
                /* verify authentication data */
                this->other_auth = authenticator_create_verifier(this->ike_sa,
                                                        message, this->other_nonce, this->my_nonce,
                                                        this->other_packet->get_data(this->other_packet),
                                                        this->my_packet->get_data(this->my_packet),
                                                        this->reserved);
                if (!this->other_auth) {
                        this->authentication_failed = TRUE;
                        return NEED_MORE;
                }
        }

接下来,调用以上创建的eap_authenticator_t类型的认证器的处理函数process,处理接收的报文。实际的函数位于文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c中的process_server,稍后介绍。

另外,目前strongswan支持的总线授权authorize包括:TKM(Trusted Key Manager),Whitelist,ext_auth和certexpire插件,这里都没有配置。

        switch (this->other_auth->process(this->other_auth, message))
        {
                case SUCCESS:
                        this->other_auth->destroy(this->other_auth);
                        this->other_auth = NULL;
                        break;
                case NEED_MORE:
                        if (message->get_payload(message, PLV2_AUTH))
                        {       /* AUTH verification successful, but another build() needed */
                                break;
                        }
                        return NEED_MORE;
                default:
                        this->authentication_failed = TRUE;
                        return NEED_MORE;
        }

        if (!charon->bus->authorize(charon->bus, FALSE)) {  /* another auth round done, invoke authorize hook */
                DBG1(DBG_IKE, "authorization hook forbids IKE_SA, cancelling");
                this->authentication_failed = TRUE;
                return NEED_MORE;
        }
EAP相关结构

文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c中函数process_server首次调用时,EAP认证未完成eap_complete为空。而认证方式method还未初始化,这里执行server_initiate_eap函数。

METHOD(authenticator_t, process_server, status_t, private_eap_authenticator_t *this, message_t *message)
{
        eap_payload_t *eap_payload;

        if (this->eap_complete) {
                if (!verify_auth(this, message, this->sent_nonce, this->received_init)) {
                        return FAILED;
                }
                if (this->method->get_auth) {
                        auth_cfg_t *auth;
                        auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
                        auth->merge(auth, this->method->get_auth(this->method), FALSE);
                }
                return NEED_MORE;
        }

        if (!this->method) {
                this->eap_payload = server_initiate_eap(this, TRUE);
        } else {
                eap_payload = (eap_payload_t*)message->get_payload(message, PLV2_EAP);
                if (!eap_payload) {  return FAILED; }
                this->eap_payload = server_process_eap(this, eap_payload);
        }

EAP服务端初始化函数server_initiate_eap如下,首先需要根据配置的type和vendor加载相应的方法methold,这里为eap-tls,角色为EAP_SERVER,由函数load_method完成。其中将调用将遍历所有注册的EAP方法,与传入的三个参数进行对比,对匹配到的项eap_entry_t,调用其构造函数constructor。这里为函数eap_tls_create_server构造函数。之后,调用method的初始化函数initiate。

static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,  bool do_identity)
{
        auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);

        /* invoke real EAP method */
        type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
        vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
        action = "loading";
        this->method = load_method(this, type, vendor, EAP_SERVER);
        if (this->method) {
                action = "initiating";
                if (this->method->initiate(this->method, &out) == NEED_MORE) {
                        type = this->method->get_type(this->method, &vendor);
                        if (vendor) {
                                DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method (id 0x%02X)", type, vendor, out->get_identifier(out));
                        } else {
                                DBG1(DBG_IKE, "initiating %N method (id 0x%02X)", eap_type_names, type, out->get_identifier(out));
                        }
                        return out;
                }

文件strongswan-5.8.1/src/libcharon/plugins/eap_tls/eap_tls.c中的函数 eap_tls_create_server负责eap_tls类型的EAP方法结构体。具体工做由同文件中的函数eap_tls_create完成。其中可见,EAP-TLS报文最大的分片长度默认为MAX_FRAGMENT_LEN(1024),最大允许分片数量为MAX_MESSAGE_COUNT(32),另外,include_length字段默认值为TRUE,表明EAP报文中需要包含EAP-TLS长度字段。

接下来是调用tls_create函数创建tls_t结构,以及tls_eap_create函数创建tls_eap_t结构。

/* Generic private constructor */
static eap_tls_t *eap_tls_create(identification_t *server, identification_t *peer, bool is_server)
{
        private_eap_tls_t *this;
        tls_t *tls;

        INIT(this,
                .public = {
                        .eap_method = {
                                .initiate = _initiate,
                                .process = _process,
                                ...
        );
        frag_size = lib->settings->get_int(lib->settings,       "%s.plugins.eap-tls.fragment_size", MAX_FRAGMENT_LEN, lib->ns);
        max_msg_count = lib->settings->get_int(lib->settings,   "%s.plugins.eap-tls.max_message_count", MAX_MESSAGE_COUNT, lib->ns);
        include_length = lib->settings->get_bool(lib->settings, "%s.plugins.eap-tls.include_length", TRUE, lib->ns);
		
        tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL, NULL);
        this->tls_eap = tls_eap_create(EAP_TLS, tls, frag_size, max_msg_count, include_length);

文件strongswan-5.8.1/src/libtls/tls.c中的函数tls_create负责创建tls_t结构。子函数tls_crypto_create创建加密使用的tls_crypto_t结构。对于服务端,如moon网关,使用tls_server_create创建服务端实现TLS握手handshake协议的结构tls_server_t。而对于客户端,如carol主机,其实现TLS Handshake协议的结构tls_peer_t由函数tls_peer_create创建。

另外,TLS报文的分片处理结构tls_fragmentation_t,压缩处理结构tls_compression_t和保护结构tls_protection_t,分别由函数tls_fragmentation_create、tls_compression_create和tls_protection_create进行创建,注意三者的初始化顺序。

tls_t *tls_create(bool is_server, identification_t *server, identification_t *peer, tls_purpose_t purpose,                 
                                  tls_application_t *application, tls_cache_t *cache)
{       
        private_tls_t *this;
        INIT(this,
                .public = {
                        .process = _process,
                        .build = _build,
                        ...
                },
                .is_server = is_server,
                .version = TLS_1_2,
                .application = application,
                .purpose = purpose,
        );
        lib->settings->add_fallback(lib->settings, "%s.tls", "libtls", lib->ns);

        this->crypto = tls_crypto_create(&this->public, cache);
        this->alert = tls_alert_create();
        if (is_server) {
                this->handshake = &tls_server_create(&this->public, this->crypto, this->alert, server, peer)->handshake;
        } else {
                this->handshake = &tls_peer_create(&this->public, this->crypto, this->alert, peer, server)->handshake;
        }
        this->fragmentation = tls_fragmentation_create(this->handshake, this->alert, this->application, purpose);
        this->compression = tls_compression_create(this->fragmentation, this->alert);
        this->protection = tls_protection_create(this->compression, this->alert);
        this->crypto->set_protection(this->crypto, this->protection);

文件strongswan-5.8.1/src/libtls/tls_crypto.c函数tls_crypto_create创建tls_crypto_t结构,用于TLS相关的加解密和完整性校验相关操作。子函数build_cipher_suite_list初始化在moon网关的strongswan.conf文件中指定的两个加密套件:TLS_DHE_RSA_WITH_AES_128_GCM_SHA256和TLS_DHE_RSA_WITH_AES_256_GCM_SHA384。

tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
{
        INIT(this,
                .public = {
                        .get_cipher_suites = _get_cipher_suites,
                        .select_cipher_suite = _select_cipher_suite,
                        .get_dh_group = _get_dh_group,
                        .get_signature_algorithms = _get_signature_algorithms,
                        .create_ec_enumerator = _create_ec_enumerator,
                        .set_protection = _set_protection,
                        .append_handshake = _append_handshake,
                        .sign = _sign,
                        .verify = _verify,
                        .sign_handshake = _sign_handshake,
                        .verify_handshake = _verify_handshake,
                        .calculate_finished = _calculate_finished,
                        .derive_secrets = _derive_secrets,
                        .resume_session = _resume_session,
                        .get_session = _get_session,
                        .change_cipher = _change_cipher,
                },
                .tls = tls,
                .cache = cache,
        );
        switch (tls->get_purpose(tls)) {               
                case TLS_PURPOSE_EAP_TLS:                  /* MSK PRF ASCII constant label according to EAP-TLS RFC 5216 */
                        this->msk_label = "client EAP encryption";
                        build_cipher_suite_list(this, FALSE);
                        break;

文件strongswan-5.8.1/src/libtls/tls_eap.c中函数tls_eap_create负责创建private_tls_eap_t结构,是一些简单的赋值操作。

tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size, int max_msg_count, bool include_length)
{                                                                                                
        private_tls_eap_t *this;

        INIT(this,
                .public = {
                        .initiate = _initiate,                   
                        .process = _process,
                        ...             
                },
                .type = type,
                .is_server = tls->is_server(tls),
                .first_fragment = (type != EAP_TNC && type != EAP_PT_EAP),
                .frag_size = frag_size,
                .max_msg_count = max_msg_count,
                .include_length = include_length,
                .tls = tls,
        );

carol的第一个IKE_AUTH报文

以下为carol的第一个IKE_AUTH报文,可见其中的EAP_ONLY载荷。

在这里插入图片描述

第一个EAP报文

在以上介绍的函数server_initiate_eap使用load_method函数完成EAP方法结构的初始化之后,接下来调用EAP方法的initiator函数,发送第一个EAP报文,即由moon网关发送给carol。此initiator函数为文件strongswan-5.8.1/src/libcharon/plugins/eap_tls/eap_tls.c中的函数initiate,如下所示。其调用了eap_method_t结构成员tls_eap的initiate函数。

METHOD(eap_method_t, initiate, status_t, private_eap_tls_t *this, eap_payload_t **out)
{
        chunk_t data;

        if (this->tls_eap->initiate(this->tls_eap, &data) == NEED_MORE)
        {
                *out = eap_payload_create_data(data);
                free(data.ptr);
                return NEED_MORE;

其为文件strongswan-5.8.1/src/libtls/tls_eap.c中的initiate函数,仅对服务端有效。其中type为EAP-TLS(13),code为EAP_REQUEST(1),标志flags字段为EAP_TLS_START。此报文的长度为6个字节。

METHOD(tls_eap_t, initiate, status_t,  private_tls_eap_t *this, chunk_t *out)
{       
        if (this->is_server)
        {       
                eap_tls_packet_t pkt = {
                        .type = this->type,
                        .code = EAP_REQUEST,
                        .flags = this->supported_version
                };
                switch (this->type) {       
                        case EAP_TLS:
                                pkt.flags |= EAP_TLS_START;
                                break;
                }
                htoun16(&pkt.length, sizeof(eap_tls_packet_t));
                pkt.identifier = this->identifier;
                
                *out = chunk_clone(chunk_from_thing(pkt));
                DBG2(DBG_TLS, "sending %N start packet (%u bytes)", eap_type_names, this->type, sizeof(eap_tls_packet_t));

最后子函数eap_payload_create_data将数据封装到eap_payload_t结构内。报文最终由文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中的函数build_response发送出去。

eap_payload_t *eap_payload_create_data(chunk_t data)
{               
        eap_payload_t *this = eap_payload_create();
        
        this->set_data(this, data);
        return this;

以下为第一个EAP报文的内容:

在这里插入图片描述

ClientHello报文发送

位于文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/ike_auth.c中的函数process_i负责处理moon网关发送的第一个EAP报文。首先看一下build_i函数,其中carol发送第一个IKE_AUTH报文时,使用authenticator_create_builder函数构建了认证器结构authenticator_t。实际上内部调用的是eap_authenticator_create_builder函数,创建了EAP类型的eap_authenticator_t认证器。

METHOD(task_t, build_i, status_t, private_ike_auth_t *this, message_t *message)
{
        auth_cfg_t *cfg;

        if (!this->my_auth)
        {
                /* build authentication data */
                this->my_auth = authenticator_create_builder(this->ike_sa, cfg,
                                                        this->other_nonce, this->my_nonce,
                                                        this->other_packet->get_data(this->other_packet),
                                                        this->my_packet->get_data(this->my_packet),
                                                        this->reserved);

之后在接收到moon网关的第一个EAP报文之后,由同文件中的process_i函数中将使用以上创建的my_auth认证器进行处理。调用其process函数处理moon网关的EAP报文。只有在认证结束之后才有可能返回SUCCESS,通常情况下都是NEED_MORE。

METHOD(task_t, process_i, status_t,  private_ike_auth_t *this, message_t *message)
{
        if (this->my_auth) {
                switch (this->my_auth->process(this->my_auth, message)) {
                        case SUCCESS:
                                apply_auth_cfg(this, TRUE);
                                if (this->my_auth->is_mutual(this->my_auth)) {
                                        apply_auth_cfg(this, FALSE);
                                }
                                this->my_auth->destroy(this->my_auth);
                                this->my_auth = NULL;
                                this->do_another_auth = do_another_auth(this);
                                break;
                        case NEED_MORE:
                                break;

此处理函数为文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c中的process_client函数。其获取报文中的EAP载荷,根据载荷中的code值,此处为EAP_REQUEST,调用client_process_eap函数进行处理。

METHOD(authenticator_t, process_client, status_t, private_eap_authenticator_t *this, message_t *message)
{
        if (this->eap_complete) {       
                if (!verify_auth(this, message, this->sent_nonce, this->received_init)) {       
                        return FAILED;
                }
                return SUCCESS;
        }
        eap_payload = (eap_payload_t*)message->get_payload(message, PLV2_EAP);
        if (eap_payload) {
                switch (eap_payload->get_code(eap_payload)) {
                        case EAP_REQUEST: {
                                this->eap_payload = client_process_eap(this, eap_payload);
                                if (this->eap_payload) { return NEED_MORE;  }
                                return FAILED;
                        }

在函数client_process_eap中,由于TLS方法method之前还未确定,此时根据server端的要求,此处为EAP-TLS,加载相应的method,由函数load_method完成,此处角色为EAP_PEER。随后,使用EAP方法的process函数处理报文。

static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, eap_payload_t *in)
{
        if (this->method == NULL) {
                {
                        DBG1(DBG_IKE, "server requested %N authentication (id 0x%02X)", eap_type_names, type, in->get_identifier(in));
                }
                auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
                conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
                conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);

                this->method = load_method(this, type, vendor, EAP_PEER);
                if (!this->method) {
                        DBG1(DBG_IKE, "EAP method not supported, sending EAP_NAK");
                        return eap_payload_create_nak(in->get_identifier(in), 0, 0,  in->is_expanded(in));
                }
        }
        type = this->method->get_type(this->method, &vendor);

        if (this->method->process(this->method, in, &out) == NEED_MORE) {  /* client methods should never return SUCCESS */
                return out;
        }

文件strongswan-5.8.1/src/libcharon/plugins/eap_tls/eap_tls.c中函数eap_tls_create_peer负责EAP方法的加载,其调用子函数eap_tls_create完成。在以上的server端(moon)函数eap_tls_create_server中,也是使用此子函数,此处不再累述。以下为其process函数,调用了其成员tls_eap_t结构中的process函数。随后,使用函数eap_payload_create_data将返回数据封装在EAP类型载荷中。

METHOD(eap_method_t, process, status_t, private_eap_tls_t *this, eap_payload_t *in, eap_payload_t **out)
{
        status_t status;
        chunk_t data;

        data = in->get_data(in);
        status = this->tls_eap->process(this->tls_eap, data, &data);
        if (status == NEED_MORE) {
                *out = eap_payload_create_data(data);
                free(data.ptr);
        }

文件strongswan-5.8.1/src/libtls/tls_eap.c中的函数process如下。对于非EAP_TLS_START报文,将使用函数process_pkt进行处理;反之,对于EAP_TLS_START报文,不需要进行处理,直接使用build_pkt创建回复报文(ClientHello)。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)in.ptr;

        {
                if (in.len == sizeof(eap_tls_packet_t)) {
                        DBG2(DBG_TLS, "received %N acknowledgement packet", eap_type_names, this->type);
                        status = build_pkt(this, out);
                        if (status == INVALID_STATE && this->tls->is_complete(this->tls))  {
                                return SUCCESS;
                        }
                        return status;
                }
                status = process_pkt(this, pkt);
                ...
        }
        status = build_pkt(this, out);

如下为build_pkt函数,完成部分eap_tls头部内容的填充,并调用tls_t结构中的build函数完成报文内容的填充。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +  msg_len_offset, &len, &reclen);

文件strongswan-5.8.1/src/libtls/tls.c中函数build如下所示。其使用tls_protection_t结构的build函数构建数据,返回type和data数据块。此处type值为TLS_HANDSHAKE(22),record的版本version值为0x0303,为TLS 1.2版本。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
        tls_content_type_t type;
        tls_record_t record;

        len = *buflen;
        if (this->output.len == 0)
        {
                /* query upper layers for new records, as many as we can get */
                while (TRUE)
                {
                        status = this->protection->build(this->protection, &type, &data);
                        switch (status) {
                                case NEED_MORE:
                                        record.type = type;
                                        htoun16(&record.version, this->version);
                                        htoun16(&record.length, data.len);
                                        this->output = chunk_cat("mcm", this->output, chunk_from_thing(record), data);
                                        DBG2(DBG_TLS, "sending TLS %N record (%d bytes)", tls_content_type_names, type, data.len);
                                        continue;

文件strongswan-5.8.1/src/libtls/tls_protection.c中定义了tls_protection_t结构的build函数,如下所示。其又调用了tls_compression_t结构的build函数。

METHOD(tls_protection_t, build, status_t, private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
{
        status = this->compression->build(this->compression, type, data);
        if (status == NEED_MORE) {
                if (*type == TLS_CHANGE_CIPHER_SPEC) {
                        this->seq_out = 0;
                        return status;
                }
                if (this->aead_out) {
                        if (!this->aead_out->encrypt(this->aead_out, this->version, *type, this->seq_out, data))
                        {

文件strongswan-5.8.1/src/libtls/tls_compression.c中实现了tls_compression_t结构的build函数,如下所示。目前tls_compession相关功能并没有实现,其直接调用tls_fragmentation_t结构的成员函数build。

METHOD(tls_compression_t, build, status_t, private_tls_compression_t *this, tls_content_type_t *type, chunk_t *data)
{
        return this->fragmentation->build(this->fragmentation, type, data);
}

文件strongswan-5.8.1/src/libtls/tls_fragmentation.c中实现了tls_fragmentation_t结构的build函数,如下所示。这里将调用函数build_handshake创建handshake报文。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
        if (!this->output.len) {       
                if (this->handshake->cipherspec_changed(this->handshake, FALSE))  {       
                        this->handshake->change_cipherspec(this->handshake, FALSE);
                        *type = TLS_CHANGE_CIPHER_SPEC;
                        *data = chunk_clone(chunk_from_chars(0x01));
                        return NEED_MORE;
                }
                if (!this->handshake->finished(this->handshake)) {       
                        status = build_handshake(this);
                }
                else if (this->application) {       
                        status = build_application(this);
                }
        }
        if (this->output.len) {       
                *type = this->output_type;
                if (this->output.len <= TLS_MAX_FRAGMENT_LEN) {       
                        *data = this->output;
                        this->output = chunk_empty;
                        return NEED_MORE;
                }
                *data = chunk_create(this->output.ptr, TLS_MAX_FRAGMENT_LEN);
                this->output = chunk_clone(chunk_skip(this->output, TLS_MAX_FRAGMENT_LEN));
                return NEED_MORE;

同一个文件中的函数build_handshake如下,其调用tls_handshake_t结构的build函数创建handshake协议报文。

static status_t build_handshake(private_tls_fragmentation_t *this)
{
        bio_writer_t *hs, *msg;
        tls_handshake_type_t type;
        status_t status;

        msg = bio_writer_create(64);
        while (TRUE) {
                hs = bio_writer_create(64);
                status = this->handshake->build(this->handshake, &type, hs);
                switch (status) {
                        case NEED_MORE:
                                if (this->alert->fatal(this->alert)) { break; }       
                                msg->write_uint8(msg, type);
                                msg->write_data24(msg, hs->get_buf(hs));
                                DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len);

文件strongswan-5.8.1/src/libtls/tls_peer.c中的build函数处理handshake协议报文的创建。对于服务端(moon),由文件tls_server.c中文件处理。这里由函数send_client_hello处理发送ClientHello报文。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        switch (this->state) {
                case STATE_INIT:
                        return send_client_hello(this, type, writer);
                case STATE_HELLO_DONE:
                        if (this->peer) {
                                return send_certificate(this, type, writer);
                        }
                        /* otherwise fall through to next state */
                case STATE_CERT_SENT:
                        return send_key_exchange(this, type, writer);
                case STATE_KEY_EXCHANGE_SENT:
                        if (this->peer) {
                                return send_certificate_verify(this, type, writer);
                        } else {
                                return INVALID_STATE;
                        }
                case STATE_CIPHERSPEC_CHANGED_OUT:
                        return send_finished(this, type, writer);

同文件中的函数send_client_hello内容如下。其创建ClientHello类型的Handshake协议报文,内容包括:Type/Length/Version,Random,Session ID,TLS cipher suites,Compression Methods和Signature算法扩展。随后状态变更为STATE_HELLO_SENT,调用tls_crypto_t结构的函数append_handshake添加报文数据。

注意由于是创建一个新的TLS连接,Session ID字段为零。

static status_t send_client_hello(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        tls_cipher_suite_t *suites;
        bio_writer_t *extensions, *curves = NULL;
        tls_version_t version;
        tls_named_curve_t curve;

        htoun32(&this->client_random, time(NULL));
        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);

        /* TLS version */
        version = this->tls->get_version(this->tls);
        this->hello_version = version;
        writer->write_uint16(writer, version);
        writer->write_data(writer, chunk_from_thing(this->client_random));

        /* session identifier */
        this->session = this->crypto->get_session(this->crypto, this->server);
        writer->write_data8(writer, this->session);

        /* add TLS cipher suites */
        count = this->crypto->get_cipher_suites(this->crypto, &suites);
        writer->write_uint16(writer, count * 2);
        for (i = 0; i < count; i++) {
                writer->write_uint16(writer, suites[i]);
        }

        /* NULL compression only */
        writer->write_uint8(writer, 1);
        writer->write_uint8(writer, 0);

        extensions = bio_writer_create(32);

        extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
        this->crypto->get_signature_algorithms(this->crypto, extensions);
        ...

        *type = TLS_CLIENT_HELLO;
        this->state = STATE_HELLO_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));

文件strongswan-5.8.1/src/libtls/tls_crypto.c中实现了tls_crypto_t结构的函数append_handshake,其将报文数据追加到handshake缓存块中。最终调用文件task_manager_v2.c中的函数generate_message生成IKE_AUTH数据包,并使用retransmit函数发送出去。

METHOD(tls_crypto_t, append_handshake, void, private_tls_crypto_t *this, tls_handshake_type_t type, chunk_t data)
{
        uint32_t header;

        /* reconstruct handshake header */
        header = htonl(data.len | (type << 24));
        this->handshake = chunk_cat("mcc", this->handshake, chunk_from_thing(header), data);

以下为ClientHello报文的截图,可参照以上函数send_client_hello对比查看其中的内容。

在这里插入图片描述

ClientHello报文处理

文件strongswan-5.8.1/src/libcharon/processing/jobs/process_message_job.c中的函数execute为接收消息处理入口。其调用ike_sa_t结构的成员函数process_message进行消息处理。

METHOD(job_t, execute, job_requeue_t, private_process_message_job_t *this)
{
        ike_sa_t *ike_sa;

        ike_sa = charon->ike_sa_manager->checkout_by_message(charon->ike_sa_manager, this->message); 
        if (ike_sa)                                                                                              
        {
                DBG1(DBG_NET, "received packet: from %#H to %#H (%zu bytes)", this->message->get_source(this->message),
                         this->message->get_destination(this->message), this->message->get_packet_data(this->message).len);
                if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME)
                {

ike_sa_t结构的函数process_message位于文件strongswan-5.8.1/src/libcharon/sa/ike_sa.c中,其调用了task_manager_t结构的process_message函数。

METHOD(ike_sa_t, process_message, status_t, private_ike_sa_t *this, message_t *message)
{
        status_t status;

        status = this->task_manager->process_message(this->task_manager, message);

文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中实现了task_manager_t结构的process_message函数。首先使用parse_message函数解析报文内容;由于此处接收到的是carol发送的ClientHello报文,其ISAKMP头部中的flag标志Response为0,表示为request报文,以下跟踪request分支代码。其使用handle_fragment处理报文分片,最后使用process_request函数处理carol的请求内容。

对于handle_fragment函数而言,其处理IKE协议的分片报文,但是对于EAP,其支持处理分片,所以EAP原始报文的分片在EAP层进行,IKE层封装的EAP报文都不需要进行分片。

METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg)
{
        charon->bus->message(charon->bus, msg, TRUE, FALSE);
        status = parse_message(this, msg);

        mid = msg->get_message_id(msg);
        if (msg->get_request(msg)) 
        {
                if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg)))
                {                                                                     
				        ...
                        status = handle_fragment(this, &this->responding.defrag, msg);
                        if (status != SUCCESS) {
                                ...
                                return status;
                        }

                        switch (process_request(this, msg)) {
                                case SUCCESS:
                                        this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, time_monotonic(NULL));
                                        this->responding.mid++;
                                        break;
                                case NEED_MORE:
                                        break;

以下我们看process_request函数。对于INFORMATIONAL等类型的报文可直接在process_request函数中进行处理;但是对于IKE_AUTH类型的报文,需要启动tasks来进行处理,这里启动TASK_IKE_AUTH处理。最后,由函数build_response创建回复报文。

static status_t process_request(private_task_manager_t *this, message_t *message)
{
        /* let the tasks process the message */
        enumerator = array_create_enumerator(this->passive_tasks);
        while (enumerator->enumerate(enumerator, (void*)&task))
        {
                switch (task->process(task, message))
                {
                        ...
                }
        }

        return build_response(this, message);

文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/ike_auth.c中的函数process_r为moon网关处理接收到的ClientHello报文。这里其调用类型authenticator_t的认证器成员变量other_auth的process处理函数指针。

METHOD(task_t, process_r, status_t,  private_ike_auth_t *this, message_t *message)
{
        switch (this->other_auth->process(this->other_auth, message))
        {

此处使用的是类型为ap_authenticator_t的认证器,位于文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c中。其处理函数为以下的process_server(moon网关作为认证服务器)。主要处理函数为server_process_eap。

METHOD(authenticator_t, process_server, status_t, private_eap_authenticator_t *this, message_t *message)
{
        eap_payload_t *eap_payload;

        if (this->eap_complete) {  ...  }

        if (!this->method) {  ...  }
        else
        {
                eap_payload = (eap_payload_t*)message->get_payload(message, PLV2_EAP);
                if (!eap_payload) {
                        return FAILED;
                }
                this->eap_payload = server_process_eap(this, eap_payload);
        }

函数server_process_eap的主体主要是对认证方法结构体成员函数process的调用。函数开通的CODE判断需要注意,虽然对于IKE协议而言,carol发送的ClientHello报文为Request,但是对于内部的EAP协议的CODE字段其为Response。

/* Handle EAP exchange as server
 */
static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, eap_payload_t *in)
{
        if (in->get_code(in) != EAP_RESPONSE) {
                DBG1(DBG_IKE, "received %N, sending %N", eap_code_names, in->get_code(in), eap_code_names, EAP_FAILURE);
                return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
        }

        switch (this->method->process(this->method, in, &out))
        {
                case NEED_MORE:
                        return out;

EAP-TLS方法的处理位于文件strongswan-5.8.1/src/libcharon/plugins/eap_tls/eap_tls.c中,由函数process实现。其主体是调用eap_tls_t结构的process处理函数。而后由函数eap_payload_create_data封装返回数据。

METHOD(eap_method_t, process, status_t, private_eap_tls_t *this, eap_payload_t *in, eap_payload_t **out)
{
        status_t status;
        chunk_t data;

        data = in->get_data(in);
        status = this->tls_eap->process(this->tls_eap, data, &data);
        if (status == NEED_MORE) {
                *out = eap_payload_create_data(data);
                free(data.ptr);
        }
        return status;

文件strongswan-5.8.1/src/libtls/tls_eap.c中实现了结构tls_eap_t的成员函数process。由于这是carol对moon网关EAP_TLS_START报文的回复,这里使用process_pkt函数进行处理。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;
        pkt = (eap_tls_packet_t*)in.ptr;

        if ((this->type == EAP_PT_EAP && (pkt->flags & EAP_PT_START)) || (pkt->flags & EAP_TLS_START)) {  ...  }
        else
        {
                if (in.len == sizeof(eap_tls_packet_t)) {  ...  }
                status = process_pkt(this, pkt);
                switch (status)
                {
                        case NEED_MORE:
                                break;
                        case SUCCESS:
                                return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
                        default:
                                return status;
                }
        }

在函数process_pkt中,根据报文中的标志EAP_TLS_LENGTH,获取到EAP消息的长度值。最后调用tls_t结构的成员函数process进行处理。

static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
{       
        uint16_t pkt_len;
        uint32_t msg_len;
        size_t msg_len_offset = 0;
        
        pkt_len = untoh16(&pkt->length);
        
        if (this->type != EAP_PT_EAP && (pkt->flags & EAP_TLS_LENGTH)) {       
                msg_len = untoh32(pkt + 1);
                if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) || msg_len > TLS_MAX_MESSAGE_LEN) {       
                        DBG1(DBG_TLS, "invalid %N packet length (%u bytes)", eap_type_names, this->type, msg_len);
                        return FAILED;
                }
                msg_len_offset = sizeof(msg_len);
        }
        return this->tls->process(this->tls, (char*)(pkt + 1) + msg_len_offset, pkt_len - sizeof(eap_tls_packet_t) - msg_len_offset);

文件strongswan-5.8.1/src/libtls/tls.c中实现了tls_t结构,process函数如下。首先解析封装在EAP头部之后的TLS Record头部,获取到Record消息的长度值,调用tls_protection_t结构的process函数处理。由于一个报文中可同时包含多个Record消息,这里进行循环处理。注意最后判断buflen是否等于0,等于零表示是一个完整的报文;否则,表示一个分片报文,仅接收到了一部分数据。

METHOD(tls_t, process, status_t, private_tls_t *this, void *buf, size_t buflen)
{
        tls_record_t *record;

        while (buflen)
        {
                if (this->input.len == 0) {       
                        while (buflen >= sizeof(tls_record_t)) {       
                                /* try to process records inline */
                                record = buf;
                                len = untoh16(&record->length);
                                
                                if (len + sizeof(tls_record_t) > buflen) {       /* not a full record, read to buffer */
                                        this->input = chunk_alloc(len + sizeof(tls_record_t));
                                        this->inpos = 0;
                                        break;
                                }
                                DBG2(DBG_TLS, "processing TLS %N record (%d bytes)", tls_content_type_names, record->type, len);
                                status = this->protection->process(this->protection, record->type, chunk_create(record->data, len));
                                if (status != NEED_MORE) {       
                                        return status;
                                }
                                buf += len + sizeof(tls_record_t);
                                buflen -= len + sizeof(tls_record_t);
                                if (buflen == 0) {  return NEED_MORE; }
                        }
                }

以下,len表示本次接收的报文的大小,this->inpos表示目前为止接收到的总大小,而this->input.len表示原始报文的总长度。以下的if判断在接收到完整的报文之后,进行处理。

                len = min(buflen, this->input.len - this->inpos);
                memcpy(this->input.ptr + this->inpos, buf, len);
                buf += len;
                buflen -= len;
                this->inpos += len;
                DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received", len, this->inpos, this->input.len);
                if (this->input.len == this->inpos) {
                        record = (tls_record_t*)this->input.ptr;
                        len = untoh16(&record->length);

                        DBG2(DBG_TLS, "processing buffered TLS %N record (%d bytes)", tls_content_type_names, record->type, len);
                        status = this->protection->process(this->protection, record->type, chunk_create(record->data, len));
                        chunk_free(&this->input);
                        this->inpos = 0;

carol发送的ClientHello报文未分片,直接使用文件strongswan-5.8.1/src/libtls/tls_protection.c中的函数process进行处理。其主体为调用tls_compression_t结构的process函数。由于目前未实现compress功能,其直接调用了tls_fragmentation_t结构的process函数。

METHOD(tls_protection_t, process, status_t, private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
{
        if (this->aead_in) {  ... }

        if (type == TLS_CHANGE_CIPHER_SPEC) {
                this->seq_in = 0;
        }  else {
                this->seq_in++;
        }
        return this->compression->process(this->compression, type, data);

文件strongswan-5.8.1/src/libtls/tls_fragmentation.c实现了结构tls_fragmentation_t,其process函数如下。

METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
{
        bio_reader_t *reader;

        reader = bio_reader_create(data);
        switch (type)
        {
                case TLS_HANDSHAKE:
                        status = process_handshake(this, reader);
                        break;

在函数process_handshake中,首先根据数据的长度值,分配chunk_t结构的input指针。对于完整接收的报文,调用tls_handshake_t结构的process函数进行处理。

static status_t process_handshake(private_tls_fragmentation_t *this, bio_reader_t *reader)
{
        while (reader->remaining(reader)) {
                if (this->input.len == 0)  {       /* new handshake message */
                        if (!reader->read_uint8(reader, &type) || !reader->read_uint24(reader, &len)) {
                                ...
                                return NEED_MORE;
                        }
                        this->type = type;
                        chunk_free(&this->input);
                        this->inpos = 0;
                        if (len) {   this->input = chunk_alloc(len);  }
                }

                len = min(this->input.len - this->inpos, reader->remaining(reader));
                memcpy(this->input.ptr + this->inpos, data.ptr, len);
                this->inpos += len;

                if (this->input.len == this->inpos) {             /* message completely defragmented, process */
                        msg = bio_reader_create(this->input);
                        DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)", tls_handshake_type_names, this->type, this->input.len);
                        status = this->handshake->process(this->handshake, this->type, msg);
                        msg->destroy(msg);
                        chunk_free(&this->input);

文件strongswan-5.8.1/src/libtls/tls_server.c中实现了TLS服务端的tls_handshake_t处理结构,如下process函数根据当前状态

METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state)
        {
                case STATE_INIT:
                        if (type == TLS_CLIENT_HELLO) {
                                return process_client_hello(this, reader);
                        }
                        expected = TLS_CLIENT_HELLO;
                        break;
                case STATE_HELLO_DONE:

函数process_client_hello实现如下。首先在加密结构tls_crypto_t中保存一份报文拷贝。之后一次读取报文中的version,random,session,ciphers和compression字段,如果还有数据没有读完,读取extension数据。这里carol的ClientHello报文中有Signature算法扩展数据。

static status_t process_client_hello(private_tls_server_t *this, bio_reader_t *reader)
{
        chunk_t random, session, ciphers, compression, ext = chunk_empty;
        bio_reader_t *extensions;
        tls_cipher_suite_t *suites;

        this->crypto->append_handshake(this->crypto, TLS_CLIENT_HELLO, reader->peek(reader));

        if (!reader->read_uint16(reader, &version) ||
                !reader->read_data(reader, sizeof(this->client_random), &random) ||
                !reader->read_data8(reader, &session) ||
                !reader->read_data16(reader, &ciphers) ||
                !reader->read_data8(reader, &compression) ||
                (reader->remaining(reader) && !reader->read_data16(reader, &ext)))
        {
                DBG1(DBG_TLS, "received invalid ClientHello");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }

接下来读取TLS_EXT_SIGNATURE_ALGORITHMS(13)类型的扩展数据,保存到private_tls_server_t结构的成员hashsig中。

        if (ext.len) {
                extensions = bio_reader_create(ext);
                while (extensions->remaining(extensions)) {
                        if (!extensions->read_uint16(extensions, &extension) || !extensions->read_data16(extensions, &ext)) {
                                ...
                                return NEED_MORE;
                        }
                        DBG2(DBG_TLS, "received TLS '%N' extension", tls_extension_names, extension);
                        switch (extension) {
                                case TLS_EXT_SIGNATURE_ALGORITHMS:
                                        this->hashsig = chunk_clone(ext);
                                        break;
                                case TLS_EXT_ELLIPTIC_CURVES:
                                        ...

以下处理random和version版本相关数据,并且选择合适的加密套件,由于carol在ClientHello报文中通告的suites与在moon网关的配置中指定了TLS_DHE_RSA_WITH_AES_128_GCM_SHA256向匹配,使用此套件。最后将状态修改为STATE_HELLO_RECEIVED。

        memcpy(this->client_random, random.ptr, sizeof(this->client_random));

        htoun32(&this->server_random, time(NULL));
        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        if (!rng || !rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4)) {
                ...
                return NEED_MORE;
        }
        rng->destroy(rng);

        if (!this->tls->set_version(this->tls, version)) {
                DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version);
                this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
                return NEED_MORE;
        }

        this->client_version = version;
        this->suite = this->crypto->resume_session(this->crypto, session, this->peer,
                                                                                chunk_from_thing(this->client_random),
                                                                                chunk_from_thing(this->server_random));
        if (this->suite) {
                ...
        } else {
                count = ciphers.len / sizeof(uint16_t);
                suites = alloca(count * sizeof(tls_cipher_suite_t));
                DBG2(DBG_TLS, "received %d TLS cipher suites:", count);
                for (i = 0; i < count; i++) {
                        suites[i] = untoh16(&ciphers.ptr[i * sizeof(uint16_t)]);
                        DBG2(DBG_TLS, "  %N", tls_cipher_suite_names, suites[i]);
                }
                if (!select_suite_and_key(this, suites, count)) {
                        this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
                        return NEED_MORE;
                }
                rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
                if (!rng || !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) {
                        DBG1(DBG_TLS, "generating TLS session identifier failed, skipped");
                }
                DESTROY_IF(rng);
                DBG1(DBG_TLS, "negotiated %N using suite %N", tls_version_names, this->tls->get_version(this->tls),
                         tls_cipher_suite_names, this->suite);
        }
        this->state = STATE_HELLO_RECEIVED;

ServerHello报文

此报文由moon网关发出,回复carol之前的ClientHello报文。文件strongswan-5.8.1/src/libtls/tls_eap.c中的函数process在处理完carol的ClientHello报文之后,使用build_pkt创建回复报文(ServerHello)。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)in.ptr;

        {
                status = process_pkt(this, pkt);
                ...
        }
        status = build_pkt(this, out);

如下函数build_pkt,对于moon网关而言,其为EAP服务端,将ID值递增一。初始化EAP协议报文的头部信息,Code为请求EAP_REQUEST,Type类型为EAP-TLS(13)。随后,调用tls_t结构的build函数完成报文组建。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        if (this->is_server) {
                this->identifier++;
        }
        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        if (this->first_fragment) {
                len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
                msg_len_offset = sizeof(uint32_t);
        }

        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + msg_len_offset, &len, &reclen);
		
        switch (status) {
                case NEED_MORE:
                        pkt->flags |= EAP_TLS_MORE_FRAGS;
                        kind = "further fragment";
                        if (this->first_fragment) {
                                pkt->flags |= EAP_TLS_LENGTH;
                                this->first_fragment = FALSE;
                                kind = "first fragment";
                        }

文件strongswan-5.8.1/src/libtls/tls.c实现了tls_t结构,成员函数build如下。其主体使用tls_protection_t结构的build函数,之后初始化TLS Record协议头部信息:type/version/length。类型type值应为TLS_HANDSHAKE(22)。注意这里的while循环,如果protection->build返回NEED_MORE,循环将继续。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
        tls_content_type_t type;
        tls_record_t record;

        len = *buflen;
        if (this->output.len == 0) {       
                while (TRUE) {       
                        status = this->protection->build(this->protection, &type, &data);
                        switch (status) {       
                                case NEED_MORE:
                                        record.type = type;
                                        htoun16(&record.version, this->version);
                                        htoun16(&record.length, data.len);
                                        this->output = chunk_cat("mcm", this->output, chunk_from_thing(record), data);
                                        DBG2(DBG_TLS, "sending TLS %N record (%d bytes)", tls_content_type_names, type, data.len);
                                        continue;

根据之前的介绍可知,tls_protection_t结构的build函数调用了tls_compression_t结构的build函数,而其又调用了tls_fragmentation_t结构的build函数。其实现位于文件strongswan-5.8.1/src/libtls/tls_fragmentation.c中。其中调用build_handshake函数构建handshake消息。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status = INVALID_STATE;

        if (!this->output.len) {
                if (this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                        this->handshake->change_cipherspec(this->handshake, FALSE);
                        *type = TLS_CHANGE_CIPHER_SPEC;
                        *data = chunk_clone(chunk_from_chars(0x01));
                        return NEED_MORE;
                }
                if (!this->handshake->finished(this->handshake)) {
                        status = build_handshake(this);
                }

如下build_handshake函数实现,其调用了tls_handshake_t结构体的成员函数build。

/* Build handshake message
 */
static status_t build_handshake(private_tls_fragmentation_t *this)
{
        bio_writer_t *hs, *msg;
        tls_handshake_type_t type;

        msg = bio_writer_create(64);
        while (TRUE) {
                hs = bio_writer_create(64);
                status = this->handshake->build(this->handshake, &type, hs);

文件strongswan-5.8.1/src/libtls/tls_server.c实现了tls_handshake_t结构的build函数。由于在上一节的函数process_client_hello中将TLS的状态修改为了STATE_HELLO_RECEIVED,此处调用函数send_server_hello发送ServerHello报文。

METHOD(tls_handshake_t, build, status_t,  private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        diffie_hellman_group_t group;

        switch (this->state) {
                case STATE_HELLO_RECEIVED:
                        return send_server_hello(this, type, writer);
                case STATE_HELLO_SENT:

由如下函数send_server_hello可见,ServerHello报文比较简单,包括version,server-random,session-id,服务端选择的cipher suite,最后是一个空的compression字段。之后将TLS状态变更为STATE_HELLO_SENT。

static status_t send_server_hello(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        /* TLS version */
        writer->write_uint16(writer, this->tls->get_version(this->tls));
        writer->write_data(writer, chunk_from_thing(this->server_random));

        /* session identifier if we have one */
        writer->write_data8(writer, this->session);

        /* add selected TLS cipher suite */
        writer->write_uint16(writer, this->suite);

        /* NULL compression only */
        writer->write_uint8(writer, 0);

        *type = TLS_SERVER_HELLO;
        this->state = STATE_HELLO_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

以下为ServerHello报文的内容。

在这里插入图片描述

服务器Certificate报文

注意上节函数send_server_hello最后的返回值NEED_MORE,在以上文件strongswan-5.8.1/src/libtls/tls.c中的build函数中,提到如果protection->build返回NEED_MORE,循环将继续调用protection->build,执行流程将再次进入文件strongswan-5.8.1/src/libtls/tls_server.c的build函数,由于在函数send_server_hello中已经将TLS状态修改为STATE_HELLO_SENT,此时调用函数send_certificate。

METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        diffie_hellman_group_t group;

        switch (this->state)
        {
                case STATE_HELLO_RECEIVED:
                        return send_server_hello(this, type, writer);
                case STATE_HELLO_SENT:
                        return send_certificate(this, type, writer);

函数send_certificate如下,首先使用auth_cfg_t结构类型成员变量server_auth的get函数获取到配置文件中指定的证书的内容(certificate_t类型)。随后将TLS状态变更为STATE_CERT_SENT,最后将证书内容添加到tls_crypto_t结构中。

static status_t send_certificate(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        certificate_t *cert;
        auth_rule_t rule;

        certs = bio_writer_create(256);
        cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT);
        if (cert) {
                if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) {
                        DBG1(DBG_TLS, "sending TLS server certificate '%Y'", cert->get_subject(cert));
                        certs->write_data24(certs, data);
                        free(data.ptr);
                }
        }
        enumerator = this->server_auth->create_enumerator(this->server_auth);
        while (enumerator->enumerate(enumerator, &rule, &cert)) {
                if (rule == AUTH_RULE_IM_CERT) {  ...  }
        }
        enumerator->destroy(enumerator);

        writer->write_data24(writer, certs->get_buf(certs));
        certs->destroy(certs);

        *type = TLS_CERTIFICATE;
        this->state = STATE_CERT_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

以下为Cerfiticate报文的内容。

在这里插入图片描述

ServerKeyExchange报文

当循环再次进入文件strongswan-5.8.1/src/libtls/tls_server.c的build函数时,由于在函数send_certificate中已经将TLS状态修改为STATE_CERT_SENT,此时调用函数this->crypto->get_dh_group进行处理。

METHOD(tls_handshake_t, build, status_t,  private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        diffie_hellman_group_t group;

        switch (this->state)
        {
                case STATE_HELLO_RECEIVED:
                        return send_server_hello(this, type, writer);
                case STATE_HELLO_SENT:
                        return send_certificate(this, type, writer);
                case STATE_CERT_SENT:
                        group = this->crypto->get_dh_group(this->crypto);
                        if (group) {
                                return send_server_key_exchange(this, type, writer, group);
                        }
                        /* otherwise fall through to next state */
                case STATE_KEY_EXCHANGE_SENT:

根据之前选定的加密套件TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,函数get_dh_cgroup在预定义的数据suite_algs中可匹配动DH组为MODP_3072_BIT。随后调用函数send_server_key_exchange创建ServerkeyExchange报文。

static suite_algs_t suite_algs[] = {
        { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                KEY_RSA, MODP_3072_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
        },

如下函数send_server_key_exchange,由于MODP_3072_BIT非椭圆曲线相关算法,省略相关部分。函数diffie_hellman_get_params更加group的值在全局定义的变量dh_params数组(diffie_hellman.c)中获取到对应的参数,将其中参数g和p的值保存,准备发送。函数lib->crypto->create_dh创建一个diffie_hellman_t类型的实例。

函数this->dh->get_my_public_value根据创建的diffie_hellman_t实例生成一个DH公开数,即g**a mod p的值,其中a为随机数。至此ServerKeyExchange报文中的三个字段:g,p和公开数以及准备好,之后对这三个字段的内容进行签名。

函数最后,将TLS的状态更新为STATE_KEY_EXCHANGE_SENT,返回值为NEED_MORE,表示还将进入循环。

static status_t send_server_key_exchange(private_tls_server_t *this,
                                              tls_handshake_type_t *type, bio_writer_t *writer, diffie_hellman_group_t group)
{
        diffie_hellman_params_t *params = NULL;
        tls_named_curve_t curve;
        {
                params = diffie_hellman_get_params(group);
                DBG2(DBG_TLS, "selected DH group %N", diffie_hellman_group_names, group);
                writer->write_data16(writer, params->prime);
                writer->write_data16(writer, params->generator);
        }
        this->dh = lib->crypto->create_dh(lib->crypto, group);
        this->dh->get_my_public_value(this->dh, &chunk);

        if (params) { writer->write_data16(writer, chunk); } 

        chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), chunk_from_thing(this->server_random), writer->get_buf(writer));
        if (!this->private || !this->crypto->sign(this->crypto, this->private,  writer, chunk, this->hashsig)) {
                ...
                return NEED_MORE;
        }
        free(chunk.ptr);
        *type = TLS_SERVER_KEY_EXCHANGE;
        this->state = STATE_KEY_EXCHANGE_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

文件strongswan-5.8.1/src/libtls/tls_crypto.c中实现了以上用到的签名函数。最终由moon的私钥生成的结构private_key_t的sign函数执行签名操作。

METHOD(tls_crypto_t, sign, bool, private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig)
{      
        if (this->tls->get_version(this->tls) >= TLS_1_2) {
                signature_scheme_t scheme;

                reader = bio_reader_create(hashsig);
                while (reader->remaining(reader) >= 2)
                {
                        if (reader->read_uint8(reader, &hash) && reader->read_uint8(reader, &alg)) {
                                scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
                                if (scheme != SIGN_UNKNOWN &&
                                        key->sign(key, scheme, NULL, data, &sig))
                                {

以下为ServerKeyExchange报文的内容。

在这里插入图片描述

服务器CertificateRequest报文

当循环再次进入文件strongswan-5.8.1/src/libtls/tls_server.c的build函数时,由于在函数send_server_key_exchange中已经将TLS状态修改为STATE_KEY_EXCHANGE_SENT,此时调用函数send_certificate_request进行处理。

METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        diffie_hellman_group_t group;

        switch (this->state) {
                case STATE_HELLO_RECEIVED:
                        return send_server_hello(this, type, writer);
                case STATE_HELLO_SENT:
                        return send_certificate(this, type, writer);
                case STATE_CERT_SENT:
                        group = this->crypto->get_dh_group(this->crypto);
                        if (group) {
                                return send_server_key_exchange(this, type, writer, group);
                        }
                        /* otherwise fall through to next state */
                case STATE_KEY_EXCHANGE_SENT:
                        return send_certificate_request(this, type, writer);

函数send_certificate_request内容如下。对于请求的证书类型支持TLS_RSA_SIGN和TLS_ECDSA_SIGN两种。函数this->crypto->get_signature_algorithms获取支持的签名算法,其保存在文件tls_crypto.c中的全局变量schemes中,将这些签名算法都添加到报文中。

static status_t send_certificate_request(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        bio_writer_t *authorities, *supported;
        enumerator_t *enumerator;
        certificate_t *cert;
        x509_t *x509;
        identification_t *id;

        supported = bio_writer_create(4);
        /* we propose both RSA and ECDSA */
        supported->write_uint8(supported, TLS_RSA_SIGN);
        supported->write_uint8(supported, TLS_ECDSA_SIGN);
        writer->write_data8(writer, supported->get_buf(supported));
        supported->destroy(supported);
        if (this->tls->get_version(this->tls) >= TLS_1_2) {
                this->crypto->get_signature_algorithms(this->crypto, writer);
        }

接下来,获取本端X509_CA中的subject id信息,添加到报文中,对端需返回此subject id所颁发的证书。函数最后,将TLS的状态变更为STATE_CERTREQ_SENT,返回NEED_MORE,表明将再次进入循环,添加下一个报文。

        authorities = bio_writer_create(64);
        enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509, KEY_RSA, NULL, TRUE);
        while (enumerator->enumerate(enumerator, &cert)) {
                x509 = (x509_t*)cert;
                if (x509->get_flags(x509) & X509_CA) {
                        id = cert->get_subject(cert);
                        DBG1(DBG_TLS, "sending TLS cert request for '%Y'", id);
                        authorities->write_data16(authorities, id->get_encoding(id));
                }
        }
        enumerator->destroy(enumerator);
        writer->write_data16(writer, authorities->get_buf(authorities));
        authorities->destroy(authorities);

        *type = TLS_CERTIFICATE_REQUEST;
        this->state = STATE_CERTREQ_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

以下为CertificateRequest报文。

在这里插入图片描述

ServerHelloDone报文

当循环再次进入文件strongswan-5.8.1/src/libtls/tls_server.c的build函数时,由于在函数send_certificate_request中已经将TLS状态修改为STATE_CERTREQ_SENT,此时调用函数send_hello_done进行处理。

METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        diffie_hellman_group_t group;

        switch (this->state) {
                case STATE_HELLO_RECEIVED:
                        return send_server_hello(this, type, writer);
                case STATE_HELLO_SENT:
                        return send_certificate(this, type, writer);
                case STATE_CERT_SENT:
                        group = this->crypto->get_dh_group(this->crypto);
                        if (group) {
                                return send_server_key_exchange(this, type, writer, group);
                        }
                        /* otherwise fall through to next state */
                case STATE_KEY_EXCHANGE_SENT:
                        return send_certificate_request(this, type, writer);
                case STATE_CERTREQ_SENT:
                        return send_hello_done(this, type, writer);

函数send_hello_done非常简单,其只有一个type值等于TLS_SERVER_HELLO_DONE。随后将TLS状态修改为STATE_HELLO_DONE。

/* Send Hello Done
 */
static status_t send_hello_done(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        *type = TLS_SERVER_HELLO_DONE;
        this->state = STATE_HELLO_DONE;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

ServerHelloDone报文的内容如下。

在这里插入图片描述

如下函数build_handshake,其通过函数this->handshake->cipherspec_changed,即文件tls_server.c中的函数cipherspec_changed来的结果来判断是否继续循环,在此函数中,如果状态值变为STATE_HELLO_SENT,将返回真,导致循环介绍。

static status_t build_handshake(private_tls_fragmentation_t *this)
{       
        msg = bio_writer_create(64);
        while (TRUE) {       
                hs = bio_writer_create(64);
                status = this->handshake->build(this->handshake, &type, hs);
                switch (status) {       
                        case NEED_MORE:
                                ...
                                if (!this->handshake->cipherspec_changed(this->handshake, FALSE)) {       
                                        hs->destroy(hs);
                                        continue;
                                }
        ...
        return status;

TLS服务端报文发送

以上接收的ServerHello、Certificate、ServerkeyExchange、CertificateRequest和ServerHelloDone报文将被封装在一个TLS Record报文中发送。文件strongswan-5.8.1/src/libtls/tls_eap.c中的函数process使用函数process_pkt处理完接收到报文之后,又调用函数build_pkt创建好了回复报文,即以上的Record报文。注意此处分配的buf的长度,为一个最大分片长度:this->frag_size。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        if (this->first_fragment) {
                len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
                msg_len_offset = sizeof(uint32_t);
        }

        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +  msg_len_offset, &len, &reclen);

所以在文件src/libtls/tls.c中函数build,在创建完Record报文之后,使用memcpy将报文数据拷贝到参数buf中,即以上的buf空间,拷贝的大小为参数buflen的值,与output中剩余数据包的长度,两者之间的较小值。由于moon网关当前的Record报文长度为2503字节超出了buflen的值,这里发送第一个分片。共使用三个分片发送此报文。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
        len = *buflen;
        if (this->output.len == 0)
        {
                /* query upper layers for new records, as many as we can get */
                while (TRUE)
                {
                        status = this->protection->build(this->protection, &type, &data);
        ...
        len = min(len, this->output.len - this->outpos);
        memcpy(buf, this->output.ptr + this->outpos, len);
        this->outpos += len;
        *buflen = len;
        if (this->outpos == this->output.len) {
                chunk_free(&this->output);
                this->outpos = 0;
                return ALREADY_DONE;
        }
        return NEED_MORE;

文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中函数process_request最后使用build_response根据以上创建的message_t,创建IKE协议回复报文。

static status_t process_request(private_task_manager_t *this, message_t *message)
{ 
        enumerator = array_create_enumerator(this->passive_tasks);
        while (enumerator->enumerate(enumerator, (void*)&task)) {
                switch (task->process(task, message)) {
                        ...
                }
        }

        return build_response(this, message);

函数build_response的实现如下。首先函数message_create创建一个新的message_t结构。随后设置ISAKMP的头部信息,由于是回复消息,将头部的flags设置为Response。随后,调用TASK_IKE_AUTH任务的build函数创建回复数据。最后,generate_message生成IKE消息,由send_packets进行发送。

static status_t build_response(private_task_manager_t *this, message_t *request)
{
        me = request->get_destination(request);
        other = request->get_source(request);

        message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
        message->set_exchange_type(message, request->get_exchange_type(request));
        message->set_source(message, me->clone(me));
        message->set_destination(message, other->clone(other));
        message->set_message_id(message, this->responding.mid);
        message->set_request(message, FALSE);

        enumerator = array_create_enumerator(this->passive_tasks);
        while (enumerator->enumerate(enumerator, (void*)&task)) {
                switch (task->build(task, message))
                {
                        ...
        }

        /* message complete, send it */
        clear_packets(this->responding.packets);
        result = generate_message(this, message, &this->responding.packets);
        message->destroy(message);
        if (id)  {       
                id->set_responder_spi(id, responder_spi);
        }
        send_packets(this, this->responding.packets, NULL, NULL);

文件/strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/ike_auth.c实现了TASK_IKE_AUTH任务,其函数build_r负责创建IKE_AUTH消息。其主要通过对认证器结构成员函数build的调用完成。

METHOD(task_t, build_r, status_t,  private_ike_auth_t *this, message_t *message)
{
        identification_t *gateway;
        auth_cfg_t *cfg;

        if (this->other_auth)
        {
                switch (this->other_auth->build(this->other_auth, message))
                {
                        ...

文件strongswan-5.8.1/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c实现了eap_authenticator_t认证器结构,对于moon网关而言,作为认证服务器其使用build_server函数创建消息。此处将eap_payload数据添加到IKE_AUTH报文的EAP类型载荷中。

METHOD(authenticator_t, build_server, status_t, private_eap_authenticator_t *this, message_t *message)
{
        if (this->eap_payload) {
                eap_code_t code;

                code = this->eap_payload->get_code(this->eap_payload);
                message->add_payload(message, (payload_t*)this->eap_payload);
                this->eap_payload = NULL;
                if (code == EAP_FAILURE) {
                        return FAILED;
                }
                return NEED_MORE;

carol主机分片处理

carol报文接收路径如下:

    (ike_auth_t)
     process_i
        |
        |
   this->my_auth->process() 
       == (eap_authenticator_t) process_client
                   |
                   |
           client_process_eap(this, eap_payload);
                        |
                        |
                  this->method->process()
                    == (eap_method_t)process
                              |
                              |
                          this->tls_eap->process()
                                     == (tls_eap_t)process
                                              |
                                              |
                                              |--- process_pkt()
                                              |         |
                                              |         |
                                              |       this->tls->process()
                                              |              == (tls_t) process
                                              |
                                              |--- build_pkt("Create response packet")

此处由文件strongswan-5.8.1/src/libtls/tls.c中的函数process开始,由于是第一个分片,此时input为空。但是解析TLS Record头之后,发现其中的长度值大于当前接收到报文的长度(buflen),表明这是一个分片报文,并且,还有后续分片需要接收。将当前报文拷贝到input中,不做处理。

METHOD(tls_t, process, status_t, private_tls_t *this, void *buf, size_t buflen)
{
        tls_record_t *record;

        while (buflen) {
                if (this->input.len == 0) {
                        while (buflen >= sizeof(tls_record_t)) {
                                /* try to process records inline */
                                record = buf;
                                len = untoh16(&record->length);

                                if (len + sizeof(tls_record_t) > buflen) {       /* not a full record, read to buffer */
                                        this->input = chunk_alloc(len + sizeof(tls_record_t));
                                        this->inpos = 0;
                                        break;
                                }

                len = min(buflen, this->input.len - this->inpos);
                memcpy(this->input.ptr + this->inpos, buf, len);
                buf += len;
                buflen -= len;
                this->inpos += len;
                DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received",  len, this->inpos, this->input.len);

之后,返回到(tls_eap_t)process函数中,根据返回值NEED_MORE,调用build_pkt函数创建回复报文。其调用流程如下。

  build_pkt
     |
     |
    this->tls->build
       == (tls_t)build
              |
              |
        this->protection->build
              == (tls_protection_t)build
                    |
                    |
                  this->compression->build
                         == (tls_compression_t)build
                                      |
                                      |
                               this->fragmentation->build
                                       == (tls_fragmentation_t)build
                                                 |
                                                 |
                                              build_handshake
                                                     |
                                                     |
                                               this->handshake->build()
                                                      == (tls_handshake_t)build

文件strongswan-5.8.1/src/libtls/tls_peer.c中实现了tls_handshake_t结构的build函数。由于在carol调用函数send_client_hello之后,将状态设置为了STATE_HELLO_SENT,此处返回INVALID_STATE。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        switch (this->state)
        {
                case STATE_INIT:
                        return send_client_hello(this, type, writer);
                case STATE_HELLO_DONE:
                        ...
                case STATE_CERT_SENT:
                        ...
                case STATE_KEY_EXCHANGE_SENT:
                        ...
                case STATE_CIPHERSPEC_CHANGED_OUT:
                        ...
                default:
                        return INVALID_STATE;

函数build_pkt中得到this->tls->build函数的返回值INVALID_STATE,其将此值返回给上层调用。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + msg_len_offset, &len, &reclen);
        switch (status) {
                case NEED_MORE:
                        ...
                case ALREADY_DONE:
                        ...
                default:
                        return status;

tls_eap_t结构的process函数根据INVALID_STATE状态,调用create_ack函数。

METHOD(tls_eap_t, process, status_t,
        private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        status = build_pkt(this, out);
        switch (status) {
                case INVALID_STATE:
                        *out = create_ack(this);
                        return NEED_MORE;

如下函数create_ack,其创建一个仅包含TLS头部信息的报文,用于请求下一个分片。

/* Send an ack to request next fragment
 */
static chunk_t create_ack(private_tls_eap_t *this)
{
        eap_tls_packet_t pkt = {
                .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
                .type = this->type,
        };

        pkt.identifier = this->identifier;
        htoun16(&pkt.length, sizeof(pkt));

        DBG2(DBG_TLS, "sending %N acknowledgement packet", eap_type_names, this->type);
        return chunk_clone(chunk_from_thing(pkt));

以下为carol发送的acknowledgement报文。

在这里插入图片描述

moon接收ACK报文

moon报文接收路径如下:

    (ike_auth_t)
     process_r
        |
        |
   this->other_auth->process() 
       == (eap_authenticator_t) process_server
                   |
                   |
           server_process_eap(this, eap_payload);
                        |
                        |
                  this->method->process()
                    == (eap_method_t)process
                              |
                              |
                          this->tls_eap->process()
                                     == (tls_eap_t)process

文件strongswan-5.8.1/src/libtls/tls_eap.c实现了结构tls_eap_t结构的process函数,如下所示。由于Acknowledgement报文没有数据,其长度等于结构eap_tls_packet_t的长度。不处理此报文,而是直接调用build_pkt创建回复报文。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)in.ptr;

        {
                if (in.len == sizeof(eap_tls_packet_t)) {
                        DBG2(DBG_TLS, "received %N acknowledgement packet", eap_type_names, this->type);
                        status = build_pkt(this, out);
                        if (status == INVALID_STATE && this->tls->is_complete(this->tls)) {
                                return SUCCESS;
                        }
                        return status;
                }

如下为build_pkt调用tls_t结构的build函数,如下所示。由于之前第一个分片仅将output中的部分数据发送,此时output的长度不为零。在拷贝一部分数据到要发送的buf中,如果此时output中的数据已经拷贝完,情况output,返回ALREADY_DONE。否则,output中的数据还需要发送,返回NEED_MORE。实际上moon网关的报文需要3个分片发送。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
        tls_content_type_t type;
        tls_record_t record;

        len = *buflen;
        if (this->output.len == 0) {
                ...
        } else {
                if (msglen) {  *msglen = 0; }
        }
        len = min(len, this->output.len - this->outpos);
        memcpy(buf, this->output.ptr + this->outpos, len);
        this->outpos += len;
        *buflen = len;
        if (this->outpos == this->output.len) {
                chunk_free(&this->output);
                this->outpos = 0;
                return ALREADY_DONE;
        }
        return NEED_MORE;

剩余分片交互

carol接收到moon的第二个分片报文之后,在此响应Acknowledgement报文,从而moon发送第三个分片(最后一个分片)。函数this->tls->build处理完最后一个分片后,返回ALREADY_DONE,不同于返回值NEED_MORE,现在不需要在报文头部设置更多分片标志EAP_TLS_MORE_FRAGS了。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        if (this->is_server) {
                this->identifier++;
        }
        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;

        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + msg_len_offset, &len, &reclen);
        switch (status) {       
                case NEED_MORE:
                        pkt->flags |= EAP_TLS_MORE_FRAGS;
                        kind = "further fragment";
                        ...
                        break;
                case ALREADY_DONE:
                        if (this->first_fragment) {       
                                ...
                        }
                        else if (this->type != EAP_TNC && this->type != EAP_PT_EAP) {
                                this->first_fragment = TRUE;
                                kind = "final fragment";
                        }

carol主机在处理最后一个分片时,调用文件src/libtls/tls_eap.c中的函数process_pkt,其又调用tls_t结构的成员函数process。由于input中已经保存了之前的分片,其长度值不为零。最后一个分片到达后,报文接收完成,可以调用函数this->protection->process进行处理了。

METHOD(tls_t, process, status_t, private_tls_t *this, void *buf, size_t buflen)
{
        tls_record_t *record;

        while (buflen) {
                if (this->input.len == 0) {
                        ...
                }
                len = min(buflen, this->input.len - this->inpos);
                memcpy(this->input.ptr + this->inpos, buf, len);
                buf += len;
                buflen -= len;
                this->inpos += len;
                DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received", len, this->inpos, this->input.len);
                if (this->input.len == this->inpos)  {
                        record = (tls_record_t*)this->input.ptr;
                        len = untoh16(&record->length);

                        DBG2(DBG_TLS, "processing buffered TLS %N record (%d bytes)", tls_content_type_names, record->type, len);
                        status = this->protection->process(this->protection, record->type, chunk_create(record->data, len));
                        chunk_free(&this->input);
                        this->inpos = 0;
                        if (status != NEED_MORE) {
                                return status;
                        }
                }
        }
        return NEED_MORE;

以下为carol接收到的完整的报文:

在这里插入图片描述

ServerHello报文处理

以下为carol处理报文流程。此处由函数process_handshake开始查看。

  this->protection->process
        == (tls_protection_t)build
              |
              |
            this->compression->process
                   == (tls_compression_t)process
                                |
                                |
                         this->fragmentation->process
                                 == (tls_fragmentation_t)process
                                           |
                                           |
                                        process_handshake
                                               |
                                               |
                                         this->handshake->process()
                                                == (tls_handshake_t)process

以下为process_handshake函数,由于之前已经接收到分片报文,此处input的长度不为空。将最后一个分片报文的数据拷贝到input中。此时inpos的长度已经等于input缓存的长度值,表明已经接收到完整的报文。调用函数this->handshake->process进行处理。

需要注意的是while循环的再次开始。在process处理完成一个报文(例如ServerHello)之后,将清空input缓存,但是此时while可判断reader中还有剩余的报文未处理,再次进行循环。此时由于input被清空,其长度值为0,读取下一个报文的type和len值,inpos清零,并且再次分配一个input缓存块。

这样len表示下一个报文的长度,由reader中读取出此报文数据,并且拷贝到新分配的input缓存块中,并将inpos由0增加到报文长度len的值,这样随后的input->len和inpos是相等的,可继续处理下一个报文,调用函数this->handshake->process。

static status_t process_handshake(private_tls_fragmentation_t *this, bio_reader_t *reader)
{
        while (reader->remaining(reader)) {
                bio_reader_t *msg;

                if (this->input.len == 0) {   /* new handshake message */ 
                        if (!reader->read_uint8(reader, &type) ||
                                !reader->read_uint24(reader, &len)) {       
                                ...
                                return NEED_MORE;
                        }
                        this->type = type;
                        chunk_free(&this->input);
                        this->inpos = 0;
                        if (len) {
                                this->input = chunk_alloc(len);
                        }
				}

                len = min(this->input.len - this->inpos, reader->remaining(reader));
                if (!reader->read_data(reader, len, &data)) {
                        DBG1(DBG_TLS, "TLS fragment has invalid length");
                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                        return NEED_MORE;
                }
                memcpy(this->input.ptr + this->inpos, data.ptr, len);
                this->inpos += len;

                if (this->input.len == this->inpos) {            /* message completely defragmented, process */
                        msg = bio_reader_create(this->input);
                        DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)", tls_handshake_type_names, this->type, this->input.len);
                        status = this->handshake->process(this->handshake, this->type, msg);
                        msg->destroy(msg);
                        chunk_free(&this->input);
                        if (status != NEED_MORE) {  return status;  }
                }
        }
        return NEED_MORE;

如下为tls_handshake_t结构的process函数,当前carol的TLS状态为STATE_HELLO_SENT,报文类型为TLS_SERVER_HELLO(2),调用函数process_server_hello进行处理。

METHOD(tls_handshake_t, process, status_t,  private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_HELLO_SENT:
                        if (type == TLS_SERVER_HELLO) {
                                return process_server_hello(this, reader);
                        }
                        expected = TLS_SERVER_HELLO;
                        break;
                case STATE_HELLO_RECEIVED:

以下为process_server_hello函数,其首先读取报文中的version/server-random/session/cipher-suite/compression字段,如果还有剩余数据,读取其中的extension字段。

注意函数开头的append_handshake函数,其将完整的ServerHello数据保存在结构private_tls_crypto_t的成员handshake(chunk_t)中,对于后续的Certificate/ServerKeyExchange/CertificateRequest/ServerHello等报文,以及其它Handshake协议报文也将进行保存。在客户端发送CertificateVerify报文时,将对这些保存的内容执行签名操作,发送给服务端,以确认客户端参与了整个的交互流程。

static status_t process_server_hello(private_tls_peer_t *this, bio_reader_t *reader)
{
        this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, reader->peek(reader));
 
        if (!reader->read_uint16(reader, &version) ||
                !reader->read_data(reader, sizeof(this->server_random), &random) ||
                !reader->read_data8(reader, &session) ||
                !reader->read_uint16(reader, &cipher) ||
                !reader->read_uint8(reader, &compression) ||
                (reader->remaining(reader) && !reader->read_data16(reader, &ext)))
        {       
                DBG1(DBG_TLS, "received invalid ServerHello");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }

接下来,保存server端的随机数;验证是否支持server端的TLS版本号。

        memcpy(this->server_random, random.ptr, sizeof(this->server_random));

        if (!this->tls->set_version(this->tls, version)) {       
                DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version);
                this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
                return NEED_MORE;
        }

以下尝试根据报文中的session id信息,获取到之前协商的加密套件,由于此时为ServerHello报文,还没有对应的加密套件,使用报文中server选择的cipher值,此处验证本端是否支持。两端协商的加密套件为:TLS_DHE_RSA_WITH_AES_128_GCM_SHA256。最后,将状态更新为STATE_HELLO_RECEIVED。

        if (chunk_equals(this->session, session)) {
                suite = this->crypto->resume_session(this->crypto, session, this->server,
                                                                                chunk_from_thing(this->client_random),
                                                                                chunk_from_thing(this->server_random));
                if (suite) {
                        DBG1(DBG_TLS, "resumed %N using suite %N", tls_version_names, version, tls_cipher_suite_names, suite);
                        this->resume = TRUE;
                }
        }
        if (!suite) {
                suite = cipher;
                if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) {
                        DBG1(DBG_TLS, "received TLS cipher suite %N unacceptable", tls_cipher_suite_names, suite);
                        this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
                        return NEED_MORE;
                }
                DBG1(DBG_TLS, "negotiated %N using suite %N", tls_version_names, version, tls_cipher_suite_names, suite);
                free(this->session.ptr);
                this->session = chunk_clone(session);
        }
        this->state = STATE_HELLO_RECEIVED;
        return NEED_MORE;

函数select_cipher_suite位于文件strongswan-5.8.1/src/libtls/tls_crypto.c中,其主体调用了函数create_ciphers实现。这里TLS版本号为1.2,使用函数tls_prf_create_12创建tls_prf_t类型结构,prf为Pseudo Random Function的缩写。由于此处协商的加密套件TLS_DHE_RSA_WITH_AES_128_GCM_SHA256使用加密算法ENCR_AES_GCM_ICV16,其属于AEAD算法,调用create_aead函数创建tls_aead_t结构。

/* Create crypto primitives
 */
static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs)
{
        destroy_aeads(this);
        DESTROY_IF(this->prf);
        if (this->tls->get_version(this->tls) < TLS_1_2) {
                this->prf = tls_prf_create_10();
        } else {
                this->prf = tls_prf_create_12(algs->prf);
        }
        if (!this->prf) {
                DBG1(DBG_TLS, "selected TLS PRF not supported");
                return FALSE;
        }

        if (algs->encr == ENCR_NULL) {
                ...
        }
        else if (encryption_algorithm_is_aead(algs->encr)) {
                if (create_aead(this, algs)) { return TRUE; }

以下为函数create_aead,两个tls_aead_t结构aead_in和aead_out分别处理inbound和outbond的流量,创建函数为tls_aead_create_aead。

/* Create AEAD transforms
 */
static bool create_aead(private_tls_crypto_t *this, suite_algs_t *algs)
{
        this->aead_in = tls_aead_create_aead(algs->encr, algs->encr_size);
        this->aead_out = tls_aead_create_aead(algs->encr, algs->encr_size);
        if (!this->aead_in || !this->aead_out) {
                DBG1(DBG_TLS, "selected TLS transforms %N-%u not supported", encryption_algorithm_names, algs->encr, algs->encr_size * 8);
                return FALSE;
        }
        return TRUE;

文件strongswan-5.8.1/src/libtls/tls_aead.c中实现了tls_aead_t结构相关函数。此处加密使用ENCR_AES_GCM_ICV16,salt值为4。

tls_aead_t *tls_aead_create_aead(encryption_algorithm_t encr, size_t encr_size)
{
        private_tls_aead_t *this;
        size_t salt;

        switch (encr) {
                ...
                case ENCR_AES_GCM_ICV16:
                        salt = 4;
                        break;
        }
        INIT(this,
                .public = {
                        .encrypt = _encrypt,
                        .decrypt = _decrypt,
                        .get_mac_key_size = _get_mac_key_size,
                        .get_encr_key_size = _get_encr_key_size,
                        .get_iv_size = _get_iv_size,
                        .set_keys = _set_keys,
                        ...
                },
                .aead = lib->crypto->create_aead(lib->crypto, encr, encr_size, salt),
                .salt = salt,
        );

        if (this->aead->get_block_size(this->aead) != 1) {  /* TLS does not define any padding scheme for AEAD */
                destroy(this);
                return NULL;

文件strongswan-5.8.1/src/libstrongswan/plugins/gcm/gcm_aead.c实现了gcm_aead_t结构,其中函数gcm_aead_create用于创建gcm_aead_t结构实例。函数lib->crypto->create_crypter用于创建加密器结构crypter_t。

gcm_aead_t *gcm_aead_create(encryption_algorithm_t algo, size_t key_size, size_t salt_size)
{
        switch (algo) {
                ...
                case ENCR_AES_GCM_ICV16:
                        algo = ENCR_AES_CBC;
                        icv_size = 16;
                        break;
        }
        INIT(this,
                .public = {
                        .aead = {
                                .encrypt = _encrypt,
                                .decrypt = _decrypt,
                                .get_block_size = _get_block_size,
                                .get_icv_size = _get_icv_size,
                                .get_iv_size = _get_iv_size,
                                .get_iv_gen = _get_iv_gen,
                                .get_key_size = _get_key_size,
                                .set_key = _set_key,
                                .destroy = _destroy,
                        },
                },
                .crypter = lib->crypto->create_crypter(lib->crypto, algo, key_size),
                .iv_gen = iv_gen_seq_create(),
                .icv_size = icv_size,
        );

文件strongswan-5.8.1/src/libstrongswan/plugins/aes/aes_crypter.c实现了结构aes_crypter_t,函数aes_crypter_create创建aes_crypter_t类型加密器实例。

aes_crypter_t *aes_crypter_create(encryption_algorithm_t algo, size_t key_size)
{
        private_aes_crypter_t *this;

        if (algo != ENCR_AES_CBC) {  return NULL;  }

        INIT(this,
                .public = {
                        .crypter = {
                                .encrypt = _encrypt,
                                .decrypt = _decrypt,
                                .get_block_size = _get_block_size,
                                .get_iv_size = _get_iv_size,
                                .get_key_size = _get_key_size,
                                .set_key = _set_key,
                                .destroy = _destroy,
                        },
                },
                .key_size = key_size,
                .aes_Nkey = key_size / 4,
        );

        return &this->public;

至此,在处理服务器ServerHello报文的过程中,客户端carol初始化好了相关的加密结构。

处理服务器Certificate报文

在以上ServerHello报文的处理中,已经将状态设置为了STATE_HELLO_RECEIVED,而此时处理的报文类型为TLS_CERTIFICATE(11),此时将调用函数process_certificate处理服务器证书。

METHOD(tls_handshake_t, process, status_t,  private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_HELLO_SENT:
                        ...
                case STATE_HELLO_RECEIVED:
                        if (type == TLS_CERTIFICATE) {
                                return process_certificate(this, reader);
                        }
                        expected = TLS_CERTIFICATE;
                        break;

如下证书报文处理函数process_certificate,首先使用lib->creds->create根据报文内容创建一个证书结构certificate_t,这里使用的是X509证书,格式为ASN1_DER。实际的create函数指针指向x509_cert_load函数(src/libstrongswan/plugins/x509/x509_cert.c)。随后使用check_certificate函数验证服务端证书。最后,将TLS的状态更新为STATE_CERT_RECEIVED。

static status_t process_certificate(private_tls_peer_t *this, bio_reader_t *reader)
{
        certificate_t *cert;
        bio_reader_t *certs;
        bool first = TRUE;

        if (!reader->read_data24(reader, &data)) {  ...  return NEED_MORE; }
        certs = bio_reader_create(data);
        while (certs->remaining(certs)) {
                if (!certs->read_data24(certs, &data)) { ...  return NEED_MORE;  }

                cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_ASN1_DER, data, BUILD_END);
                if (cert) {
                        if (first) {
                                if (!check_certificate(this, cert)) { ...  return NEED_MORE; }

                                this->server_auth->add(this->server_auth, AUTH_HELPER_SUBJECT_CERT, cert);
                                DBG1(DBG_TLS, "received TLS server certificate '%Y'", cert->get_subject(cert));
                                first = FALSE;
                        } else {
                                DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'", cert->get_subject(cert));
                                this->server_auth->add(this->server_auth,  AUTH_HELPER_IM_CERT, cert);
                        }
                } else {  ...  }
        }
        certs->destroy(certs);
        this->state = STATE_CERT_RECEIVED;
        return NEED_MORE;

x509_cert_load函数的主体是调用了parse_certificate函数,其解析证书中的VERSION/SN/ISSUER/SUBJECT等字符串字段,注意这里的X509_OBJ_TBS_CERTIFICATE字段,其包含证书的主体body,之后的签名数据据此产生。另外由证书的X509_OBJ_SIG_ALG字段获取的其使用的签名算法。
字段X509_OBJ_SUBJECT_PUBLIC_KEY_INFO中为证书中包含的服务端(moon)的公钥信息,使用lib->creds->create(即文件pkcs1_builder.c中的函数pkcs1_public_key_load)完成解析。

static bool parse_certificate(private_x509_cert_t *this)
{
        asn1_parser_t *parser;
        signature_params_t sig_alg = {};

        parser = asn1_parser_create(certObjects, this->encoding);
        this->flags |= X509_IKE_COMPLIANT;  /* unless we see a keyUsage extension we are compliant with RFC 4945 */

        while (parser->iterate(parser, &objectID, &object)) {
                u_int level = parser->get_level(parser)+1;
                switch (objectID) {
                        case X509_OBJ_TBS_CERTIFICATE:
                                this->tbsCertificate = object;
                                break;
                        case X509_OBJ_SIG_ALG:
                                if (!signature_params_parse(object, level, &sig_alg)) {
                                        DBG1(DBG_ASN, "  unable to parse signature algorithm");
                                        goto end;
                                }
                                break;
                        case X509_OBJ_SUBJECT_PUBLIC_KEY_INFO:
                                this->public_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
                                                KEY_ANY, BUILD_BLOB_ASN1_DER, object, BUILD_END);
                        }

接下来,由X509_OBJ_ALGORITHM取得使用的签名算法,并且将其与从字段X509_OBJ_SIG_ALG中取得的算法值进行比较,二者必须相等。之后将X509_OBJ_SIGNATURE字段的签名数据保存到signature缓存中。

                        case X509_OBJ_ALGORITHM:
                                INIT(this->scheme);
                                if (!signature_params_parse(object, level, this->scheme)) {               
                                        DBG1(DBG_ASN, "  unable to parse signature algorithm");           
                                        goto end;
                                }               
                                if (!signature_params_equal(this->scheme, &sig_alg)) {               
                                        DBG1(DBG_ASN, "  signature algorithms do not agree");
                                        goto end;
                                }       
                                break;          
                        case X509_OBJ_SIGNATURE:                                                         
                                this->signature = chunk_skip(object, 1);
                                break; 

函数在解析完证书内容之后,调用函数this->public.interface.interface.issued_by(即本文件内的issued_by函数)验证证书是否有声明的发布者(Issuer)所签发,过程是使用本地的Issuer的公钥,重新对证书体(以上的tbsCertificate字段内容)进行签名,与字段X509_OBJ_SIGNATURE中的签名信息对比,确认证书的合法性。

最后,使用函数allocate_hash对证书的原始内容encodeing,进行哈希运算,将得到的值保存在encodeing_hash中,之后使用。

                /* check if the certificate is self-signed */
                if (this->public.interface.interface.issued_by( &this->public.interface.interface,
                                                                &this->public.interface.interface, NULL)) {
                        this->flags |= X509_SELF_SIGNED;
                }
                /* create certificate hash */
                hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
                if (!hasher ||
                        !hasher->allocate_hash(hasher, this->encoding, &this->encoding_hash))
                {

至此证书解析完成,返回到以上函数process_certificate中,其后调用函数check_certificate检查证书的subject id字段是否于本地(carol)期望的一致,以及检查扩展字段subjectAltName等。至此,moon网关的Certificate报文处理完成。

static bool check_certificate(private_tls_peer_t *this, certificate_t *cert)
{         
        identification_t *id;
 
        if (cert->has_subject(cert, this->server)) {
                return TRUE;
        }
        id = cert->get_subject(cert);
        if (id->matches(id, this->server)) {
                return TRUE;
        }
        if (cert->get_type(cert) == CERT_X509) {
                x509_t *x509 = (x509_t*)cert;
                enumerator_t *enumerator;

                enumerator = x509->create_subjectAltName_enumerator(x509);
                while (enumerator->enumerate(enumerator, &id)) {
                        if (id->matches(id, this->server)) {
                                enumerator->destroy(enumerator);
                                return TRUE;

处理服务器ServerKeyExchange报文

由于以上process_certificate函数将状态变更为STATE_CERT_RECEIVED,之后的报文类型为TLS_SERVER_KEY_EXCHANGE,调用函数process_key_exchange进行处理。

METHOD(tls_handshake_t, process, status_t,  private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_HELLO_SENT:    ...
                case STATE_HELLO_RECEIVED:   ...
                case STATE_CERT_RECEIVED:
                        if (type == TLS_SERVER_KEY_EXCHANGE) {
                                return process_key_exchange(this, reader);
                        }
                        /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */

以下为process_key_exchange函数。首先由之前两端协商的加密套件中获取到DH组(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256),其不属于ECP类DH,使用函数process_modp_key_exchange进行处理。

static status_t process_key_exchange(private_tls_peer_t *this, bio_reader_t *reader)
{
        diffie_hellman_group_t group;

        this->crypto->append_handshake(this->crypto, TLS_SERVER_KEY_EXCHANGE, reader->peek(reader));

        group = this->crypto->get_dh_group(this->crypto);
        if (group == MODP_NONE) {
                DBG1(DBG_TLS, "received Server Key Exchange, but not required for current suite");
                this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
                return NEED_MORE;
        }
        if (diffie_hellman_group_is_ec(group)) {
                ...
        }
        return process_modp_key_exchange(this, reader);

以下process_modp_key_exchange函数。首先获取报文中的p/g和pub三个字段的值,拒绝prime长度小于1024bit的值。之后由函数find_public_key获取服务端在Certificate报文中发来的公钥,使用公钥对三个DH值进行签名,将结构与报文中的签名进行对比,以便确认为server(moon)发送的DH数据,此由函数this->crypto->verify完成。

static status_t process_modp_key_exchange(private_tls_peer_t *this, bio_reader_t *reader)
{
        chunk_t prime, generator, pub, chunk;
        public_key_t *public;

        chunk = reader->peek(reader);
        if (!reader->read_data16(reader, &prime) || !reader->read_data16(reader, &generator) ||
                !reader->read_data16(reader, &pub)) {
                ...
                return NEED_MORE;
        }
        if (prime.len < 1024 / 8) {  ...  return NEED_MORE; }  /* reject (export) DH groups using primes smaller than 1024 bit */

        public = find_public_key(this);

        chunk.len = 2 + prime.len + 2 + generator.len + 2 + pub.len;
        chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), chunk_from_thing(this->server_random), chunk);
        if (!this->crypto->verify(this->crypto, public, reader, chunk)) {
                ...
                this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE);
                return NEED_MORE;
        }

以上验证完成,接下来创建diffie_hellman_t结构,并且保存报文中的DH公开数,在函数set_other_public_value中,客户端carol还将根据DH交换推导出协商DH公共值。最后,将状态变更为STATE_KEY_EXCHANGE_RECEIVED。

        this->dh = lib->crypto->create_dh(lib->crypto, MODP_CUSTOM, generator, prime);
        if (!this->dh) {
                DBG1(DBG_TLS, "custom DH parameters not supported");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }
        if (!this->dh->set_other_public_value(this->dh, pub)) {
                DBG1(DBG_TLS, "applying DH public value failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }
        this->state = STATE_KEY_EXCHANGE_RECEIVED;
        return NEED_MORE;

处理服务器CertificateRequest报文

由于以上process_key_exchange函数将状态变更为STATE_KEY_EXCHANGE_RECEIVED,之后的报文类型为TLS_CERTIFICATE_REQUEST,调用函数process_certreq进行处理。

METHOD(tls_handshake_t, process, status_t,  private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_KEY_EXCHANGE_RECEIVED:
                        if (type == TLS_CERTIFICATE_REQUEST) {
                                return process_certreq(this, reader);

以下函数process_certreq,首先获取报文中证书类型字段,此字段标识请求的证书类型,moon的证书请求中包括两种类型:TLS_RSA_SIGN(1)和TLS_ECDSA_SIGN(64)。对于TLS版本号1.2或者以上的情况,报文中的第二部分为签名哈希算法列表,carol客户端将选择其中的一种算法,对自身证书进行签名,此签名数据包含在之后carol回复给moon的CertificateVerify报文中。

static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader)
{
        chunk_t types, hashsig, data;
        bio_reader_t *authorities;
        identification_t *id;
        certificate_t *cert;

        if (!reader->read_data8(reader, &types)) {
                DBG1(DBG_TLS, "certreq message header invalid");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }
        this->cert_types = chunk_clone(types);
        if (this->tls->get_version(this->tls) >= TLS_1_2) {
                if (!reader->read_data16(reader, &hashsig)) {
                        DBG1(DBG_TLS, "certreq message invalid");
                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                        return NEED_MORE;
                }
                this->hashsig = chunk_clone(hashsig);
        }

报文中的第三部分为CA证书的Distinguish Name名称,客户端回复的证书需由此名称的CA证书所签发,函数lib->credmgr->get_cert根据其中的ID字段,查找系统内相符的CERT_X509证书。这里将找到carol的证书。get_cert函数的实现位于文件src/libstrongswan/credentials/credential_manager.c中。

        if (!reader->read_data16(reader, &data)) { ... return NEED_MORE;  }

        authorities = bio_reader_create(data);
        while (authorities->remaining(authorities)) {
                if (!authorities->read_data16(authorities, &data)) { ...  return NEED_MORE; }

                if (this->peer) {
                        id = identification_create_from_encoding(ID_DER_ASN1_DN, data);
                        cert = lib->credmgr->get_cert(lib->credmgr, CERT_X509, KEY_ANY, id, TRUE);
                        if (cert) {
                                DBG1(DBG_TLS, "received TLS cert request for '%Y", id);
                                this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert);
                        } else {
                                DBG1(DBG_TLS, "received TLS cert request for unknown CA '%Y'", id);
                        }
                        id->destroy(id);
                }
        }
        authorities->destroy(authorities);
        this->state = STATE_CERTREQ_RECEIVED;
        return NEED_MORE;

处理服务器HelloDone报文

以上函数process_certreq将TLS状态修改为STATE_CERTREQ_RECEIVED,并且下一个报文为

METHOD(tls_handshake_t, process, status_t,  private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
        case STATE_CERTREQ_RECEIVED:
            if (type == TLS_SERVER_HELLO_DONE) {
                return process_hello_done(this, reader);
            }
            expected = TLS_SERVER_HELLO_DONE;
            break;

以下函数process_hello_done,不执行具体的操作,将状态修改为STATE_HELLO_DONE。至此,接收报文处理完成。

static status_t process_hello_done(private_tls_peer_t *this, bio_reader_t *reader)
{
        this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO_DONE, reader->peek(reader));
        this->state = STATE_HELLO_DONE;
        return NEED_MORE;

carol回复报文

在以上process_pkt处理完成接收的报文之后,carol使用build_pkt创建回复报文。报文的大小不能大于指定的缓存buf大小值this->frag_size,即最大分片大小阈值。在此函数中初始化EAP协议报文头部信息。随后调用this->tls->build函数填充TLS回复数据。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;

        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        if (this->first_fragment) {
                len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
                msg_len_offset = sizeof(uint32_t);
        } else {
                len = sizeof(buf) - sizeof(eap_tls_packet_t);
                msg_len_offset = 0;
        }
        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + msg_len_offset, &len, &reclen);

如下文件src/libtls/tls.c中实现的build函数,如果output缓存中没有数据,使用this->protection->build创建回复数据,否则,表明在分片发送数据,将output中剩余的数据拷贝到参数buf中,如果output中数据拷贝完成,即拷贝了最后一个分片,释放output缓存,返回ALREADY_DONE,否则,返回值NEED_MORE。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
    tls_content_type_t type;
    tls_record_t record;
    
    len = *buflen;
    if (this->output.len == 0) {
        /* query upper layers for new records, as many as we can get */
        while (TRUE)
        {
                status = this->protection->build(this->protection, &type, &data);
    ...
    len = min(len, this->output.len - this->outpos);
    memcpy(buf, this->output.ptr + this->outpos, len);
    this->outpos += len;
    *buflen = len;
    if (this->outpos == this->output.len) {
        chunk_free(&this->output);
        this->outpos = 0;
        return ALREADY_DONE;
    }
    return NEED_MORE;

目前我们还没有创建回复报文,output缓存为空,使用tls_protection_t结构的build函数,而其又调用tls_compression_t结构的build函数,接下来是调用tls_fragmentation_t的build函数,如下所示。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
    if (!this->output.len)
    {
        if (this->handshake->cipherspec_changed(this->handshake, FALSE)) {
            this->handshake->change_cipherspec(this->handshake, FALSE);
            *type = TLS_CHANGE_CIPHER_SPEC;
            *data = chunk_clone(chunk_from_chars(0x01));
            return NEED_MORE;
        }
        if (!this->handshake->finished(this->handshake)) {
            status = build_handshake(this);
        }
        else if (this->application) {
            status = build_application(this);
        }

将调用build_handshake函数处理carol客户端的回复报文,如下所示,其将使用this->handshake->build函数创建报文内容。

static status_t build_handshake(private_tls_fragmentation_t *this)
{
        bio_writer_t *hs, *msg;
        tls_handshake_type_t type;

        msg = bio_writer_create(64);
        while (TRUE) {
                hs = bio_writer_create(64);
                status = this->handshake->build(this->handshake, &type, hs);
                switch (status) {
                        case NEED_MORE:
                                msg->write_uint8(msg, type);
                                msg->write_data24(msg, hs->get_buf(hs));
                                DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len);
                                if (!this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                                        hs->destroy(hs);
                                        continue;

Carol的Certificate报文

文件strongswan-5.8.1/src/libtls/tls_peer.c实现了tls_handshake_t结构的build函数。由于之前carol发送了ClientHello报文,其状态为STATE_HELLO_DONE,这里调用函数send_certificate处理。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        switch (this->state) {
                case STATE_INIT:
                        return send_client_hello(this, type, writer);
                case STATE_HELLO_DONE:
                        if (this->peer) {
                                return send_certificate(this, type, writer);
                        }
                        /* otherwise fall through to next state */
                case STATE_CERT_SENT:
                        return send_key_exchange(this, type, writer);
                case STATE_KEY_EXCHANGE_SENT:
                        if (this->peer) {
                                return send_certificate_verify(this, type, writer);
                        } else {
                                return INVALID_STATE;
                        }
                case STATE_CIPHERSPEC_CHANGED_OUT:
                        return send_finished(this, type, writer);

以下send_certificate函数,find_private_key用于查找本地符合服务端请求证书格式(moon支持TLS_RSA_SIGN和TLS_ECDSA_SIGN类型)的证书秘钥。

static status_t send_certificate(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        enumerator_t *enumerator;
        certificate_t *cert;
        auth_rule_t rule;
        bio_writer_t *certs;
        chunk_t data;

        this->private = find_private_key(this);
        if (!this->private) {
                DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', "
                         "skipping client authentication", this->peer);
                this->peer->destroy(this->peer);
                this->peer = NULL;
        }

之前介绍的初始化函数build_auth_cfg中,已经将carol配置文件ipsec.conf中指定的证书文件进行了加载(auth_cfg_t结构的add),此处使用auth_cfg_t结构的get函数取出证书。随后将证书进行CERT_ASN1_DER格式的编码。除去此AUTH_RULE_SUBJECT_CERT证书外,如果配置了中间证书AUTH_RULE_IM_CERT,同样封装中间证书。此处carol没有中间证书。

函数最后将状态修改为STATE_CERT_SENT。

        certs = bio_writer_create(256);
        if (this->peer) {
                cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT);
                if (cert) {
                        if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) {
                                DBG1(DBG_TLS, "sending TLS peer certificate '%Y'", cert->get_subject(cert));
                                certs->write_data24(certs, data);
                                free(data.ptr);
                        }
                }
                enumerator = this->peer_auth->create_enumerator(this->peer_auth);
                while (enumerator->enumerate(enumerator, &rule, &cert)) {
                        if (rule == AUTH_RULE_IM_CERT) {
                                if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) {
                                        DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", cert->get_subject(cert));
                                        certs->write_data24(certs, data);
                                        free(data.ptr);
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }

        writer->write_data24(writer, certs->get_buf(certs));
        certs->destroy(certs);

        *type = TLS_CERTIFICATE;
        this->state = STATE_CERT_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

以下为carol发送的Certificate报文(作为整个报文的一部分)。

在这里插入图片描述

Carol之ClientKeyExchange报文

以上在创建完成carol的Certificate报文数据之后,返回build_handshake函数,继续进行循环。再次进入tls_peer.c文件中函数build时,由于状态已经更新为STATE_CERT_SENT,将调用send_key_exchange函数。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        switch (this->state) {
                case STATE_INIT:
                        return send_client_hello(this, type, writer);
                case STATE_HELLO_DONE:
                        if (this->peer) {
                                return send_certificate(this, type, writer);
                        }
                        /* otherwise fall through to next state */
                case STATE_CERT_SENT:
                        return send_key_exchange(this, type, writer);

函数send_key_exchange如下,由于在处理服务端ServerKeyExchange报文时,创建了dh结构(类型diffie_hellman_t)。参见函数process_modp_key_exchange的处理,此处实际调用send_key_exchange_dhe创建报文。

/* Send client key exchange, depending on suite
 */
static status_t send_key_exchange(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        if (this->dh) {
                return send_key_exchange_dhe(this, type, writer);
        }
        return send_key_exchange_encrypt(this, type, writer);

以下函数send_key_exchange_dhe首先由carol的diffie_hellman_t结构的get_shared_secret函数获取到本端计算的DH共享秘钥(在处理服务端ServerKeyExchange报文时,在函数set_other_public_value中根据服务端的DH公开值计算而得),这里也叫做premaster秘钥,服务端和客户端都可根据对端的DH公开值,计算出相同的DH共享秘钥。

static status_t send_key_exchange_dhe(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        chunk_t premaster, pub;

        if (!this->dh->get_shared_secret(this->dh, &premaster)) {
                DBG1(DBG_TLS, "calculating premaster from DH failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }

接下来,函数this->crypto->derive_secrets根据premaster,客户端随机数以及服务端随机数,生成主密钥,稍后进行介绍。随后,get_my_public_value函数获取到本地的DH公开值,填充到报文中,不同于ServerKeyExchange报文,由于客户端接受了服务端之前发送的DH的参数g和p的值,这是客户端carol发送的ClientKeyExchange报文中仅携带本地的DH公开值。

最后,将状态更新为STATE_KEY_EXCHANGE_SENT。

        if (!this->crypto->derive_secrets(this->crypto, premaster, this->session, this->server,
                                   chunk_from_thing(this->client_random), chunk_from_thing(this->server_random))) {
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                chunk_clear(&premaster);
                return NEED_MORE;
        }
        chunk_clear(&premaster);

        if (!this->dh->get_my_public_value(this->dh, &pub)) {
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;                                               
        }                                                                       
        if (this->dh->get_dh_group(this->dh) == MODP_CUSTOM) {
                writer->write_data16(writer, pub);
        }
        free(pub.ptr);

        *type = TLS_CLIENT_KEY_EXCHANGE;
        this->state = STATE_KEY_EXCHANGE_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

文件strongswan-5.8.1/src/libtls/tls_crypto.c实现了tls_crypto_t结构,其函数derive_secrets如下。这里有两个函数调用derive_master和expand_keys,以下依次介绍。

METHOD(tls_crypto_t, derive_secrets, bool, private_tls_crypto_t *this, chunk_t premaster, chunk_t session,
        identification_t *id, chunk_t client_random, chunk_t server_random)
{
        return derive_master(this, premaster, session, id,
                               client_random, server_random) && expand_keys(this, client_random, server_random);

如下为derive_master函数,其调用tls_prf_t结构的函数set_key和get_bytes函数,根据premaster和两端的随机数生成主密钥master(48字节)。之后,是针对session创建主密钥缓存,目前的strongswan版本中TLS插件还没有开启此功能,this->cache为空。

static bool derive_master(private_tls_crypto_t *this, chunk_t premaster, chunk_t session, identification_t *id,
                                                  chunk_t client_random, chunk_t server_random)
{
        char master[48];
        chunk_t seed;

        /* derive master secret */
        seed = chunk_cata("cc", client_random, server_random);

        if (!this->prf->set_key(this->prf, premaster) ||
                !this->prf->get_bytes(this->prf, "master secret", seed, sizeof(master), master) || 
                  !this->prf->set_key(this->prf, chunk_from_thing(master))) {
                return FALSE;
        }

        if (this->cache && session.len) { this->cache->create(this->cache, session, id, chunk_from_thing(master), this->suite); }
		
        memwipe(master, sizeof(master));

在处理ServerHello报文时,我们调用create_ciphers函数创建秘钥结构的过程中,对于TLS版本大于等于1.2的情况,使用tls_prf_create_12函数创建了tls_prf_t结构。对于之前版本,小于TLS1.2,使用函数tls_prf_create_10创建tls_prf_t结构。

以上使用的this->prf->set_key函数,为set_key12函数,可见其调用tls_prf_t结构中的成员prf(prf_t结构)的set_key函数处理。

METHOD(tls_prf_t, set_key12, bool, private_tls_prf12_t *this, chunk_t key)
{
        return this->prf->set_key(this->prf, key);

文件strongswan-5.8.1/src/libstrongswan/crypto/prfs/mac_prf.c实现了prf_t结构。其set_key函数如下。主体为对mac_t结构的set_key函数的调用。mac_t结构实现在文件libstrongswan/plugins/hmac/hmac.c中。对于双方协商的HASH_SHA256哈希算法,块长度为64字节,如果秘钥长度大于此长度,将秘钥进行哈希运算。

METHOD(prf_t, set_key, bool, private_prf_t *this, chunk_t key)
{
        return this->mac->set_key(this->mac, key);
}

METHOD(mac_t, set_key, bool, private_mac_t *this, chunk_t key)
{
        int i;
        uint8_t buffer[this->b];

        memset(buffer, 0, this->b);

        if (key.len > this->b) {
                /* if key is too long, it will be hashed */
                if (!this->h->reset(this->h) || !this->h->get_hash(this->h, key, buffer)) {
                        return FALSE;
                }
        } else {
                /* if not, just copy it in our pre-padded k */
                memcpy(buffer, key.ptr, key.len);
        }

        /* apply ipad and opad to key */
        for (i = 0; i < this->b; i++) {
                this->ipaded_key.ptr[i] = buffer[i] ^ 0x36;
                this->opaded_key.ptr[i] = buffer[i] ^ 0x5C;
        }

        /* begin hashing of inner pad */
        return this->h->reset(this->h) && this->h->get_hash(this->h, this->ipaded_key, NULL);
}

函数derive_master中的this->prf->get_bytes用于获取主密钥,其使用以下的P_hash算法,其中seed为client_random和server_random数据。

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                       HMAC_hash(secret, A(2) + seed) +
                       HMAC_hash(secret, A(3) + seed) + ...

这里"+"是指级联。

A(i)被定义为:

     A(0) = seed
     A(i) = HMAC_hash(secret, A(i-1))

P_hash实现在tls_prf.c文件中,在这里将label(“master secret”)与之前的随机数seed结合。prf->get_bytes函数为hmac.c文件中的get_mac函数(底层为libstrongswan/plugins/sha2/sha2_hasher.c文件中的函数get_hash256)。while循环在bytes(初始值48)小于等于块长度(HASH_SIZE_SHA256 - 32)时,拷贝最后一块数据(此处为16字节),退出循环。out参数中为生成的主密钥。

/* The P_hash function as in TLS 1.0/1.2 */
static bool p_hash(prf_t *prf, char *label, chunk_t seed, size_t block_size, size_t bytes, char *out)
{       
        char buf[block_size], abuf[block_size];
        chunk_t a;
        
        /* seed = label + seed */
        seed = chunk_cata("cc", chunk_create(label, strlen(label)), seed);
        /* A(0) = seed */
        a = seed;
        
        while (TRUE) {       
                /* A(i) = HMAC_hash(secret, A(i-1)) */
                if (!prf->get_bytes(prf, a, abuf)) {  return FALSE; } 

                a = chunk_from_thing(abuf);
                /* HMAC_hash(secret, A(i) + seed) */
                if (!prf->get_bytes(prf, a, NULL) || !prf->get_bytes(prf, seed, buf)) { return FALSE; }
                
                if (bytes <= block_size) {  memcpy(out, buf, bytes); break; }

                memcpy(out, buf, block_size);
                out += block_size;
                bytes -= block_size;
        }
        return TRUE;

在以上的derive_secrets函数中,主密钥生成之后,将调用expand_keys函数由主密钥生成扩展秘钥。由于在ServerHello报文处理一节以及由函数create_aead创建了aead_in和aead_out,这里不为空。首先获取MAC秘钥/加密秘钥/IV等三个字段的长度值,其次,以server_random和client_random随机数据创建seed。由于要创建客户端和服务端两者的扩展秘钥,整个block块的长度要翻倍。最后,使用之前的PRF算法创建block长度的秘钥数据,这里label为"key expansion",其也作为seed的一部分。

在以上函数derive_master中生成主密钥之后,以及使用this->prf->set_key将主密钥设置到prf_t结构中,所以这里扩展秘钥依据主密钥生成。

/* Expand key material from master secret
 */
static bool expand_keys(private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random)
{
        chunk_t seed, block;
        chunk_t cw_mac, cw, cw_iv;
        chunk_t sw_mac, sw, sw_iv;
        int mklen, eklen, ivlen;

        if (!this->aead_in || !this->aead_out) {  return FALSE;  }

        /* derive key block for key expansion */
        mklen = this->aead_in->get_mac_key_size(this->aead_in);
        eklen = this->aead_in->get_encr_key_size(this->aead_in);
        ivlen = this->aead_in->get_iv_size(this->aead_in);
        seed = chunk_cata("cc", server_random, client_random);
        block = chunk_alloca((mklen + eklen + ivlen) * 2);
        if (!this->prf->get_bytes(this->prf, "key expansion", seed, block.len, block.ptr)) { return FALSE; }

在生成扩展秘钥数据block之后,依次分配客户端写MAC秘钥、服务端写MAC秘钥、客户端写加密秘钥和服务端写加密秘钥、客户端IV和服务端IV数据。

        /* client/server write signer keys */
        cw_mac = chunk_create(block.ptr, mklen);
        block = chunk_skip(block, mklen);
        sw_mac = chunk_create(block.ptr, mklen);
        block = chunk_skip(block, mklen);

        /* client/server write encryption keys */
        cw = chunk_create(block.ptr, eklen);
        block = chunk_skip(block, eklen);
        sw = chunk_create(block.ptr, eklen);
        block = chunk_skip(block, eklen);

        /* client/server write IV; TLS 1.0 implicit IVs or AEAD salt, if any */
        cw_iv = chunk_create(block.ptr, ivlen);
        block = chunk_skip(block, ivlen);
        sw_iv = chunk_create(block.ptr, ivlen);
        block = chunk_skip(block, ivlen);

对于carol而言,其作为客户端,将生成的客户端写MAC秘钥、加密秘钥和IV数据设置到outbound方向的aead_out中,将服务端扩展秘钥设置到inbound方向的aead_in结构中。

        if (this->tls->is_server(this->tls)) {
                ...
        } else {
                if (!this->aead_out->set_keys(this->aead_out, cw_mac, cw, cw_iv) ||
                        !this->aead_in->set_keys(this->aead_in, sw_mac, sw, sw_iv)) {
                        return FALSE;
                }
        }

以下为RFC5216中的TLS秘钥体系结构图:

            |                       | pre_master_secret       |
      server|                       |                         | client
      Random|                       V                         | Random
            |     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     |
            |     |                                     |     |
            +---->|             master_secret           |<----+
            |     |               (TMS)                 |     |
            |     |                                     |     |
            |     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     |
            |                       |                         |
            V                       V                         V
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                         |
      |                         key_block                       |
      |                   label == "key expansion"              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |         |         |         |         |         |
        | client  | server  | client  | server  | client  | server
        | MAC     | MAC     | write   | write   | IV      | IV
        |         |         |         |         |         |
        V         V         V         V         V         V

                  Figure 1 - TLS [RFC4346] Key Hierarchy

函数expand_keys最后,生成64字节的EAP主会话秘钥(Master Session Key),this->msk_label的值固定为"client EAP encryption"。

        /* EAP-MSK */
        if (this->msk_label) {
                seed = chunk_cata("cc", client_random, server_random);
                this->msk = chunk_alloc(64);
                if (!this->prf->get_bytes(this->prf, this->msk_label, seed, this->msk.len, this->msk.ptr)) {
                        return FALSE;
                }
        }

以下为RFC5216中的EAP-TLS秘钥体系结构图:

         |                       | pre_master_secret       |
   server|                       |                         | client
   Random|                       V                         | Random
         |     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     |
         |     |                                     |     |
         +---->|             master_secret           |<----+
         |     |                                     |     |
         |     |                                     |     |
         |     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+     |
         |                       |                         |
         V                       V                         V
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                         |
   |                        MSK, EMSK                        |
   |               label == "client EAP encryption"          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |             |             |
     | MSK(0,31)   | MSK(32,63)  | EMSK(0,63)
     |             |             |
     |             |             |
     V             V             V

                     Figure 2 - EAP-TLS Key Hierarchy

以下为carol发送的ClientKeyExchange报文:

在这里插入图片描述

Carol之CertificateVerify报文

以上在创建完成carol的ClientKeyExchange报文数据之后,返回build_handshake函数,继续进行循环。再次进入tls_peer.c文件中函数build时,由于状态已经更新为STATE_KEY_EXCHANGE_SENT,将调用send_certificate_verify函数。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        switch (this->state) {
                ...
                case STATE_KEY_EXCHANGE_SENT:
                        if (this->peer) {
                                return send_certificate_verify(this, type, writer);

以下函数send_certificate_verify, 由于之前在发送证书函数send_certificate中,已经使用find_private_key函数获取到了carol中配置的证书私钥,此处sign_handshake函数将使用此私钥对所有服务端发送的handshake消息进行签名。

static status_t send_certificate_verify(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        if (!this->private || !this->crypto->sign_handshake(this->crypto, this->private, writer, this->hashsig)) {
                DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }

        *type = TLS_CERTIFICATE_VERIFY;
        this->state = STATE_VERIFY_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

文件libtls/tls_crypto.c中实现了函数this->crypto->sign_handshake,其主体是对函数sign的调用,此处使用哈希算法为TLS_HASH_SHA256(4),签名算法为TLS_SIG_RSA(1),实际的签名操作由private_key_t结构的成员函数sign完成。

CertificateVerify报文中包含三个部分的数据:hash算法,签名算法和生成的签名数据。函数最后将状态更新为STATE_VERIFY_SENT。

METHOD(tls_crypto_t, sign, bool, private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig)
{
        if (this->tls->get_version(this->tls) >= TLS_1_2) {       
                signature_scheme_t scheme;
                bio_reader_t *reader;
                uint8_t hash, alg;
                chunk_t sig;
                bool done = FALSE;
   
                reader = bio_reader_create(hashsig);
                while (reader->remaining(reader) >= 2) {       
                        if (reader->read_uint8(reader, &hash) && reader->read_uint8(reader, &alg)) {       
                                scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
                                if (scheme != SIGN_UNKNOWN && key->sign(key, scheme, NULL, data, &sig)) {       
                                        done = TRUE;
                                        break;
                                }
                        }
                }

                DBG2(DBG_TLS, "created signature with %N/%N", tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
                writer->write_uint8(writer, hash);
                writer->write_uint8(writer, alg);
                writer->write_data16(writer, sig);

以下为CertificateVerify报文的截图:

在这里插入图片描述

carol的ChangeCipherSpec报文

以上在build_handshake函数处理完handshake协议数据之后,函数结束循环。此时,输出缓存output中包含了之前创建的所有handshake协议数据,output_type设置为TLS_HANDSHAKE。

static status_t build_handshake(private_tls_fragmentation_t *this)
{
        bio_writer_t *hs, *msg;
        tls_handshake_type_t type;

        msg = bio_writer_create(64);
        while (TRUE) {
                hs = bio_writer_create(64);
                status = this->handshake->build(this->handshake, &type, hs);
                switch (status) {
                        case NEED_MORE:
                                msg->write_uint8(msg, type);
                                msg->write_data24(msg, hs->get_buf(hs));
                                DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len);
                                if (!this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                                        hs->destroy(hs);
                                        continue;
                                }
                                /* FALL */
                        case INVALID_STATE:
                                this->output_type = TLS_HANDSHAKE;
                                this->output = chunk_clone(msg->get_buf(msg));
                                break;

在文件strongswan-5.8.1/src/libtls/tls.c中的build函数中,由于handshake协议处理的最后返回值为NEED_MORE,此处的循环将继续,尝试聚合更多的上层Record协议报文。

METHOD(tls_t, build, status_t, private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{
        tls_content_type_t type;
        tls_record_t record;

        len = *buflen;
        if (this->output.len == 0) {
                /* query upper layers for new records, as many as we can get */
                while (TRUE) {
                        status = this->protection->build(this->protection, &type, &data);
                        switch (status) {
                                case NEED_MORE:
                                        record.type = type;
                                        htoun16(&record.version, this->version);
                                        htoun16(&record.length, data.len);
                                        this->output = chunk_cat("mcm", this->output, chunk_from_thing(record), data);
                                        DBG2(DBG_TLS, "sending TLS %N record (%d bytes)", tls_content_type_names, type, data.len);
                                        continue;
                                ...
                        }
                        break;
                }
                if (msglen) { *msglen = this->output.len; }
        } else {
                if (msglen) { *msglen = 0; }
        }

虽然以上的output报文可能很大,但是在以下仅拷贝len长度的数据到发送缓存中,如果output缓存中的数据长度大于len的长度,意味着报文将分片发送。

        len = min(len, this->output.len - this->outpos);
        memcpy(buf, this->output.ptr + this->outpos, len);
        this->outpos += len;
        *buflen = len;
        if (this->outpos == this->output.len) {
                chunk_free(&this->output);
                this->outpos = 0;
                return ALREADY_DONE;
        }
        return NEED_MORE;

在结构tls_fragmentation_t的成员函数build中,由于cipherspec_changed函数返回真,创建ChangeCipherSpec报文。类型为TLS_CHANGE_CIPHER_SPEC(20),数据固定为0x01。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status = INVALID_STATE;

        if (!this->output.len) {
                if (this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                        this->handshake->change_cipherspec(this->handshake, FALSE);
                        *type = TLS_CHANGE_CIPHER_SPEC;
                        *data = chunk_clone(chunk_from_chars(0x01));
                        return NEED_MORE;
                }

如下change_cipherspec函数的实现,其调用tls_crypto_t结构的change_cipher函数,对于carol作为TLS客户端,将其状态修改为STATE_CIPHERSPEC_CHANGED_OUT。

METHOD(tls_handshake_t, change_cipherspec, void, private_tls_peer_t *this, bool inbound)
{
        this->crypto->change_cipher(this->crypto, inbound);
        if (inbound) {
                this->state = STATE_CIPHERSPEC_CHANGED_IN;
        } else {
                this->state = STATE_CIPHERSPEC_CHANGED_OUT;
        }

以下为tls_crypto.c文件中实现的change_cipher函数,这里调用tls_protection_t结构的set_cipher函数,第二个参数inbound为FALSE,最后一个参数使用tls_aead_t结构的aead_out。

METHOD(tls_crypto_t, change_cipher, void, private_tls_crypto_t *this, bool inbound)
{
        if (this->protection) {
                if (inbound) {
                        this->protection->set_cipher(this->protection, TRUE, this->aead_in);
                } else {
                        this->protection->set_cipher(this->protection, FALSE, this->aead_out);
                }

文件tls_protection.c中的set_cipher函数为简单的赋值操作。此后的报文将进行加密处理。

METHOD(tls_protection_t, set_cipher, void, private_tls_protection_t *this, bool inbound, tls_aead_t *aead)
{
        if (inbound) {
                this->aead_in = aead;
        } else {
                this->aead_out = aead;
        }

以下为carol发送的ChangeCipherSpec报文:

在这里插入图片描述

carol的Finished报文

在文件strongswan-5.8.1/src/libtls/tls.c中的build函数中,循环将继续,尝试聚合更多的上层Record协议报文。如下tls_protection_t结构的build函数,在创建上一节的ChangeCipherSpec数据的最后,初始化了private_tls_protection_t结构的aead_out成员,之后的数据包将进行加密处理。TLS_CHANGE_CIPHER_SPEC报文为最后一个明文报文,此时将发送序号seq_out设置为零。

METHOD(tls_protection_t, build, status_t, private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status;

        status = this->compression->build(this->compression, type, data);
        if (status == NEED_MORE) {
                if (*type == TLS_CHANGE_CIPHER_SPEC) {
                        this->seq_out = 0;
                        return status;
                }
                if (this->aead_out) {
                        if (!this->aead_out->encrypt(this->aead_out, this->version, *type, this->seq_out, data)) {
                                DBG1(DBG_TLS, "TLS record encryption failed");
                                chunk_free(data);
                                return FAILED;
                        }
                }
                this->seq_out++;
        }
        return status;

由于上节的change_cipherspec函数中将状态更新为STATE_CIPHERSPEC_CHANGED_OUT,将再次调用函数build_handshake进行处理。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status = INVALID_STATE;

        if (!this->output.len) {
                if (this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                        this->handshake->change_cipherspec(this->handshake, FALSE);
                        *type = TLS_CHANGE_CIPHER_SPEC;
                        *data = chunk_clone(chunk_from_chars(0x01));
                        return NEED_MORE;
                }
                if (!this->handshake->finished(this->handshake)) {
                        status = build_handshake(this);
                }

文件tls_peer.c中的build函数根据STATE_CIPHERSPEC_CHANGED_OUT状态,将调用send_finished创建数据。

METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{       
        switch (this->state)
        {       
                ...
                case STATE_CIPHERSPEC_CHANGED_OUT: 
                        return send_finished(this, type, writer);

以下函数send_finished,其调用calculate_finished完成数据创建,函数最后将状态更新为STATE_FINISHED_SENT。

static status_t send_finished(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        char buf[12];

        if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) {
                DBG1(DBG_TLS, "calculating client finished data failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }

        writer->write_data(writer, chunk_from_thing(buf));

        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;

文件tls_crypto.c中实现了calculate_finished函数,将保存在handshake缓存块中的所有Handshake协议报文数据执行哈希运算,结果作为种子seed,在使用之前的PRF算法,生成12字节的数据作为Finished报文的内容。

METHOD(tls_crypto_t, calculate_finished, bool, private_tls_crypto_t *this, char *label, char out[12])
{
        chunk_t seed;

        if (!this->prf) {
                return FALSE;
        }
        if (!hash_data(this, this->handshake, &seed)) {
                return FALSE;
        }
        if (!this->prf->get_bytes(this->prf, label, seed, 12, out)) {
                free(seed.ptr);
                return FALSE;
        }

由于在结构tls_protection_t的build函数中对Finished报文进行了加密,看不到明文。

在这里插入图片描述

moon服务端接收报文(Certificate)

之前提到了在tls_t结构的函数build中,每次返回一个分片大小的数据,此处客户端的以上报文分成三个分片发送,moon服务端在接收到分片之后,回复一个仅包含EAP-TLS头部信息的请求报文,确认分片的接收,并请求下一个分片。

在服务端接收tls_t结构的process函数中,其接收完所有的分片之后,开始进行处理。与以上的tls_protection_t结构的build函数对于,这里的process函数在接收到TLS_CHANGE_CIPHER_SPEC类型的报文时,将输入序号清空。

METHOD(tls_protection_t, process, status_t, private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
{
        if (this->aead_in) {
                if (!this->aead_in->decrypt(this->aead_in, this->version, type, this->seq_in, &data)) {
                        DBG1(DBG_TLS, "TLS record decryption failed");
                        this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
                        return NEED_MORE;
                }
        }

        if (type == TLS_CHANGE_CIPHER_SPEC) { this->seq_in = 0; }
        else { this->seq_in++; }
        return this->compression->process(this->compression, type, data);

以下tls_fragmentation_t结构的process函数,首先处理Handshake协议报文。

METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
{
        bio_reader_t *reader;

        reader = bio_reader_create(data);
        switch (type) {
                case TLS_CHANGE_CIPHER_SPEC:
                        if (this->handshake->cipherspec_changed(this->handshake, TRUE)) {
                                this->handshake->change_cipherspec(this->handshake, TRUE);
                                status = NEED_MORE;
                                break;
                        }
                        status = FAILED;
                        break;
                case TLS_HANDSHAKE:
                        status = process_handshake(this, reader);
                        break;

文件strongswan-5.8.1/src/libtls/tls_server.c总函数process处理handshake协议报文,由于之前接收处理了ClientHello报文,此处的状态为STATE_HELLO_DONE,当前接收的报文为Certificate,调用process_certificate进行处理。

METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_HELLO_DONE:
                        if (type == TLS_CERTIFICATE) {
                                return process_certificate(this, reader);
                        }

如下process_certificate函数,首先append_hanshake保存Certificate报文数据。其次读取报文开头三个字节的长度值;最后将证书内容读取到certs(bio_reader_t)结构中。之前在解析服务端的Certificate报文时,以及介绍了文件x509_cert.c中x509_cert_load(lib->creds->create指针)函数根据报文数据创建certificate_t证书结构。

static status_t process_certificate(private_tls_server_t *this, bio_reader_t *reader)
{
        certificate_t *cert;
        bio_reader_t *certs;
        chunk_t data;
        bool first = TRUE;

        this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE, reader->peek(reader));

        if (!reader->read_data24(reader, &data)) {
                DBG1(DBG_TLS, "certificate message header invalid");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }
        certs = bio_reader_create(data);
        while (certs->remaining(certs)) {
                if (!certs->read_data24(certs, &data)) {
                        DBG1(DBG_TLS, "certificate message invalid");
                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                        certs->destroy(certs);
                        return NEED_MORE;
                }
                cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_ASN1_DER, data, BUILD_END);
                if (cert) {

随后,将证书添加到auth_cfg_t结构的AUTH_HELPER_SUBJECT_CERT规则中,之后将会用到。函数最后将状态更新为STATE_CERT_RECEIVED。

                        if (first) {
                                this->peer_auth->add(this->peer_auth, AUTH_HELPER_SUBJECT_CERT, cert);
                                DBG1(DBG_TLS, "received TLS peer certificate '%Y'", cert->get_subject(cert));
                                first = FALSE;
                                if (this->peer == NULL) {       /* apply identity to authenticate */
                                        this->peer = cert->get_subject(cert);
                                        this->peer = this->peer->clone(this->peer);
                                        this->peer_auth_optional = TRUE;
                                }
                        } 
                }
        }
        certs->destroy(certs);
        this->state = STATE_CERT_RECEIVED;
        return NEED_MORE;

服务端接收报文(ClientKeyExchange)

由于之前的process_certificate函数已经将状态变更为STATE_CERT_RECEIVED,当前处理的接收报文类型为TLS_CLIENT_KEY_EXCHANGE,调用函数process_key_exchange处理。

METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_CERT_RECEIVED:
                        if (type == TLS_CLIENT_KEY_EXCHANGE) {
                                return process_key_exchange(this, reader);
                        }

这里由于协商了DH组,实际使用process_key_exchange_dhe函数。

static status_t process_key_exchange(private_tls_server_t *this, bio_reader_t *reader)
{
        if (this->dh) {
                return process_key_exchange_dhe(this, reader);
        }
        return process_key_exchange_encrypted(this, reader);

如下process_key_exchange_dhe函数,由于双方协商的加密套件TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,并不属于ECP类型,ec为空。客户端的ClientKeyExchange报文仅包含DH公开数,这里使用diffie_hellman_t结构的成员函数set_other_public_value函数设置公开数,至此服务端获得了DH交互的所有参数,将推导出DH的共享数,此将作为TLS秘钥体系的premaster值。

static status_t process_key_exchange_dhe(private_tls_server_t *this, bio_reader_t *reader)
{
        chunk_t premaster, pub;
        bool ec;

        this->crypto->append_handshake(this->crypto, TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader));

        ec = diffie_hellman_group_is_ec(this->dh->get_dh_group(this->dh));
        if ((ec && !reader->read_data8(reader, &pub)) || (!ec && (!reader->read_data16(reader, &pub) || pub.len == 0))) {
                ...
                return NEED_MORE;
        }
        if (ec) { ... }

        if (!this->dh->set_other_public_value(this->dh, pub)) {
                DBG1(DBG_TLS, "applying DH public value failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }
        if (!this->dh->get_shared_secret(this->dh, &premaster)) {
                DBG1(DBG_TLS, "calculating premaster from DH failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }

随后,服务端根据此premaster数值,以及客户端随机数和服务端随机数,生成主密钥和扩展秘钥,参见函数this->crypto->derive_secrets。函数最后,将状态更新为STATE_KEY_EXCHANGE_RECEIVED。

        if (!this->crypto->derive_secrets(this->crypto, premaster, this->session, this->peer,
                                   chunk_from_thing(this->client_random), chunk_from_thing(this->server_random))) {
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                chunk_clear(&premaster);
                return NEED_MORE;
        }
        chunk_clear(&premaster);

        this->state = STATE_KEY_EXCHANGE_RECEIVED;
        return NEED_MORE;

文件strongswan-5.8.1/src/libtls/tls_crypto.c实现了tls_crypto_t结构,其函数derive_secrets如下。已经在之前客户端创建ClientKeyExchange报文时进行了介绍。

METHOD(tls_crypto_t, derive_secrets, bool, private_tls_crypto_t *this, chunk_t premaster, chunk_t session,
        identification_t *id, chunk_t client_random, chunk_t server_random)
{
        return derive_master(this, premaster, session, id,
                               client_random, server_random) && expand_keys(this, client_random, server_random);

服务端接收报文(CertificateVerify)

由于之前的process_key_exchange函数已经将状态变更为STATE_KEY_EXCHANGE_RECEIVED,当前处理的接收报文类型为TLS_CERTIFICATE_VERIFY,调用函数process_cert_verify处理。

METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state) {
                case STATE_KEY_EXCHANGE_RECEIVED:
                        if (type == TLS_CERTIFICATE_VERIFY) {
                                return process_cert_verify(this, reader);
                        }

以下为process_cert_verify函数,由于在之前除了客户端的Certificate报文时,设置了客户端的公钥,这里取出此公钥,调用this->crypto->verify_handshake验证CertificateVerify报文中携带的签名是否正确。函数最后,将状态更新为STATE_CERT_VERIFY_RECEIVED。

static status_t process_cert_verify(private_tls_server_t *this, bio_reader_t *reader)
{
        bool verified = FALSE;
        enumerator_t *enumerator;
        public_key_t *public;
        auth_cfg_t *auth;
        bio_reader_t *sig;

        enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, KEY_ANY, this->peer, this->peer_auth, TRUE);
        while (enumerator->enumerate(enumerator, &public, &auth)) {
                sig = bio_reader_create(reader->peek(reader));
                verified = this->crypto->verify_handshake(this->crypto, public, sig);
                sig->destroy(sig);
                if (verified) {
                        this->peer_auth->merge(this->peer_auth, auth, FALSE);
                        break;
                }
                DBG1(DBG_TLS, "signature verification failed, trying another key");
        }
        enumerator->destroy(enumerator);

        ...
        {
                this->state = STATE_CERT_VERIFY_RECEIVED;
        }
        this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, reader->peek(reader));
        return NEED_MORE;

文件tls_crypto.c中的函数verify_handshake,调用verify函数完成,其最后一个参数handshake中缓存了所有的Handshake交互报文数据。

METHOD(tls_crypto_t, verify_handshake, bool, private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader)
{
        return verify(this, key, reader, this->handshake);

如下函数verify,报文内容有三部分组成:哈希算法为TLS_HASH_SHA256(4),签名算法为TLS_SIG_RSA(1),最后为签名数据。验证由public_key_t结构的verify函数完成。

METHOD(tls_crypto_t, verify, bool, private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader, chunk_t data)
{
        if (this->tls->get_version(this->tls) >= TLS_1_2) {
                signature_scheme_t scheme = SIGN_UNKNOWN;
                uint8_t hash, alg;
                chunk_t sig;

                if (!reader->read_uint8(reader, &hash) ||
                        !reader->read_uint8(reader, &alg) ||
                        !reader->read_data16(reader, &sig)) {
                        DBG1(DBG_TLS, "received invalid signature");
                        return FALSE;
                }
                scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
                if (scheme == SIGN_UNKNOWN) {
                        DBG1(DBG_TLS, "signature algorithms %N/%N not supported",
                                 tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
                        return FALSE;
                }
                if (!key->verify(key, scheme, NULL, data, sig)) { return FALSE; }

                DBG2(DBG_TLS, "verified signature with %N/%N", tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
        }

服务端接收报文(ChangeCipherSpec)

以上处理完了Handshake报文,接下来是客户端的ChangeCipherSpec报文,类型为TLS_CHANGE_CIPHER_SPEC(20)。调用tls_handshake_t结构的成员函数change_cipherspec进行处理。

METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
{
        bio_reader_t *reader;
        status_t status;

        reader = bio_reader_create(data);
        switch (type) {
                case TLS_CHANGE_CIPHER_SPEC:
                        if (this->handshake->cipherspec_changed(this->handshake, TRUE)) {
                                this->handshake->change_cipherspec(this->handshake, TRUE);
                                status = NEED_MORE;
                                break;
                        }
                case TLS_HANDSHAKE:

文件tls_server.c中函数change_cipherspec,处理完成之后,将状态更新为STATE_CIPHERSPEC_CHANGED_IN。

METHOD(tls_handshake_t, change_cipherspec, void, private_tls_server_t *this, bool inbound)
{
        this->crypto->change_cipher(this->crypto, inbound);
        if (inbound) {
                this->state = STATE_CIPHERSPEC_CHANGED_IN;
        } else {
                this->state = STATE_CIPHERSPEC_CHANGED_OUT;
        }

以下为tls_crypto_t的change_cipher函数,对于服务器而言,inbound为真,调用tls_protection_t结构的set_cipher函数。

METHOD(tls_crypto_t, change_cipher, void, private_tls_crypto_t *this, bool inbound)
{
        if (this->protection) {
                if (inbound) {
                        this->protection->set_cipher(this->protection, TRUE, this->aead_in);
                } else {
                        this->protection->set_cipher(this->protection, FALSE, this->aead_out);
                }

如下tls_protection_t结构的set_cipher函数,为简单的赋值操作,设置了aead_in变量。

METHOD(tls_protection_t, set_cipher, void, private_tls_protection_t *this, bool inbound, tls_aead_t *aead)
{
        if (inbound) {
                this->aead_in = aead;
        } else {
                this->aead_out = aead;
        }

服务端接收报文(Finished)

在处理完成ChangeCipherSpec报文之后,接下来是客户端的Finished报文,由于在上一节设置好了tls_protection_t结构的aead_in成员,并且客户端的Finished报文时加密的,此处进行解密处理。

METHOD(tls_protection_t, process, status_t, private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
{
        if (this->aead_in) {
                if (!this->aead_in->decrypt(this->aead_in, this->version, type, this->seq_in, &data))
                {
                        DBG1(DBG_TLS, "TLS record decryption failed");
                        this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
                        return NEED_MORE;
                }
        }

        if (type == TLS_CHANGE_CIPHER_SPEC) {
                this->seq_in = 0;
        } else {
                this->seq_in++;
        }
        return this->compression->process(this->compression, type, data);

由于Finished属于Handshake协议报文,此处调用process_handshake进行处理。

METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
{
        bio_reader_t *reader;

        reader = bio_reader_create(data);
        switch (type) {
                ...
                case TLS_HANDSHAKE:
                        status = process_handshake(this, reader);

由于目前的状态为STATE_CIPHERSPEC_CHANGED_IN,处理报文的类型为TLS_FINISHED,调用函数process_finished进行处理。

METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state)
        {
                case STATE_CIPHERSPEC_CHANGED_IN:
                        if (type == TLS_FINISHED) {
                                return process_finished(this, reader);
                        }

以下为process_finished函数。读取报文中的12字节长度的数据,调用tls_crypto_t结构的calculate_finished函数,计算Handshake协议数据的哈希值。与读取到12个字节进行比较。最后将状态更新为STATE_FINISHED_RECEIVED。

static status_t process_finished(private_tls_server_t *this, bio_reader_t *reader)
{
        chunk_t received;
        char buf[12];

        if (!reader->read_data(reader, sizeof(buf), &received)) {
                DBG1(DBG_TLS, "received client finished too short");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }
        if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) {
                DBG1(DBG_TLS, "calculating client finished failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }
        if (!chunk_equals_const(received, chunk_from_thing(buf))) {
                DBG1(DBG_TLS, "received client finished invalid");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
                return NEED_MORE;
        }

        this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
        this->state = STATE_FINISHED_RECEIVED;
        return NEED_MORE;

以下为tls_crypto.c结构的calculate_finished函数,其使用tls_prf_t结构的函数get_bytes获取12字节的数据。

METHOD(tls_crypto_t, calculate_finished, bool, private_tls_crypto_t *this, char *label, char out[12])
{
        chunk_t seed;

        if (!this->prf) { return FALSE; }
        if (!hash_data(this, this->handshake, &seed)) {
                return FALSE;
        }
        if (!this->prf->get_bytes(this->prf, label, seed, 12, out)) {
                free(seed.ptr);
                return FALSE;
        }

服务端回复报文(ChangeCipherSpec)

如下tls_eap_t结构的process函数,在process_pkt处理完成carol客户端的报文之后,调用函数build_pkt创建回复报文。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;

        {       
                if (in.len == sizeof(eap_tls_packet_t)) {       
                        DBG2(DBG_TLS, "received %N acknowledgement packet", eap_type_names, this->type);
                        ...
                        return status;
                }
                status = process_pkt(this, pkt);
                switch (status) {       
                        case NEED_MORE:
                                break;
                        case SUCCESS:
                                return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
                        ...
                }
        }
        status = build_pkt(this, out);

如下函数build_pkt,初始化EAP-TLS头部信息,其它由tls_t结构的函数build完成。

static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
{
        char buf[this->frag_size];
        eap_tls_packet_t *pkt;
        size_t len, reclen, msg_len_offset;
        status_t status;
        char *kind;

        if (this->is_server)
        {
                this->identifier++;
        }
        pkt = (eap_tls_packet_t*)buf;
        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        pkt->identifier = this->identifier;
        pkt->type = this->type;
        pkt->flags = this->supported_version;

        ...
        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + msg_len_offset, &len, &reclen);

tls_t结构的build的函数主体是调用了tls_protection_t结构的build函数,启用调用tls_compression_t结构的build函数。注意这里的aead_out结构,由于还没有进行初始化,创建之后的报文将不进行加密。

METHOD(tls_protection_t, build, status_t, private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status;

        status = this->compression->build(this->compression, type, data);
        if (status == NEED_MORE) {
                if (*type == TLS_CHANGE_CIPHER_SPEC) {
                        this->seq_out = 0;
                        return status;
                }
                if (this->aead_out) {
                        if (!this->aead_out->encrypt(this->aead_out, this->version, *type, this->seq_out, data)) {
                                DBG1(DBG_TLS, "TLS record encryption failed");
                                chunk_free(data);
                                return FAILED;
                        }
                }
                this->seq_out++;

如下tls_fragmentation_t结构的build函数,这里调用tls_handshake_t结构的change_cipherspec函数,TLS_CHANGE_CIPHER_SPEC类型报文的数据固定为0x01。

METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
        status_t status = INVALID_STATE;

        if (!this->output.len) {
                if (this->handshake->cipherspec_changed(this->handshake, FALSE)) {
                        this->handshake->change_cipherspec(this->handshake, FALSE);
                        *type = TLS_CHANGE_CIPHER_SPEC;
                        *data = chunk_clone(chunk_from_chars(0x01));
                        return NEED_MORE;
                }

这里inbound参数为FALSE,设置outbound方向的AEAD结构,即tls_protection_t结构中的成员aead_out。状态更新为STATE_CIPHERSPEC_CHANGED_OUT。

METHOD(tls_handshake_t, change_cipherspec, void, private_tls_server_t *this, bool inbound)
{
        this->crypto->change_cipher(this->crypto, inbound);
        if (inbound) {
                this->state = STATE_CIPHERSPEC_CHANGED_IN;
        } else {
                this->state = STATE_CIPHERSPEC_CHANGED_OUT;
        }

moon服务端的ChangeCipherSpec报文如下。

在这里插入图片描述

服务端回复报文(Finished)

文件tls_server.c中函数send_finished如下,与以上客户端发送Finished报文类似,此报文也需经过加密处理。

static status_t send_finished(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
{
        char buf[12];

        if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) {
                DBG1(DBG_TLS, "calculating server finished data failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return FAILED;
        }

        writer->write_data(writer, chunk_from_thing(buf));

        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
        this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));

        return NEED_MORE;

moon网关finished报文如下:

在这里插入图片描述

客户端接收报文(ChangeCipherSpec)

在文件tls_fragmentation.c中函数process根据报文类型TLS_CHANGE_CIPHER_SPEC,调用tls_handshake_t结构的change_ciperspec处理报文。

METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
{
        bio_reader_t *reader;
        status_t status;

        reader = bio_reader_create(data);
        switch (type)
        {
                case TLS_CHANGE_CIPHER_SPEC:
                        if (this->handshake->cipherspec_changed(this->handshake, TRUE)) {
                                this->handshake->change_cipherspec(this->handshake, TRUE);
                                status = NEED_MORE;
                                break;
                        }

其调用tls_crypto_t结构的change_cipher函数设置inbound方向的AEAD结构,最后将状态设置为STATE_CIPHERSPEC_CHANGED_IN。

METHOD(tls_handshake_t, change_cipherspec, void, private_tls_peer_t *this, bool inbound)
{
        this->crypto->change_cipher(this->crypto, inbound);
        if (inbound) {
                this->state = STATE_CIPHERSPEC_CHANGED_IN;
        } else {
                this->state = STATE_CIPHERSPEC_CHANGED_OUT;
        }

客户端接收报文(Finished)

上一节将状态更新为STATE_CIPHERSPEC_CHANGED_IN,当前处理的报文类型为TLS_FINISHED,此处调用process_finished进行处理。

METHOD(tls_handshake_t, process, status_t, private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
{
        tls_handshake_type_t expected;

        switch (this->state)
        {
                case STATE_CIPHERSPEC_CHANGED_IN:
                        if (type == TLS_FINISHED) {
                                return process_finished(this, reader);
                        }

此函数与以上服务端处理Finished报文的流程类似。

static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader)
{
        chunk_t received;
        char buf[12];

        if (!reader->read_data(reader, sizeof(buf), &received)) {
                DBG1(DBG_TLS, "received server finished too short");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }
        if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) {
                DBG1(DBG_TLS, "calculating server finished failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
                return NEED_MORE;
        }
        if (!chunk_equals_const(received, chunk_from_thing(buf))) {
                DBG1(DBG_TLS, "received server finished invalid");
                this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
                return NEED_MORE;
        }
        this->state = STATE_FINISHED_RECEIVED;
        this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);

        return NEED_MORE;

客户端回复报文(Acknowledgement)

由于此时TLS协议的交互报文已经完成,build_pkt函数返回INVALID_STATE,之后使用函数create_ack创建客户确认报文。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        status = build_pkt(this, out);
        switch (status)
        {
                case INVALID_STATE:
                        *out = create_ack(this);
                        return NEED_MORE;

服务端处理报文(Acknowledgement)

服务端的build_pkt函数返回INVALID_STATE,并且tls_t结构的is_complete函数返回真,TLS交互完成。返回SUCCESS。

METHOD(tls_eap_t, process, status_t, private_tls_eap_t *this, chunk_t in, chunk_t *out)
{
        eap_tls_packet_t *pkt;

        {
                if (in.len == sizeof(eap_tls_packet_t)) {
                        DBG2(DBG_TLS, "received %N acknowledgement packet", eap_type_names, this->type);
                        status = build_pkt(this, out);
                        if (status == INVALID_STATE && this->tls->is_complete(this->tls)) {
                                return SUCCESS;
                        }
                        return status;
                }

如下为服务端函数server_process_eap,在处理函数process返回SUCCESS之后,获取主会话秘钥msk,拷贝到EAP认证器结构中。为private_ike_sa_t结构的成员ike_condition_t设置COND_EAP_AUTHENTICATED标志。

static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, eap_payload_t *in)
{
        switch (this->method->process(this->method, in, &out)) {
                ...
                case SUCCESS:
                        if (!vendor && type == EAP_IDENTITY) { ... }

                        if (this->method->get_msk(this->method, &this->msk) == SUCCESS) {
                                this->msk = chunk_clone(this->msk);
                        }
                        if (vendor) {
                                ...
                        } else {
                                DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established", eap_type_names, type, this->msk.ptr ? "" : "no ");
                        }
                        this->ike_sa->set_condition(this->ike_sa, COND_EAP_AUTHENTICATED, TRUE);
                        this->eap_complete = TRUE;
                        return eap_payload_create_code(EAP_SUCCESS, in->get_identifier(in));

以下函数eap_payload_create_code,创建code为EAP_SUCCESS(3),identifier为接收到报文中的EAP头部ID字段值(此处为90)的EAP报文。最后函数eap_payload_create_data创建IKE协议的EAP载荷。

eap_payload_t *eap_payload_create_code(eap_code_t code, uint8_t identifier)
{       
        chunk_t data;
        
        data = chunk_from_chars(code, identifier, 0, 0);
        htoun16(data.ptr + 2, data.len);
        return eap_payload_create_data(data);  

服务器回复的EAP认证成功消息如下:

在这里插入图片描述

客户端处理EAP认证成功报文

以下处理函数process_client,取出报文中的PLV2_EAP载荷的数据,根据Code值,此时为EAP_SUCCESS进行处理。将主会话秘钥MSK缓存在EAP认证器结构中。将EAP_TLS类型添加到ike_sa_t结构中,设置eap_complete为真。

METHOD(authenticator_t, process_client, status_t,  private_eap_authenticator_t *this, message_t *message)
{
        eap_payload_t *eap_payload;

        eap_payload = (eap_payload_t*)message->get_payload(message, PLV2_EAP);
        if (eap_payload) {
                switch (eap_payload->get_code(eap_payload))
                {
                        ...
                        case EAP_SUCCESS: {
                                eap_type_t type;
                                uint32_t vendor;
                                auth_cfg_t *cfg;

                                if (this->method->get_msk(this->method, &this->msk) == SUCCESS) {
                                        this->msk = chunk_clone(this->msk);
                                }
                                type = this->method->get_type(this->method, &vendor);
                                if (vendor) {  ...
                                } else {
                                        DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established", eap_type_names, type, this->msk.ptr ? "" : "no ");
                                }
                                cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
                                cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
                                if (vendor) { ... }
                                this->eap_complete = TRUE;
                                return NEED_MORE;

客户端IKE协议AUTH载荷报文

客户端carol的认为TASK_IKE_AUTH运行之后,执行build_i函数,其调用authenticator_t结构的build函数。

METHOD(task_t, build_i, status_t, private_ike_auth_t *this, message_t *message)
{
        ...
        switch (this->my_auth->build(this->my_auth, message))
        {
                case SUCCESS:
                        apply_auth_cfg(this, TRUE);
                        this->my_auth->destroy(this->my_auth);
                        this->my_auth = NULL;
                        break;
                case NEED_MORE:
                        break;
                default:
                        charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
                        return FAILED;
        }

如下为认证器结构的build_client函数,在EAP完成之后,eap_complete为真,调用build_auth函数。

METHOD(authenticator_t, build_client, status_t, private_eap_authenticator_t *this, message_t *message)
{
        if (this->eap_payload) {
                message->add_payload(message, (payload_t*)this->eap_payload);
                this->eap_payload = NULL;
                return NEED_MORE;
        }
        if (this->eap_complete && build_auth(this, message, this->received_nonce, this->sent_init)) {
                return NEED_MORE;
        }
        return NEED_MORE;

以下函数build_auth,结构keymat_v2_t的成员函数get_psk_sig的参数包括本端的IKE_SA_INIT报文数据(sent_init),对端发送的Nonce值(received_nonce),以及本端的ID标识,和EAP-TLS生成的主会话秘钥MSK。根据这些参数生成Auth载荷数据,认证方式为AUTH_PSK(2)。

static bool build_auth(private_eap_authenticator_t *this, message_t *message, chunk_t nonce, chunk_t init)
{
        auth_payload_t *auth_payload;
        identification_t *my_id;
        chunk_t auth_data;
        keymat_v2_t *keymat;

        my_id = this->ike_sa->get_my_id(this->ike_sa);
        keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);

        DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP);

        if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, this->ppk, my_id, this->reserved, &auth_data)) {
                return FALSE;
        }
        auth_payload = auth_payload_create();
        auth_payload->set_auth_method(auth_payload, AUTH_PSK);
        auth_payload->set_data(auth_payload, auth_data);
        message->add_payload(message, (payload_t*)auth_payload);
        chunk_free(&auth_data);

        if (this->no_ppk_auth) {   ...   }
        return TRUE;

如下文件strongswan-5.8.1/src/libcharon/sa/ikev2/keymat_v2.c中的函数get_psk_sig。其首先调用get_auth_octets生成octets数据。接着使用如下公式生成AUTH数据:AUTH = prf(prf(Shared Secret,“Key Pad for IKEv2”), )。

/* Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
 */
#define IKEV2_KEY_PAD "Key Pad for IKEv2"
#define IKEV2_KEY_PAD_LENGTH 17

METHOD(keymat_v2_t, get_psk_sig, bool, private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce,
        chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], chunk_t *sig)
{
        chunk_t skp_ppk = chunk_empty, key = chunk_empty, octets = chunk_empty;
        chunk_t key_pad;
        bool success = FALSE;

        if (!secret.len) {       /* EAP uses SK_p if no MSK has been established */
                ...
        }
        if (!get_auth_octets(this, verify, ike_sa_init, nonce, ppk, id, reserved, &octets, NULL)) {
                goto failure;
        }
        /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
        key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH);
        if (!this->prf->set_key(this->prf, secret) || !this->prf->allocate_bytes(this->prf, key_pad, &key)) {
                goto failure;
        }
        if (!this->prf->set_key(this->prf, key) || !this->prf->allocate_bytes(this->prf, octets, sig)) {
                goto failure;
        }
        DBG4(DBG_IKE, "secret %B", &secret);
        DBG4(DBG_IKE, "prf(secret, keypad) %B", &key);
        DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", sig);
        success = TRUE;

函数get_auth_octets以及以下公式生成Octet数据

octets = ike_sa_init(send) + nonce(received) + prf(Sk_px, IDx')

以下为carol发送的Auth载荷数据:

在这里插入图片描述

服务端接收IKE协议AUTH载荷报文

文件eap_authenticator.c中的函数process_server处理此报文,由于此时eap_complete为真,此处调用verify_auth函数进行处理。

METHOD(authenticator_t, process_server, status_t, private_eap_authenticator_t *this, message_t *message)
{
        eap_payload_t *eap_payload;

        if (this->eap_complete) {
                if (!verify_auth(this, message, this->sent_nonce, this->received_init)) {
                        return FAILED;
                }
                if (this->method->get_auth) {
                        auth_cfg_t *auth;

                        auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
                        auth->merge(auth, this->method->get_auth(this->method), FALSE);
                }
                return NEED_MORE;

以下verify_auth函数。首先由报文中取出PLV2_AUTH类型的载荷数据。之后,与客户端的操作相同,使用keymat_v2_t结构的函数get_psk_sig创建auth数据,与报文中接收到的数据进行对比。此处keymat_v2_t结构成员函数get_psk_sig使用的参数与客户端不同,IKE_SA_INIT数据为接收的数据,而None为服务端发送的数据。

函数最后将EAP认证器结构的成员变量auth_complete设置为真,认证完成。

static bool verify_auth(private_eap_authenticator_t *this, message_t *message, chunk_t nonce, chunk_t init)
{
        auth_payload_t *auth_payload;
        notify_payload_t *notify;
        chunk_t auth_data, recv_auth_data;
        identification_t *other_id;
        auth_cfg_t *auth;
        keymat_v2_t *keymat;
        eap_type_t type;
        uint32_t vendor;

        auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH);

        recv_auth_data = auth_payload->get_data(auth_payload);

        if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && !this->ppk.ptr) {/*look for a NO_PPK_AUTH notify if we have no PPK */
                ...
        }

        other_id = this->ike_sa->get_other_id(this->ike_sa);
        keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
        if (!keymat->get_psk_sig(keymat, TRUE, init, nonce, this->msk, this->ppk, other_id, this->reserved, &auth_data)) {
                return FALSE;
        }
        if (!auth_data.len || !chunk_equals_const(auth_data, recv_auth_data)) {
                DBG1(DBG_IKE, "verification of AUTH payload with%s EAP MSK failed", this->msk.ptr ? "" : "out");
                chunk_free(&auth_data);
                return FALSE;
        }
        chunk_free(&auth_data);

        DBG1(DBG_IKE, "authentication of '%Y' with %N successful", other_id, auth_class_names, AUTH_CLASS_EAP);
        this->auth_complete = TRUE;
        auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
        auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);

        type = this->method->get_type(this->method, &vendor);
        auth->add(auth, AUTH_RULE_EAP_TYPE, type);
        if (vendor) {
                auth->add(auth, AUTH_RULE_EAP_VENDOR, vendor);
        }
        return TRUE;

服务器回复IKE_AUTH报文

文件task_manager_v2.c中处理函数process_request处理上一节介绍到的carol的IKE请求报文(仅有Auth载荷),由于以上的处理函数process_server返回NEED_MORE,这里调用函数build_response创建响应报文。其将调用到ike_auth.c文件中的任务build_r函数;以及child_create.c文件中的build_r函数。

static status_t process_request(private_task_manager_t *this, message_t *message)
{
        /* let the tasks process the message */
        enumerator = array_create_enumerator(this->passive_tasks);
        while (enumerator->enumerate(enumerator, (void*)&task)) {
                switch (task->process(task, message)) {
                        case SUCCESS:
                                /* task completed, remove it */
                                array_remove_at(this->passive_tasks, enumerator);
                                task->destroy(task);
                                break;
                        case NEED_MORE:
                                /* processed, but task needs at least another call to build() */
                                break;
                        ...
                }
        }
        enumerator->destroy(enumerator);

        return build_response(this, message);

首先看一下文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/ike_auth.c中的函数build_r,内容如下,其调用认证器结构authenticator_t的成员函数build创建报文数据。

METHOD(task_t, build_r, status_t,  private_ike_auth_t *this, message_t *message)
{
        ...
        if (this->other_auth)
        {
                switch (this->other_auth->build(this->other_auth, message)) {
                        case SUCCESS:
                                this->other_auth->destroy(this->other_auth);
                                this->other_auth = NULL;
                                break;
                        case NEED_MORE:
                                break;

如下函数build_server,由于eap_complete和auth_complete都为真,这里调用build_auth为服务端创建Auth载荷字段,其内容与以上介绍相同。返回SUCCESS之后,以上build_r函数,将清空认证器结构。

METHOD(authenticator_t, build_server, status_t, private_eap_authenticator_t *this, message_t *message)
{
        if (this->eap_payload) {
                eap_code_t code;

                code = this->eap_payload->get_code(this->eap_payload);
                message->add_payload(message, (payload_t*)this->eap_payload);
                this->eap_payload = NULL;
                if (code == EAP_FAILURE) { return FAILED; }
                return NEED_MORE;
        }
        if (this->eap_complete && this->auth_complete && build_auth(this, message, this->received_nonce, this->sent_init)) {
                return SUCCESS;
        }

其次,看一下src/libcharon/sa/ikev2/tasks/child_create.c文件中的build_r函数,select_child_cfg以及carol发送的IKE_AUTH报文中的流量选择器信息(10.1.0.0/16 === 192.168.0.100/32),匹配到moon的子连接rw-eap。函数child_sa_create创建子关联结构child_sa_t。

函数select_and_install将选择安全关联提案Proposal:(ESP:AES_CBC_128/HMAC_SHA2_256_128/NO_EXT_SEQ),选取流量选择器。创建子连接秘钥:(initiator加密/验证秘钥,responder加密/验证秘钥)。最后,向内核下发SA和Policy配置。

METHOD(task_t, build_r, status_t, private_child_create_t *this, message_t *message)
{
        payload_t *payload;

        if (this->config == NULL) {
                this->config = select_child_cfg(this);
        }

        this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE);
        this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE);
        this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
        this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
                             this->ike_sa->get_other_host(this->ike_sa), this->config, &this->child);

        switch (select_and_install(this, no_dh, ike_auth)) {
                case SUCCESS:
                        break;
                ...
        }

        if (!build_payloads(this, message)) {
                message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
                handle_child_sa_failure(this, message);
                return SUCCESS;
        }

函数build_payloads创建IKE_AUTH的回复报文载荷,连同之前创建的Auth载荷,一同回复到carol客户端。IKE_AUTH报文如下:

在这里插入图片描述

客户端处理IKE_AUTH响应报文

文件eap_authenticator.c中函数process_client处理carol接收到的IKE_AUTH响应报文,由于eap_complete为真,此处调用verify_auth验证报文中Auth载荷,之前已经介绍此函数。

METHOD(authenticator_t, process_client, status_t, private_eap_authenticator_t *this, message_t *message)
{
        eap_payload_t *eap_payload;

        if (this->eap_complete) {
                if (!verify_auth(this, message, this->sent_nonce, this->received_init)) {
                        return FAILED;
                }
                if (this->require_mutual && !this->method->is_mutual(this->method)) {/* we require mutual authentication due to EAP-only */
                        uint32_t vendor;

                        DBG1(DBG_IKE, "EAP-only authentication requires a mutual and MSK deriving EAP method, but %N is not",
                                 eap_type_names, this->method->get_type(this->method, &vendor));
                        return FAILED;
                }
                return SUCCESS;

以上处理函数在ike_auth.c文件中的process_i任务函数中调用,之后,文件child_create.c中任务函数process_i继续处理报文中其它载荷,由函数process_payloads完成。最后,调用select_and_install创建子连接,并将SA和Policy下发到内核中。连接建立完成。

METHOD(task_t, process_i, status_t, private_child_create_t *this, message_t *message)
{
        ...
        process_payloads(this, message);

        if (select_and_install(this, no_dh, ike_auth) == SUCCESS) {
                if (!this->rekey) {       /* invoke the child_up() hook if we are not rekeying */
                        charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
                }
        } else { ... }

        return SUCCESS;

.
.

strongswan版本: 5.8.1

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页