說明:
在默認(rèn)情況下,本文講述的都是ARMV8-aarch64架構(gòu),linux kernel 5.14
思考:
1、Linux Kernel中支持哪些密碼學(xué)算法?分別都是怎么實(shí)現(xiàn)的?哪些是C語言實(shí)現(xiàn)?哪些是Neon指令實(shí)現(xiàn)?哪些是ARM Cryptography Extension硬件實(shí)現(xiàn)?這些不同的實(shí)現(xiàn)方式,他們之間的關(guān)系是怎樣的?并列關(guān)系?多選一?多選多?
2、應(yīng)用程序的密碼學(xué)算法一般又是怎樣實(shí)現(xiàn)的?應(yīng)用程序的密碼學(xué)算法實(shí)現(xiàn),是否依賴Kernel底層的密碼學(xué)算法?
3、應(yīng)用程序是如何調(diào)用到Kernel底層的密碼學(xué)算法?Kernel底層的其它模塊,如何調(diào)用密碼學(xué)算法?
4、如何在Kernel底層增加一種密碼學(xué)算法的實(shí)現(xiàn)?
5、Kernel的其它模塊中,有哪些需要使用密碼學(xué)算法的場(chǎng)景?
本文術(shù)語定義:算法 :算法的種類,如對(duì)稱密碼算法、非對(duì)稱密碼算法...算法實(shí)現(xiàn) :具體的某一類算法,如aes-cbc、aes-ebc、sm4-cbc、twofish-ecb...
目錄
1、密碼學(xué)基礎(chǔ)知識(shí)
2、Kernel密碼學(xué)算法的軟件框架和接口模型
2.1、Userspace對(duì)底層密碼算法的訪問2.2、Kernelspace對(duì)底層密碼算法的訪問2.3、增加一個(gè)算法實(shí)現(xiàn)3、kernel中實(shí)現(xiàn)的算法實(shí)現(xiàn)
4、crypto engine的實(shí)現(xiàn)
5、代碼導(dǎo)讀
1、密碼學(xué)基礎(chǔ)知識(shí)
基本概念,如下請(qǐng)自行學(xué)習(xí)和理解:
-
對(duì)稱密碼
-
非對(duì)稱密碼
-
數(shù)字摘要
-
隨機(jī)數(shù)
2、Kernel密碼學(xué)算法的軟件框架和接口模型
Linux Kernel系統(tǒng)中實(shí)現(xiàn)了很多算法,這些算法被統(tǒng)一歸納為:對(duì)稱密碼算法、數(shù)字摘要算法、隨機(jī)數(shù)算法、認(rèn)證加密算法、非對(duì)稱密碼算法等,并在Kernel層提供了統(tǒng)一操作的接口,供kernel其他模塊調(diào)用。部分算法又被封裝到了網(wǎng)絡(luò)層,開放暴露給Userspace。其具體的結(jié)構(gòu)/接口模型如下所示:

2.1、Userspace對(duì)底層密碼算法的訪問
Userspace通過netlink接口方式( PF_ALG)調(diào)用到底層算法的實(shí)現(xiàn)

