Kernel Crypto框架


隨着數字時代的發展,每天都有海量的數據產生,並且用戶也越來越重視個人隱私數據的安全,從某種意義上講,用戶個人數據的價值正逐步高於設備本身。實現數據安全保護的基礎是【密鑰 + 加密算法】;對於加密算法,kernel其實早在linux-2.5.45版本中就引入了crypto基礎能力。本篇文章主要講關於kernel crypto算法框架,以及結合它在文件系統加密這一場景中的應用,分析內部的實現細節,以便讀者對crypto框架有相關的認識,並能基於它做開發。

本篇文章的所有代碼都是圍繞Linux-4.19,以OOP思想進行闡述,大家可以結合源碼進行閱讀。

為方便理解,我們約定一些術語:

  • 數據轉換(transformation/TFM):不管是對數據做加/解密,還是做hash,都定義成是對數據所做的轉換操作。

  • 算法:本文當中的算法並不特指加/解密算法。

一、Crypto子系統簡介

1.功能

Kernel crypto是內核實現的一套通用crypto算法框架,是一個獨立的子系統,源碼在kernel/crypto下,它實現了對算法的統一管理,並提供出統一的數據處理接口給其他子系統使用;因此基於這套框架,我們不僅可以使用kernel已有的crypto算法對數據做轉換,還能自行擴展添加算法。

Kernel crypto 當前實現了對稱加解密,非對稱加解密,認證加解密,hash,Hmac,DRBG偽隨機數生成算法和壓縮算法。

2.適用場景

Kernel crypto主要用於kernel層的安全特性實現,但在user-space也可以通過系統調用的方式來使用它;因為在Linux-2.6.38中已經通過socket (addr family: AF_ALG)方式導出接口到user-space.

在開發時如要快速確認kernel中是否支持某種算法,可以cat /proc/crypto 查看,如下圖1.1。

圖1.1 kernel支持的加密算法

name代表算法名稱,hmac是對應的模式(抽象成template);priority代表算法的優先級(在相同名稱下,數字越大代表優先級越高,默認使用高優先級的算法);selftest代表開機算法自檢結果;type指算法類型;async指異步方式調用;blocksize指最小單個數據處理塊大小;min keysize和max keysize指算法的最小/最大密鑰長度;ivsize指算法的IV初始向量長度。

*selftest之后的所有字段其實都是crypto_type->show()所提供的,后面會提及。

3.整體架構

下面通過一張圖來展示crypto的整體框架。

圖1.2 kernel crypto框架

crypto core是最基本骨架 ,它提供crypto的核心組件(包括crypto_alg,crypto_template的管理,cryptd內核線程等);基於crypto core,內核實現了8類常用的算法,DRBG偽隨機數算法,Hash算法,SKCIPHER對稱加解密算法,AKCIPHER非對稱加解密算法,AEAD認證加密算法,HMAC算法,COMPRESS壓縮算法,KPP密鑰協商算法。

一些用於secure的硬件模塊(如hw_rng硬件隨機數產生器,qce硬加密模塊)的驅動程序,會通過crypto core提供的算法注冊接口(crypto_register_alg)將其注冊到crypto子系統中,並且在注冊時會對算法做靜態正確性自檢,並在/proc/crypto中的selftest中呈現到userspace。除了注冊到crypto子系統以外,驅動也可以通過VFS以設置節點形式提供給用戶空間使用(如/dev/qce,/dev/hw_rng)。

Crypto core通過socket方式,將kernel層的算法能力提供給用戶空間。

二、Crypto核心數據結構及邏輯實現

算法本質上就是一些對數據進行邏輯處理的函數,在應用層的實現普遍是直接實現一個加解密接口,應用程序直接調用,對使用者來說極為方便。但在kernel中,使用起來會較為復雜些,因為它需要考慮到算法的易擴展性,通用性,易維護性等,因此對算法做了高度的抽象化;所以要理解kernel crypto的設計思想,必須要理解它的幾個核心struct。

