開發密碼演算法¶
註冊與登出變換¶
加密 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 塊。