在Userspace,需指定socket接口 PF_ALG,需指定算法名稱(如skcipher)、需指定具體調(diào)用的"算法實(shí)現(xiàn)"(如aes-cbc),這樣命令傳輸?shù)終ernel層,就能根據(jù)這些信息跳轉(zhuǎn)到響應(yīng)的算法實(shí)現(xiàn)層。注意akcipher算法沒有暴露給網(wǎng)絡(luò)層,也就沒有開放給Userspace了,所以在User程序中,是無法調(diào)用Kernel層的非對(duì)稱密碼算法的。
如下是一個(gè)Userspace程序調(diào)用kernel底層算法的示例:
(1)建立一個(gè)socket會(huì)話的流程:
socket(AF_ALG,...)bind()setsockoptacceptsendmsgrecvmsg
(2)相關(guān)代碼
static int linux_af_alg_socket(const char *type, const char *name){struct sockaddr_alg sa;int s;s = socket(AF_ALG, SOCK_SEQPACKET, 0);if (s < 0) {LogErr("%s: Failed to open AF_ALG socket: %s ",__func__, strerror(errno));return -1;}os_memset(&sa, 0, sizeof(sa));sa.salg_family = AF_ALG;os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_name));if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {LogErr("%s: Failed to bind AF_ALG socket(%s,%s): %s ",__func__, (char *) sa.salg_type, (char *) sa.salg_name, strerror(errno));close(s);return -1;}return s;}static struct linux_af_alg_skcipher *linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len){struct linux_af_alg_skcipher *skcipher;skcipher = os_zalloc(sizeof(*skcipher));if (!skcipher)goto fail;skcipher->t = -1;skcipher->s = linux_af_alg_socket(TYPE_NAME, alg);if (skcipher->s < 0)goto fail;if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {LogErr("%s: setsockopt(ALG_SET_KEY) failed: %s ",__func__, strerror(errno));goto fail;}skcipher->t = accept(skcipher->s, NULL, NULL);if (skcipher->t < 0) {LogErr("%s: accept on AF_ALG socket failed: %s ",__func__, strerror(errno));goto fail;}return skcipher;fail:linux_af_alg_skcipher_deinit(skcipher);return NULL;}static int aes_128_cbc_oper(char *alg_name, const u8 *key,size_t key_len, int enc, const u8 *iv, u8 *data, size_t data_len){struct linux_af_alg_skcipher *skcipher;char buf[100];struct iovec io[1];struct msghdr msg;struct cmsghdr *hdr;ssize_t ret;u32 *op;struct af_alg_iv *alg_iv;size_t iv_len = AES_BLOCK_SIZE;skcipher = linux_af_alg_skcipher(alg_name, key, key_len);//alg_name = "__cbc-aes-asr-ce"if (!skcipher)return -1;io[0].iov_base = (void *) data;io[0].iov_len = data_len;os_memset(&msg, 0, sizeof(msg));os_memset(buf, 0, sizeof(buf));msg.msg_control = buf;msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +CMSG_SPACE(sizeof(*alg_iv) + iv_len);msg.msg_iov = io;msg.msg_iovlen = 1;hdr = CMSG_FIRSTHDR(&msg);hdr->cmsg_level = SOL_ALG;hdr->cmsg_type = ALG_SET_OP;hdr->cmsg_len = CMSG_LEN(sizeof(u32));op = (u32 *) CMSG_DATA(hdr);*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;hdr = CMSG_NXTHDR(&msg, hdr);hdr->cmsg_level = SOL_ALG;hdr->cmsg_type = ALG_SET_IV;hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);if(NULL != iv){alg_iv->ivlen = iv_len;os_memcpy(alg_iv->iv, iv, iv_len);}else{alg_iv->ivlen = 0;}ret = sendmsg(skcipher->t, &msg, 0);if (ret < 0) {LogErr("%s: sendmsg failed: %s ",__func__, strerror(errno));linux_af_alg_skcipher_deinit(skcipher);return -1;}ret = recvmsg(skcipher->t, &msg, 0);if (ret < 0) {LogErr("%s: recvmsg failed: %s ",__func__, strerror(errno));linux_af_alg_skcipher_deinit(skcipher);return -1;}if ((size_t) ret < data_len) {LogErr("%s: recvmsg not return full data (%d/%d) ",__func__, (int) ret, (int) data_len);linux_af_alg_skcipher_deinit(skcipher);return -1;}//s_to_binary(data,data_len);linux_af_alg_skcipher_deinit(skcipher);return 0;}
2.2、Kernelspace對(duì)底層密碼算法的訪問
Kernel程序?qū)Φ讓铀惴ǖ恼{(diào)用采用函數(shù)直接調(diào)用的方式。流程為:kernel程序--->算法中間層--->算法實(shí)現(xiàn)層. 算法中間層 就是暴露給kernel其它模塊的API函數(shù)。
如下是一個(gè)kernel中調(diào)用底層算法的一個(gè)示例(因skcipher為例):
static int test_skcipher(void){struct crypto_skcipher *tfm = NULL;struct skcipher_request *req = NULL;u8 *data = NULL;const size_t datasize = 512; /* data size in bytes */struct scatterlist sg;DECLARE_CRYPTO_WAIT(wait);u8 iv[16]; /* AES-256-XTS takes a 16-byte IV */u8 key[64]; /* AES-256-XTS takes a 64-byte key */int err;/** Allocate a tfm (a transformation object) and set the key.** In real-world use, a tfm and key are typically used for many* encryption/decryption operations. But in this example, we'll just do a* single encryption operation with it (which is not very efficient).*/tfm = crypto_alloc_skcipher("xts(aes)", 0, 0);if (IS_ERR(tfm)) {pr_err("Error allocating xts(aes) handle: %ld ", PTR_ERR(tfm));return PTR_ERR(tfm);}get_random_bytes(key, sizeof(key));err = crypto_skcipher_setkey(tfm, key, sizeof(key));if (err) {pr_err("Error setting key: %d ", err);goto out;}/* Allocate a request object */req = skcipher_request_alloc(tfm, GFP_KERNEL);if (!req) {err = -ENOMEM;goto out;}/* Prepare the input data */data = kmalloc(datasize, GFP_KERNEL);if (!data) {err = -ENOMEM;goto out;}get_random_bytes(data, datasize);/* Initialize the IV */get_random_bytes(iv, sizeof(iv));/** Encrypt the data in-place.** For simplicity, in this example we wait for the request to complete* before proceeding, even if the underlying implementation is asynchronous.** To decrypt instead of encrypt, just change crypto_skcipher_encrypt() to* crypto_skcipher_decrypt().*/sg_init_one(&sg, data, datasize);skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |CRYPTO_TFM_REQ_MAY_SLEEP,crypto_req_done, &wait);skcipher_request_set_crypt(req, &sg, &sg, datasize, iv);err = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);if (err) {pr_err("Error encrypting data: %d ", err);goto out;}pr_debug("Encryption was successful ");out:crypto_free_skcipher(tfm);skcipher_request_free(req);kfree(data);return err;}
2.3、增加一個(gè)算法實(shí)現(xiàn)
增加一個(gè)"算法的實(shí)現(xiàn)" 只需要:
-
定義一個(gè)該算法的結(jié)構(gòu)體變量并初始化,其實(shí)就是實(shí)現(xiàn)其中的成員函數(shù)
-
將該算法實(shí)現(xiàn)注冊(cè)到系統(tǒng)中。
結(jié)構(gòu)體的定義并初始化:
static struct skcipher_alg aes_algs[] = {{.base.cra_name = "__ecb(aes)",.base.cra_driver_name = "__ecb-aes-neonbs",.base.cra_priority = 250,.base.cra_blocksize = AES_BLOCK_SIZE,.base.cra_ctxsize = sizeof(struct aesbs_ctx),.base.cra_module = THIS_MODULE,.base.cra_flags = CRYPTO_ALG_INTERNAL,.min_keysize = AES_MIN_KEY_SIZE,.max_keysize = AES_MAX_KEY_SIZE,.walksize = 8 * AES_BLOCK_SIZE,.setkey = aesbs_setkey,.encrypt = ecb_encrypt,.decrypt = ecb_decrypt,},{.base.cra_name = "__cbc(aes)",.base.cra_driver_name = "__cbc-aes-neonbs",.base.cra_priority = 250,.base.cra_blocksize = AES_BLOCK_SIZE,.base.cra_ctxsize = sizeof(struct aesbs_cbc_ctx),.base.cra_module = THIS_MODULE,.base.cra_flags = CRYPTO_ALG_INTERNAL,.min_keysize = AES_MIN_KEY_SIZE,.max_keysize = AES_MAX_KEY_SIZE,.walksize = 8 * AES_BLOCK_SIZE,.ivsize = AES_BLOCK_SIZE,.setkey = aesbs_cbc_setkey,.encrypt = cbc_encrypt,.decrypt = cbc_decrypt,}};
成員函數(shù)的實(shí)現(xiàn),例如:
static int ecb_encrypt(struct skcipher_request *req){return __ecb_crypt(req, aesbs_ecb_encrypt);}
將該算法實(shí)現(xiàn)注冊(cè)到系統(tǒng)中:
小小總結(jié)一下, 如果您要增加一個(gè)算法實(shí)現(xiàn),那么您就是需要實(shí)現(xiàn)定義如下結(jié)構(gòu)體,并調(diào)用static int __init aes_init(void){...err = crypto_register_skciphers(aes_algs, ARRAY_SIZE(aes_algs));...}module_init(aes_init);
crypto_register_xxx()注冊(cè)到kernel系統(tǒng)中:- skcipher_alg
- akcipher_alg
- ahash_alg
- rng_alg
- aead_alg
3、kernel中實(shí)現(xiàn)的算法實(shí)現(xiàn)
思考:
對(duì)稱密碼底層是怎樣實(shí)現(xiàn)的?純軟?硬件?Neon指令?CE指令?
非對(duì)稱密碼底層是怎樣實(shí)現(xiàn)的?
Hash、rng、aead 又都是怎樣實(shí)現(xiàn)的?
實(shí)現(xiàn)算法的方式:
-
(1)在armv8/armv9的芯片中,有ARM-CE指令可以進(jìn)行aes/hash/md5計(jì)算,
-
(2)在armv8/armv9的芯片中,也有ARM-NEON指令也可以進(jìn)行aes/hash/md5計(jì)算
-
(3)arm的security IP中,有cryptocell之類的加密芯片
-
(4)另外SOC廠商也可能集成自己設(shè)計(jì)的crypto engine加解密芯片
毫無疑問,在效率這塊肯定是:(3)(4) > (1) > (2) > (5).另外從"實(shí)現(xiàn)算法的方式" 來看,如果是rng、aead、rsa之類的算法,那么就不能用ARM-CE這種方式,只有編程語言實(shí)現(xiàn)、Neon指令實(shí)現(xiàn)、crypto engine(含arm security IP)這幾種方式了。
kernel怎么玩的?:
-
針對(duì) crypto engine(含arm security IP) 這種,先當(dāng)SOC硬件不支持,跳過此場(chǎng)景。
-
針對(duì)rng、aead、rsa,那么kernel有一套純軟的實(shí)現(xiàn) (似乎沒有看到arm neon指令的實(shí)現(xiàn))
-
針對(duì)aes、hash,有arm-ce的實(shí)現(xiàn)、arm neon指令的實(shí)現(xiàn)、純軟的實(shí)現(xiàn),三者三選一(通過宏開關(guān),只能選1)
crypto engine的實(shí)現(xiàn):如果自定義了crypto engine的實(shí)現(xiàn),那么要看你具體的設(shè)計(jì),是設(shè)計(jì)成“取代原有算法實(shí)現(xiàn)”,還是設(shè)計(jì)成“新增算法實(shí)現(xiàn)”。如果是前者,那么對(duì)于aes/hash,則變成了四選一的了(crypto engine實(shí)現(xiàn)、arm-ce的實(shí)現(xiàn)、arm neon指令的實(shí)現(xiàn)、純軟)。如果是后者,這和原有實(shí)現(xiàn)不沖突。
有關(guān)aes/hash底層實(shí)現(xiàn)三選一的開關(guān):
(1) 開啟下面兩個(gè)宏,使用ARM Neon指令的實(shí)現(xiàn) CONFIG_CRYPTO_AES_ARM64_CE_BLK CONFIG_CRYPTO_AES_ARM64_NEON_BLK(2) 在(1) 的基礎(chǔ)之上,再開啟如下宏,使用ARM CE指令的實(shí)現(xiàn) USE_V8_CRYPTO_EXTENSIONS(3) 以上三個(gè)宏都不開啟的情況下,使用默認(rèn)的純軟實(shí)現(xiàn)
4、crypto engine的實(shí)現(xiàn)
(以ARM Security IP的cryptocell 712為例)