1.核心數據結構

在軟件界有句話寫的很實在:”軟件=文件+程序,程序=數據結構+算法”;由此能看出弄清數據結構的重要性。

Kernel crypto中基本所有操作都是圍繞着幾個核心數據結構展開:struct crypto_alg,struct crypto_template,struct crypto_instance,struct crypto_tfm,struct crypto_type。其他算法都可以基於它們做擴展。例如struct skcipher_alg,struct shash_alg都是繼承自struct crypto_alg,見下圖2.1:

圖2.1 crypto數據結構uml類圖

下面對這6個結構體做相關說明:

1)struct crypto_template

算法模板,一般在module_init時通過調用crypto_register_template接口注冊到crypto_template_list鏈表中。

在算法加密中,分塊加密模式分為很多種,以對稱加解密為例,有CBC,ECB,GCM,CTR,XTS,而這些加密模式適用於所有的對稱加密算法,如AES,DES;因此kernel就將加密模式抽象成模板,在開發新的算法時只需要實現單個block的數據處理(加密,hmac等);在申請使用算法時,我們通過算法名來組合出相應的算法(kernel會將組合出來的算法動態注冊到crypto子系統),格式為template(single block cipher),例如cbc(aes),ecb(des)。

  • list用於模塊的crypto_template_list鏈表管理;

  • instance用於管理當前模板下所有的crypto_instance;

  • alloc接口用於申請算法實例;

  • free用於釋放算法實例;

2)struct crypto_alg

crypto_alg是個基類,任何算法都可以基於它派生出衍生類;每個算法都對應着一個struct crypto_alg實例,一般在module_init中調用crypto_register_alg接口將具體的crypto_alg對象添加到crypto_alg_list鏈表中。這里有一個很重要的數據成員cra_u,因為它體現了kernel crypto架構設計者的設計思想:它將四種比較常用的算法類型的處理方式抽象到基類當中,即如果你要添加的算法為這4類,就只需要實現這4類算法所對應的方法,如果不是這4類當中,就需要在基類上做派生,實現特定的crypto_type。

  • cra_list:是用作鏈表管理

  • cra_users:此算法被引用的所有crypto_spawn實例鏈表。

  • cra_blocksize:是單個處理數據塊大小

  • cra_ctxsize:為transformation context大小

  • cra_alignmask:指待處理數據buffer的對齊要求

  • cra_priority:是當前算法優先級

  • cra_refcnt:為當前算法的引用計數

  • cra_name和cra_driver_name:分別指代算法名及驅動名

  • cra_type:指算法類型;cra_u將四大類算法類型進行了統一。

  • cra_init:是用於每次數據操作上下文前的初始化,比如在硬件加密中,會實現此接口對相關寄存器做初始化;cra_exit則與前者相反。

  • cra_destroy:是用於crypto在kernel中注銷的相關操作。

3)struct crypto_instance

這個結構體是代表kernel通過template動態創建的算法實例,並且會與crypto_template相關聯,可以看到這里的alg並不是個指針。它是通過template->alloc()創建的,創建的同時,會將算法name初始化。

  • __ctx:當前只指向crypto_spawn,我個人理解可能是架構設計者考慮到未來擴展性,就將crypto_spawn與crypto_instance拆分開來了。

4)struct crypto_spawn

通過模板動態生成的算法實例的一部分。

  • list:添加到crypto_alg->cra_users鏈表中。

  • frontend:見下文。

5)struct crypto_type

