首頁 > 軟體

Linux核心crypto子系統深入理解

2020-06-16 16:39:58

本文分析Linux kernel裡crypto子系統的大概實現,寫crypto子系統下的加速器驅動的時候可以參考下。crypto子系統支援加解密,壓縮解壓縮等功能。

Analysis will start from crypto test cases in crypto/testmgr.c, e.g. deflate.
上面的路徑上是核心裡這對crypto子系統的一個測試程式。通過分析這個程式可以大概看出crypto子系統向外提供的API. 整個系統的情況大概是這樣的:

crypto API <—> crypto core <—> crypto_register_alg

裝置驅動通過crypto_register_alg把一個裝置支援的演算法註冊到crypto系統裡。
註冊的時候會通過struct crypto_alg把相關的資訊傳遞給crypto core.

struct crypto_alg的結構是這樣的:

struct crypto_alg {
    struct list_head cra_list;
    struct list_head cra_users;

    u32 cra_flags;
    unsigned int cra_blocksize;
    unsigned int cra_ctxsize;
    unsigned int cra_alignmask;

    int cra_priority;
    atomic_t cra_refcnt;

    char cra_name[CRYPTO_MAX_ALG_NAME];
    char cra_driver_name[CRYPTO_MAX_ALG_NAME];

    const struct crypto_type *cra_type;

    union {
        struct ablkcipher_alg ablkcipher;
        struct blkcipher_alg blkcipher;
        struct cipher_alg cipher;
        struct compress_alg compress;
    } cra_u;

    int (*cra_init)(struct crypto_tfm *tfm);
    void (*cra_exit)(struct crypto_tfm *tfm);
    void (*cra_destroy)(struct crypto_alg *alg);

    struct module *cra_module;
} CRYPTO_MINALIGN_ATTR;

這個結構的幾個關鍵的資訊是: cra_ctxsize, cra_u(下面以compress_alg說明), cra_init,
                cra_exit.
這個結構表述的是演算法相關的系統,但是在執行一個請求的時候,還有維護一組上下文的資訊, 這些資訊記錄在結構體: struct crypto_tfm.

struct crypto_tfm {

    u32 crt_flags;

    union {
        struct ablkcipher_tfm ablkcipher;
        struct blkcipher_tfm blkcipher;
        struct cipher_tfm cipher;
        struct compress_tfm compress;
                    --> cot_compress
                    --> cot_decompress
    } crt_u;

    void (*exit)(struct crypto_tfm *tfm);

    struct crypto_alg *__crt_alg;

    void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};

其中最後一個__crt_ctx是這個上下文的私有資料。上面的cra_ctxsize就是這個私有資料的size. cra_init是準備上下文的函數,比如,你用一個硬體裝置壓縮資料,實際的物理操作發生在這個硬體的一個佇列上,那麼就需要準備這個佇列,準備必要的快取等等。cra_exit 是退出上下文。cra_u裡是具體執行演算法的函數,比如可以壓縮和解壓縮的函數。

從裝置驅動的角度講, 裝置驅動只是看到了crypto_alg這個結構。這個結構裡的crypt_tfm 即一個操作執行的上下問是從哪裡知道的呢?畢竟crypto_alg這個結構裡的.cra_init, .cra_exit, .cra_u裡的.coa_compress和.coa_decompress都需要這個執行上下文。
我們在下面具體看一下。

知道這些內部的資料結構對我們理解外部的API有幫助。現在假設crypto的裝置驅動已經有了,那麼,其他的核心模組怎麼用呢? 其實一開頭我們已經講到crypto/testmgr.c測試程式。

測試的程式碼裡有非同步的測試和同步的測試流程,我們這裡先看同步的測試:

主要的邏輯就三個函數, 第一先需要分配一個壓縮的上下文(本文用壓縮的例子), 其實它就是crypto_tfm的包裝,和cryto_tfm是一樣的:

struct crypto_comp {
    struct crypto_tfm base;
};

struct crypto_comp = crypto_alloc_comp(driver, type, mask), 這個過程中會呼叫到cra_init函數,這個函數是裝置驅動實現的,完成硬體相關的設定,上面已經提到過。
呼叫關係如下:

/* 分配一個壓縮解壓縮的上下文, 可以看到這裡的壓縮解壓縮的上下文完全就是crypto_tfm */
struct crypto_comp = crypto_alloc_comp(driver, type, mask);
    --> crypto_alloc_base(alg_name, type, mask)
            /* find algrithm: use alg_name, driver name */
        --> alg = crypto_alg_mod_lookup(alg_name, type, mask);
            /* 上下文是依據具體的演算法去分配的 */
        --> tfm = __crypto_alloc_tfm(alg, type, mask);
            /* 上下文中指定相關的演算法 */
            --> tfm->__crt_alg = alg;
            --> crypto_init_ops
                /* 把相應的演算法中的壓縮解壓縮函數傳遞給上下文 */
                --> crypto_init_compress_ops(tfm)
                        /* ops is struct compress_tfm */
                --> ops->cot_compress = crypto_compress;
                            /* tfm->__crt_alg->cra_u.compress.coa_compress */
                            /*
                            * e.g. drivers/crypto/cavium/zip/zip_main.c
                            *      struct crypto_alg zip_comp_deflate.
                            * will finally call zip_comp_compress!
                            */
                        --> tfm->__crt_alg->cra_compress.coa_compress

                --> ops->cot_decompress = crypto_decompress;
        /*
        * 在建立上下文的最後呼叫下,演算法裡的初始化函數,如果是和一個硬體
        * 的驅動適配,那麼這裡就可以執行相應硬體初始化的內容。
        */
            --> if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))

第二,就是執行壓縮的操作:
crypto_comp_compress(tfm, input, ilen, result, &dlen)

crypto_comp_compress(crypto_comp, src, slen, dst, dlen)
        /* so hardware can do compress here! */
    --> compress_tfm->cot_compress;

第三,就是釋放這個壓縮的上下文
crypto_free_comp(comp)

核心雖然現在提供了壓縮的非同步介面,但是貌似還沒有驅動會用到。非同步介面的使用要比同步介面複雜一點。下面具體看看。

In alg_test_comp, async branch:
/* 和同步一樣,這裡也建立一個非同步的上下文 */
acomp = crypto_alloc_acomp(driver, type, mask);
/*
 * 不過和同步介面不一樣的是,這裡又建立一個acomp_req的上下文, 後續的操作都圍繞著這個req結構展開。可以看到req裡面包含了非同步介面需要的回撥函數。
 */
 
req = acomp_request_alloc(tfm);
acomp_request_set_params(req, &src, &dst, ilen, dlen);
acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
              crypto_req_done, &wait);
crypto_acomp_compress(req)
 
這裡需要說明的是,testmsg.c裡的這個acomp的測試程式裡加了wait/complete的相關內容。這裡應該是為了測試方便而加的,一般的非同步介面裡, 當硬體完成操作的時候,在中斷函數裡直接呼叫非同步介面的回撥函數就可以了。


IT145.com E-mail:sddin#qq.com