在Linux Kernel中開啟 CONFIG_CRYPTO_DEV_CCREE宏控即可起用該實(shí)現(xiàn), 代碼路徑如下:

以為aes-cbc為例,其實(shí)現(xiàn)的名字 和 Kernel中默認(rèn)是算法實(shí)現(xiàn)的名字是一致的,即使這種實(shí)現(xiàn)方式是取代原有算法實(shí)現(xiàn)
{.name = "cbc(aes)",.driver_name = "cbc-aes-ccree",.blocksize = AES_BLOCK_SIZE,.template_skcipher = {.setkey = cc_cipher_setkey,.encrypt = cc_cipher_encrypt,.decrypt = cc_cipher_decrypt,.min_keysize = AES_MIN_KEY_SIZE,.max_keysize = AES_MAX_KEY_SIZE,.ivsize = AES_BLOCK_SIZE,},.cipher_mode = DRV_CIPHER_CBC,.flow_mode = S_DIN_to_AES,.min_hw_rev = CC_HW_REV_630,.std_body = CC_STD_NIST,}
4、代碼導(dǎo)讀
在網(wǎng)絡(luò)層、算法中間層、算法實(shí)現(xiàn)層有著豐富的結(jié)構(gòu)體類型?那么怎么去閱讀代碼?怎弄清各個(gè)層面之間的邏輯呢?事實(shí)上我們只要理清這些結(jié)構(gòu)體之間的關(guān)系,將其抽象成模型,就會(huì)變得更加容易理解了。
如下是以Userspace調(diào)用底層的對(duì)稱密碼函數(shù)為例總結(jié)的一張數(shù)據(jù)結(jié)構(gòu)圖:

