開發密碼演算法

註冊與登出變換

加密 API 中有三種不同型別的註冊函式。一種用於註冊通用密碼變換,而另外兩種則特定於 HASH 變換和壓縮。我們將在單獨的章節中討論後兩者,此處僅介紹通用型別。

在討論註冊函式之前,必須考慮用於填充每個註冊函式的資料結構,即 struct crypto_alg —— 該資料結構的描述見下文。

通用註冊函式可以在 include/linux/crypto.h 中找到,其定義見下文。前者註冊單個變換,而後者則對一個變換描述陣列進行操作。後者在批次註冊變換時很有用,例如當驅動程式實現多個變換時。

int crypto_register_alg(struct crypto_alg *alg);
int crypto_register_algs(struct crypto_alg *algs, int count);

這些函式對應的登出函式列出如下。

void crypto_unregister_alg(struct crypto_alg *alg);
void crypto_unregister_algs(struct crypto_alg *algs, int count);

註冊函式成功時返回 0,失敗時返回負的 errno 值。crypto_register_algs() 僅在成功註冊所有給定演算法時才成功;如果中途失敗,則所有更改都將回滾。

登出函式總是成功,因此它們沒有返回值。不要嘗試登出當前未註冊的演算法。

單塊對稱密碼 [CIPHER]

變換示例:aes, serpent, ...

本節描述了所有變換實現中最簡單的一種,即用於對稱密碼的 CIPHER 型別。CIPHER 型別用於每次精確操作一個塊的變換,並且塊之間沒有任何依賴關係。

註冊細節

[CIPHER] 演算法的註冊特點在於 struct crypto_alg 欄位 .cra_type 為空。.cra_u.cipher 必須填充適當的回撥函式以實現此變換。

見下文中的 struct cipher_alg

使用 struct cipher_alg 定義密碼

Struct cipher_alg 定義了一個單塊密碼。

以下是這些函式在核心其他部分被呼叫時的示意圖。請注意,.cia_setkey() 呼叫可能發生在這些示意圖所示的任何操作之前或之後,但絕不能在任何這些操作進行中發生。

KEY ---.    PLAINTEXT ---.
       v                 v
 .cia_setkey() -> .cia_encrypt()
                         |
                         '-----> CIPHERTEXT

請注意,多次呼叫 .cia_setkey() 的模式也是有效的

KEY1 --.    PLAINTEXT1 --.         KEY2 --.    PLAINTEXT2 --.
       v                 v                v                 v
 .cia_setkey() -> .cia_encrypt() -> .cia_setkey() -> .cia_encrypt()
                         |                                  |
                         '---> CIPHERTEXT1                  '---> CIPHERTEXT2

多塊密碼

變換示例:cbc(aes), chacha20, ...

本節描述了多塊密碼變換的實現。多塊密碼用於對提供給變換函式的資料 scatterlist 進行操作的變換。它們也將結果輸出到資料 scatterlist 中。

註冊細節

多塊密碼演算法的註冊是整個加密 API 中最標準的程式之一。

請注意,如果密碼實現需要適當的資料對齊,呼叫者應使用 crypto_skcipher_alignmask() 函式來識別記憶體對齊掩碼。核心加密 API 能夠處理未對齊的請求。然而,這會帶來額外的開銷,因為核心加密 API 需要執行資料重新對齊,這可能意味著資料移動。

使用 struct skcipher_alg 定義密碼

Struct skcipher_alg 定義了一個多塊密碼,或者更一般地說,一個保持長度的對稱密碼演算法。

Scatterlist 處理

某些驅動程式會希望使用通用 ScatterWalk,以防硬體需要分別處理包含明文和將包含密文的 scatterlist 塊。請參考 Linux 核心 scatter / gather 列表實現提供的 ScatterWalk 介面。

雜湊 [HASH]

變換示例:crc32, md5, sha1, sha256,...

註冊與登出變換

註冊 HASH 變換有多種方式,取決於變換是同步 [SHASH] 還是非同步 [AHASH],以及我們正在註冊的 HASH 變換的數量。您可以在 include/crypto/internal/hash.h 中找到定義的原型。

int crypto_register_ahash(struct ahash_alg *alg);

int crypto_register_shash(struct shash_alg *alg);
int crypto_register_shashes(struct shash_alg *algs, int count);

用於登出 HASH 變換的相應函式如下

void crypto_unregister_ahash(struct ahash_alg *alg);

void crypto_unregister_shash(struct shash_alg *alg);
void crypto_unregister_shashes(struct shash_alg *algs, int count);

使用 struct shash_alg 和 ahash_alg 定義密碼

以下是這些函式在核心其他部分被呼叫時的示意圖。請注意,.setkey() 呼叫可能發生在這些示意圖所示的任何操作之前或之後,但絕不能在任何這些操作進行中發生。請注意,緊接著呼叫 .init() 後立即呼叫 .final() 也是一個完全有效的變換。

I)   DATA -----------.
                     v
      .init() -> .update() -> .final()      ! .update() might not be called
                  ^    |         |            at all in this scenario.
                  '----'         '---> HASH

II)  DATA -----------.-----------.
                     v           v
      .init() -> .update() -> .finup()      ! .update() may not be called
                  ^    |         |            at all in this scenario.
                  '----'         '---> HASH

III) DATA -----------.
                     v
                 .digest()                  ! The entire process is handled
                     |                        by the .digest() call.
                     '---------------> HASH

以下是 .export()/.import() 函式在核心其他部分被使用時的示意圖。

KEY--.                 DATA--.
     v                       v                  ! .update() may not be called
 .setkey() -> .init() -> .update() -> .export()   at all in this scenario.
                          ^     |         |
                          '-----'         '--> PARTIAL_HASH

----------- other transformations happen here -----------

PARTIAL_HASH--.   DATA1--.
              v          v
          .import -> .update() -> .final()     ! .update() may not be called
                      ^    |         |           at all in this scenario.
                      '----'         '--> HASH1

PARTIAL_HASH--.   DATA2-.
              v         v
          .import -> .finup()
                        |
                        '---------------> HASH2

請注意,“放棄”請求物件是完全合法的:- 呼叫 .init(),然後(多次)呼叫 .update() - 將來_不_再呼叫 .final()、.finup() 或 .export() 中的任何一個

換句話說,實現者應注意資源分配和清理。呼叫 .init() 或 .update() 後,不應有與請求物件相關的資源保持分配狀態,因為可能沒有機會釋放它們。

非同步 HASH 變換的細節

某些驅動程式會希望使用通用 ScatterWalk,以防實現需要分別處理包含輸入資料的 scatterlist 塊。