[dev][ipsec][dpdk] strongswan/dpdk源碼分析--(七)ipsec算法配置過程


1 簡述

storngswan的配置里用一種固定格式的字符串設置了用於協商的預定義算法。在包協商過程中strongswan將字符串轉換為固定的枚舉值封在數據包里用於傳輸。

協商成功之后,這組被協商選中的枚舉值會通過netlink接口以xfrm定義好的字符串形式,傳遞給內核,內核再將字符串轉換成pfkey定義的枚舉值,最終進行加密設置。

DPDK的話,也有其統一的一組枚舉值的抽象。在調用不同的cryptodev pmd時,會想這組值轉換為對應的值或操作,如轉變成openssl對應的API調用。

見下圖:

 

1.1 名詞解釋

ICV:ICV有兩種翻譯,兩種解釋:Integrity Check Value, initial chaining vector。

通常我們把IV理解為: initial chaining vector; 把ICV理解為:Integrity Check Value

 

2 strongswan

在strongswan的配置里,算法是可以通過字符串進行配置的,這樣的字符串如下:

esp_proposals = aes128-sha384-curve25519-esn,aes128-sha384-esn

逗號分隔的是兩個套裝。橫線分隔的是不同的階段,加密-認證-協商-是否esn

手冊里有詳細的解釋:man  ipsec.conf

The notation is encryption-integrity[-dhgroup][-esnmode].

上面提到的是字符串格式的定義。具體的字符串到數據包中的值的定義,是在RFC中約定的,見文檔:

https://wiki.strongswan.org/projects/strongswan/wiki/IKEv2CipherSuites

https://wiki.strongswan.org/projects/strongswan/wiki/IKEv1CipherSuites

 

3 netlink/xfrm

為了使用xfrm的統一接口,strongswan的netlink模塊做了一次轉換

見代碼:kernel_netlink_ipsec.c

 

4 linux kernel

kernel的xfrm模塊將netlink plugin傳遞過來的string轉換成pfkey中定義的枚舉。

xfrm部分的代碼:xfrm_algo.c

pfkey中的枚舉定義:linux/pfkeyv2.h

 

5 dpdk

dpdk中cryptodev同樣也有自己的封裝定義,按類別分為對稱,非對稱等。

算法的枚舉值定義在這個地方: rte_crypto_sym.h

5.1 關鍵API:

int
rte_cryptodev_sym_session_init(uint8_t dev_id,
            struct rte_cryptodev_sym_session *sess,
            struct rte_crypto_sym_xform *xforms,
            struct rte_mempool *mempool);

如,開篇那個圖,所有的算法都是通過這個API注冊進dpdk的。

在這個數據結構里,它是一個串在一起的鏈表,將加密算法和消息認證碼算法串在一起作為參數傳遞。

struct rte_crypto_sym_xform {
    struct rte_crypto_sym_xform *next;
    /**< next xform in chain */
    enum rte_crypto_sym_xform_type type
    ; /**< xform type */
    RTE_STD_C11
    union {
        struct rte_crypto_auth_xform auth;
        /**< Authentication / hash xform */
        struct rte_crypto_cipher_xform cipher;
        /**< Cipher xform */
        struct rte_crypto_aead_xform aead;
        /**< AEAD xform */
    };
};

接下來逐個討論加密,消息認證和aead的結構:

5.2 加密:

struct rte_crypto_cipher_xform {
    enum rte_crypto_cipher_operation op;   // 加密還是解密
    enum rte_crypto_cipher_algorithm algo;  // 哪一種加密算法
    struct {
        uint8_t *data;  
        uint16_t length;
    } key;                                 // 秘鑰及其長度
    struct {
        uint16_t offset;                  // 對於塊分組加密這里是初始化向量的起點(rte_crypto_op??),對於CRT分組加密,這里是counter, CCM的時候,第一個字節保留,第二個自己用來寫入nonce
        uint16_t length;                  // CRT的時候等於block length, CCM的時候是nonce的長度,7-13之間。
    } iv;
};

5.3 消息認證:

struct rte_crypto_auth_xform {
    enum rte_crypto_auth_operation op;
    enum rte_crypto_auth_algorithm algo;
    struct {
        uint8_t *data;
        uint16_t length;    // 必須小於或等於 block size
    } key;
    struct {
        uint16_t offset;    // 初始化向量,需要八字節對齊
        uint16_t length;
    } iv;
    uint16_t digest_length;        // hash算法的截斷長度
};