sock通信進(jìn)入網(wǎng)絡(luò)層后(algifskcipher.c),構(gòu)建skcipherrequest結(jié)構(gòu)體,通過該結(jié)構(gòu)體,就能尋址到底層的算法實(shí)現(xiàn),繼而完成算法實(shí)現(xiàn)的調(diào)用。這些總結(jié)一下就是:
-
skcipher_request //網(wǎng)絡(luò)層構(gòu)建的結(jié)構(gòu)體
-
cryptoskcipher // kernel中間層構(gòu)建的結(jié)構(gòu)體,如果是kernel層調(diào)用底層算法,那么就從構(gòu)建cryptocipher結(jié)構(gòu)體開始。
-
skcipher_alg //算法實(shí)現(xiàn)層的結(jié)構(gòu)體,描述著具體的算法實(shí)現(xiàn),有實(shí)現(xiàn)廠商自己添加。
上述復(fù)雜的結(jié)構(gòu)體流程,進(jìn)一步抽象,就變成如下這個(gè)樣子:

既然如此,那么我們還可以舉一反三一下:

審核編輯 :李倩
-
算法
+關(guān)注
關(guān)注
23文章
4784瀏覽量
98101 -
Linux
+關(guān)注
關(guān)注
88文章
11768瀏覽量
219106 -
代碼
+關(guān)注
關(guān)注
30文章
4970瀏覽量
74017
原文標(biāo)題:一文了解Linux Kernel中密碼學(xué)算法的設(shè)計(jì)與應(yīng)用
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
力旺電子攜手熵碼科技達(dá)成全方位后量子密碼學(xué)防護(hù)里程碑
不止于數(shù)學(xué):實(shí)際部署是筑牢后量子安全的關(guān)鍵環(huán)節(jié)
AMI在Aptio V UEFI固件中成功部署后量子密碼學(xué)
SM4算法實(shí)現(xiàn)分享(一)算法原理
復(fù)雜的軟件算法硬件IP核的實(shí)現(xiàn)
AES加解密算法邏輯實(shí)現(xiàn)及其在蜂鳥E203SoC上的應(yīng)用介紹
國(guó)密系列算法簡(jiǎn)介及SM4算法原理介紹
RISCV-K指令集擴(kuò)展分享
在Ubuntu20.04系統(tǒng)中訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型的一些經(jīng)驗(yàn)
電科網(wǎng)安助力第三屆“熵密杯”密碼安全挑戰(zhàn)賽圓滿收官
潤(rùn)和軟件StackRUNS異構(gòu)分布式推理框架的應(yīng)用案例
潤(rùn)和軟件發(fā)布StackRUNS異構(gòu)分布式推理框架
Kernel密碼學(xué)算法的軟件框架和接口模型
評(píng)論