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

1 簡述

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

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

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

見下圖:github

 

1.1 名詞解釋

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

一般咱們把IV理解爲: initial chaining vector; 把ICV理解爲:Integrity Check Valuec#

 

2 strongswan

在strongswan的配置裏,算法是能夠經過字符串進行配置的,這樣的字符串以下:api

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

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

手冊裏有詳細的解釋: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

相關文章
相關標籤/搜索