crypto_type就是用於重載crypto_alg中的cra_u中的各個類中的成員函數,當通過crypto_alloc_base,crypto_create_tfm等接口申請相應的crypto的TFM上下文時,若有傳入crypto_type參數,TFM優先使用crypto_type中的init_tfm成員函數去初始化crypto_tfm衍生類的操作方法。

  • ctxsize:獲取當前算法類型TFM上下文大小(crypto_tfm+crypto_tfm.__crt_ctx)

  • extsize:獲取當前算法類型TFM上下文大小(即crypto_tfm衍生類的大小)。

  • init:一般為空(功能與init_tfm類似,通常在后者中初始化TFM)。

  • init_tfm:顧名思義,初始化TFM。

  • show:呈現當前算法類型的基本信息,/proc/crypto后半段信息就是從這獲取的。

  • free:釋放crypto_instance。

6)struct crypto_tfm

具體算法處理(transformation)上下文的實例,里面會將此次算法上下文的key,IV等信息設置到__crt_ctx中。

  • crt_u:算法的operation,kernel會在__crypto_alloc_tfm接口中關聯到crypto_type或xxx_alg中的實現方法。

2.通過用例介紹crypto子系統邏輯

那我們在使用kernel中的算法時,框架內部是如何做處理的呢?下面通過一個例子來說明這個問題。在文件系統加密(FBE)中通過kernel crypto做密鑰派生。

背景:在Android 7.0時,引入了文件加密功能,所謂文件加密,即每個文件都用不同的key對文件進行加密。

原理:密鑰派生中,使用了crypto中的ecb(aes)算法通過類密鑰及inode.nonce派生出每個文件的密鑰。

具體實現在kernel/fs/crypto/keyinfo.c,如下圖2.2所示:

圖2.2 crypto在FBE中的應用

1)申請“ecb(aes)”算法的tfm上下文。這里會涉及到“算法動態注冊”,即如果在crypto_alg_list鏈表中沒有找到name為”ecb(aes)”的crypto_alg對象,那crypto子系統會通過一個名為”cryptomgr_probe”的內核線程查找到name為“ecb”的crypto_template對象,以及查找到name為”aes”的crypto_alg對象,動態創建出一個name為“ecb(aes)”的crypto_alg並注冊到鏈表當中。調用流程如圖2.3

圖2.3 算法動態注冊流程

在獲取到了crypto_alg后,就會申請crypto_tfm,並用crypto_alg->cra_init()或crypto_type->init_tfm()對其進行初始化(主要是當前算法的各個函數指針)。

2) 在tfm上下文中申請一個數據處理請求(req)。一個tfm中,可以做多次數據加解密。這里只是申請內存,並關聯到tfm的操作。

3) 設置密鑰到tfm->__crt_ctx中。

4) 把待加密數據信息放入req當中。

5)以異步方式調用crypto_skcipher_encrypt對req做加密處理,線程在此會block一段時間,直到req請求被處理完成。因此不能在中斷上下文中使用。

3.算法自檢

出於安全性考慮,FIPS等相關標准要求在系統開機時必須做算法正確性自檢,如果自檢失敗,則停止系統的啟動;因此算法自檢這部分自然也在框架中實現了。(源碼位於:kernel/crypto/testmgr.c)

Kernel中的算法自檢為靜態自檢,即給定輸入參數,及正確結果,如果算法計算出來的結果與正確結果不匹配,則自檢失敗。

算法自檢的時間點固定為每個crypto_alg注冊的時候,具體流程如下圖2.4所示:

圖2.4 算法自檢流程

三、總結

Kernel crypto在設計時進行了高度的抽象化,本篇文章主要通過一些核心數據結構,並采用OOP角度來揭示各個struct的功能及之間的關系,以便讀者能了解架構設計者的初衷。由於篇幅所限,本文未對kernel中已支持的所有算法的使用方式做介紹,這部分讀者可直接參考算法自檢中的代碼實現。

參考文獻

[1] Kernel Crypto API Architecture:https://kernel.org/doc/html/crypto/architecture.html

[2] Linux kernel 4.19源代碼

掃碼關注
“內核工匠”微信公眾號
Linux 內核黑科技 | 技術文章 | 精選教程


免責聲明!

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



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