5.4 AEAD:

struct rte_crypto_aead_xform {
    enum rte_crypto_aead_operation op;
    enum rte_crypto_aead_algorithm algo;
    struct {
        uint8_t *data;
        uint16_t length;
    } key;
    struct {
        uint16_t offset;      // CCM的時候,第一個字節保留,第二個字節用來寫入nonce
        uint16_t length;      // CCM的時候,nonce的長度,7-13之間
    } iv;
    uint16_t digest_length;
    uint16_t aad_length;      // 附加驗證長度??
};

 

前文的IV offset與一個結構體相關struct rte_crypto_op cop

這個結構體是作為參數傳遞給下面api的

static inline int
rte_crypto_op_attach_sym_session(struct rte_crypto_op *op,
        struct rte_cryptodev_sym_session *sess)

在不使用rte_ipsec庫的情況下,op中的一些值需要用戶關心。使用rte_ipsec庫的時候,一般不用關心。rte_ipsec將其封裝在內部處理。

 

6 rte_ipsec

在rte_ipsec庫中,圍繞這前文提到的加密算法及其參數主要做了以下三方面的動作:

1. 通過API rte_cryptodev_sym_session_init()將算法和參數下發給底層的pmd設備。

2. 為每一個需要加解密的報文准備op(rte_crypto_op),然后attach進session里邊。通過這個函數esp_inb_tun_cop_prepare() [librte_ipsec/sa.c]

3. 為每一個需要加解密的報文准備數據包的esp封裝。通過這個函數esp_inb_tun_pkt_prepare() [librte_ipsec/sa.c]

所以在整個rte_ipsec庫的使用過程中。只有保證以上3者的數據和設置可以正常配合,便能保證成功使用。

詳見:[dev][dpdk][crypto] dpdk加解密設備與IPSEC

(另外:在分析rte_ipsec的源碼過程中,並沒有發現對API rte_crypto_op_attach_sym_session的調用。奇怪。。。)

 

7 strongswan libipsec

基於我們對於dpdk庫的分析,此刻已經了解到,一個算法的基本構成單元,是由方法本身及一組參數構成的。參數為:key長度,iv長度,digest長度,鹽等組成的。

現在回到strongswan,它的代碼里邊除了對rfc中各個函數的定義以為,一定還擁有這些參數值與辦法定義直接的對應關系。

前文,我們已經知道與內核通信時的netlink plugin中對這部分參數在傳遞過程中進行了定義,但由於內核的支持有限,所以也並不完整。同時,strongswan也提供了一個用戶態的esp實現,

叫做libipsec,根據文檔中的描述,該庫對rfc中的所有算法實現了完整的支持。接下來我們對這個庫進行簡要的分析。

 

進入這部分功能的入口在,函數 create_traditional() [/strongswan.git/src/libipsec/esp_context.c] 中。

1. create_traditional() 會調用函數crypto_factory.c::create_crypter()

2. 之后會進入加解密plugin的create函數

METHOD(crypto_factory_t, create_crypter, crypter_t*,
    private_crypto_factory_t *this, encryption_algorithm_t algo,
    size_t key_size)
{
    。。。 。。。
    enumerator = this->crypters->create_enumerator(this->crypters);
    while (enumerator->enumerate(enumerator, &entry))
    {
            。。。 。。。
            crypter = entry->create_crypter(algo, key_size);
            。。。 。。。
    }
    。。。 。。。
}

紅色字體是一個函數指針,是由下文中的這個宏,注冊進去的。

METHOD(plugin_t, get_features, int,
    private_aes_plugin_t *this, plugin_feature_t *features[])
{
    static plugin_feature_t f[] = {
        PLUGIN_REGISTER(CRYPTER, aes_crypter_create),
                        ... ...
    };
        ... ...  
}

 

故,逐個分析每一個算法plugin的實現,將能夠得到具體的參數數值。

 

8 RFC

一些關於算法參數的細節除了可以查看7中的實現以外,我們還可以查看RFC。

 所有RFC的列表可以從第二章的兩個鏈接關聯出來,便能針對不同的算法找到它所對應的RFC文件了。

在RFC中有對各種算法的參數特別詳細的描寫。

https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml

https://www.iana.org/assignments/ipsec-registry/ipsec-registry.xhtml

https://www.iana.org/assignments/isakmp-registry/isakmp-registry.xhtml

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM