使用者空間介面¶
介紹¶
核心加密 API 在核心空間可見的概念也完全適用於使用者空間介面。因此,關於核心內用例的核心加密 API 高層討論也適用於此處。
然而,主要區別在於使用者空間只能作為變換或密碼演算法的消費者,而不能作為提供者。
以下內容涵蓋了核心加密 API 匯出的使用者空間介面。此描述的一個工作示例是 libkcapi,可從 [1] 獲取。該庫可供需要核心加密服務的使用者空間應用程式使用。
然而,核心內加密 API 的某些方面不適用於使用者空間。這包括同步呼叫和非同步呼叫之間的區別。使用者空間 API 呼叫是完全同步的。
使用者空間 API 概述¶
核心加密 API 可從使用者空間訪問。目前,以下密碼演算法可訪問:
訊息摘要,包括帶金鑰訊息摘要 (HMAC, CMAC)
對稱密碼
AEAD 密碼
隨機數生成器
介面透過 AF_ALG 型別的套接字提供。此外,setsockopt 選項型別為 SOL_ALG。如果使用者空間標頭檔案尚未匯出這些標誌,請使用以下宏:
#ifndef AF_ALG
#define AF_ALG 38
#endif
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
密碼演算法的訪問名稱與核心內 API 呼叫相同。這包括密碼演算法的通用與唯一命名方案,以及對通用名稱的優先順序強制執行。
要與核心加密 API 互動,使用者空間應用程式必須建立一個套接字。使用者空間使用 send()/write() 系統呼叫系列呼叫密碼操作。密碼操作的結果透過 read()/recv() 系統呼叫系列獲取。
以下 API 呼叫假設套接字描述符已由使用者空間應用程式開啟,並且只討論核心加密 API 特定的呼叫。
要初始化套接字介面,消費者必須執行以下序列:
建立一個 AF_ALG 型別的套接字,並使用下面為不同密碼型別指定的 `struct sockaddr_alg` 引數。
使用套接字描述符呼叫 bind
使用套接字描述符呼叫 accept。accept 系統呼叫返回一個新的檔案描述符,該描述符將用於與特定密碼例項互動。當呼叫 send/write 或 recv/read 系統呼叫向核心傳送資料或從核心獲取資料時,必須使用 accept 返回的檔案描述符。
就地密碼操作¶
就像核心加密 API 的核心內操作一樣,使用者空間介面允許就地進行密碼操作。這意味著用於 send/write 系統呼叫的輸入緩衝區和用於 read/recv 系統呼叫的輸出緩衝區可以是同一個。這對於對稱密碼操作尤其重要,因為可以避免將輸出資料複製到最終目的地。
另一方面,如果消費者希望將明文和密文儲存在不同的記憶體位置,消費者只需為加密和解密操作提供不同的記憶體指標即可。
訊息摘要 API¶
用於密碼操作的訊息摘要型別在呼叫 bind 系統呼叫時選擇。bind 要求呼叫者提供一個已填充的 `struct sockaddr` 資料結構。該資料結構必須按如下方式填充:
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "hash", /* this selects the hash logic in the kernel */
.salg_name = "sha1" /* this is the cipher name */
};
`salg_type` 值“hash”適用於訊息摘要和帶金鑰訊息摘要。不過,帶金鑰訊息摘要由相應的 `salg_name` 引用。請參閱下面的 setsockopt 介面,其中解釋瞭如何為帶金鑰訊息摘要設定金鑰。
使用 send() 系統呼叫,應用程式提供應使用訊息摘要處理的資料。send 系統呼叫允許指定以下標誌:
MSG_MORE:如果設定此標誌,send 系統呼叫將像訊息摘要更新函式一樣執行,此時最終雜湊尚未計算。如果未設定此標誌,send 系統呼叫將立即計算最終訊息摘要。
使用 recv() 系統呼叫,應用程式可以從核心加密 API 讀取訊息摘要。如果緩衝區對於訊息摘要來說太小,核心會設定 MSG_TRUNC 標誌。
為了設定訊息摘要金鑰,呼叫應用程式必須使用 setsockopt() 的 ALG_SET_KEY 或 ALG_SET_KEY_BY_KEY_SERIAL 選項。如果未設定金鑰,則 HMAC 操作將在沒有金鑰導致的初始 HMAC 狀態更改的情況下執行。
對稱密碼 API¶
操作與訊息摘要的討論非常相似。在初始化期間,`struct sockaddr` 資料結構必須按如下方式填充:
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher", /* this selects the symmetric cipher */
.salg_name = "cbc(aes)" /* this is the cipher name */
};
在可以使用 write/send 系統呼叫系列向核心傳送資料之前,消費者必須設定金鑰。金鑰設定在下面的 setsockopt 呼叫中描述。
使用 sendmsg() 系統呼叫,應用程式提供應進行加密或解密處理的資料。此外,IV 透過 sendmsg() 系統呼叫提供的資料結構指定。
`sendmsg` 系統呼叫的 `struct msghdr` 引數嵌入到 `struct cmsghdr` 資料結構中。有關 `cmsghdr` 資料結構如何與 send/recv 系統呼叫系列一起使用的更多資訊,請參閱 recv(2) 和 cmsg(3)。該 `cmsghdr` 資料結構包含以下由單獨頭例項指定的資訊:
使用以下標誌之一指定密碼操作型別:
ALG_OP_ENCRYPT - 資料加密
ALG_OP_DECRYPT - 資料解密
指定帶有 ALG_SET_IV 標誌的 IV 資訊
send 系統呼叫系列允許指定以下標誌:
MSG_MORE:如果設定此標誌,send 系統呼叫將像密碼更新函式一樣執行,後續的 send 系統呼叫預計會有更多輸入資料。
注意:對於任何意外資料,核心會報告 -EINVAL。呼叫者必須確保所有資料都與 /proc/crypto 中為所選密碼指定的約束匹配。
使用 recv() 系統呼叫,應用程式可以從核心加密 API 讀取密碼操作的結果。輸出緩衝區的大小必須至少足以容納所有加密或解密資料塊。如果輸出資料大小較小,則只會返回能放入該輸出緩衝區大小的資料塊。
AEAD 密碼 API¶
操作與對稱密碼的討論非常相似。在初始化期間,`struct sockaddr` 資料結構必須按如下方式填充:
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead", /* this selects the symmetric cipher */
.salg_name = "gcm(aes)" /* this is the cipher name */
};
在可以使用 write/send 系統呼叫系列向核心傳送資料之前,消費者必須設定金鑰。金鑰設定在下面的 setsockopt 呼叫中描述。
此外,在可以使用 write/send 系統呼叫系列向核心傳送資料之前,消費者必須設定認證標籤大小。要設定認證標籤大小,呼叫者必須使用下面描述的 setsockopt 呼叫。
使用 sendmsg() 系統呼叫,應用程式提供應進行加密或解密處理的資料。此外,IV 透過 sendmsg() 系統呼叫提供的資料結構指定。
`sendmsg` 系統呼叫的 `struct msghdr` 引數嵌入到 `struct cmsghdr` 資料結構中。有關 `cmsghdr` 資料結構如何與 send/recv 系統呼叫系列一起使用的更多資訊,請參閱 recv(2) 和 cmsg(3)。該 `cmsghdr` 資料結構包含以下由單獨頭例項指定的資訊:
使用以下標誌之一指定密碼操作型別:
ALG_OP_ENCRYPT - 資料加密
ALG_OP_DECRYPT - 資料解密
指定帶有 ALG_SET_IV 標誌的 IV 資訊
使用 ALG_SET_AEAD_ASSOCLEN 標誌指定相關認證資料 (AAD)。AAD 與明文/密文一起傳送到核心。記憶體結構請參見下文。
send 系統呼叫系列允許指定以下標誌:
MSG_MORE:如果設定此標誌,send 系統呼叫將像密碼更新函式一樣執行,後續的 send 系統呼叫預計會有更多輸入資料。
注意:對於任何意外資料,核心會報告 -EINVAL。呼叫者必須確保所有資料都與 /proc/crypto 中為所選密碼指定的約束匹配。
使用 recv() 系統呼叫,應用程式可以從核心加密 API 讀取密碼操作的結果。輸出緩衝區的大小必須至少與下面記憶體結構中定義的大小相同。如果輸出資料大小較小,則不執行密碼操作。
認證解密操作可能指示完整性錯誤。這種完整性破壞以 -EBADMSG 錯誤程式碼標記。
AEAD 記憶體結構¶
AEAD 密碼透過以下資訊執行,這些資訊在使用者和核心空間之間作為單個數據流進行通訊:
明文或密文
相關認證資料 (AAD)
認證標籤
AAD 和認證標籤的大小透過 sendmsg 和 setsockopt 呼叫提供(請參見那裡)。由於核心知道整個資料流的大小,因此現在能夠計算資料流中資料元件的正確偏移量。
使用者空間呼叫者必須按以下順序安排上述資訊:
AEAD 加密輸入:AAD || 明文
AEAD 解密輸入:AAD || 密文 || 認證標籤
使用者空間呼叫者提供的輸出緩衝區必須至少足夠大以容納以下資料:
AEAD 加密輸出:密文 || 認證標籤
AEAD 解密輸出:明文
隨機數生成器 API¶
同樣,操作與其他 API 非常相似。在初始化期間,`struct sockaddr` 資料結構必須按如下方式填充:
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "rng", /* this selects the random number generator */
.salg_name = "drbg_nopr_sha256" /* this is the RNG name */
};
根據 RNG 型別,RNG 必須進行播種。種子透過 setsockopt 介面設定金鑰來提供。例如,ansi_cprng 需要種子。DRBG 不需要種子,但可以播種。在 NIST SP 800-90A 標準中,種子也稱為“個性化字串”(Personalization String)。
使用 read()/recvmsg() 系統呼叫,可以獲取隨機數。核心在一次呼叫中最多生成 128 位元組。如果使用者空間需要更多資料,則必須進行多次 read()/recvmsg() 呼叫。
警告:使用者空間呼叫者可以多次呼叫最初提及的 accept 系統呼叫。在這種情況下,返回的檔案描述符具有相同的狀態。
當核心使用 CRYPTO_USER_API_RNG_CAVP 選項構建時,以下 CAVP 測試介面將被啟用:
熵 (Entropy) 和 隨機數 (Nonce) 的串聯可以透過 ALG_SET_DRBG_ENTROPY setsockopt 介面提供給 RNG。設定熵需要 CAP_SYS_ADMIN 許可權。
附加資料 (Additional Data) 可以使用 send()/sendmsg() 系統呼叫提供,但只能在熵設定之後。
零複製介面¶
除了 send/write/read/recv 系統呼叫系列之外,AF_ALG 介面還可以透過 splice/vmsplice 的零複製介面訪問。顧名思義,核心嘗試避免將資料複製到核心空間。
零複製操作要求資料在頁邊界對齊。也可以使用非對齊資料,但這可能需要核心進行更多操作,從而抵消從零複製介面獲得的速度增益。
單個零複製操作的系統固有大小限制為 16 頁。如果需要向 AF_ALG 傳送更多資料,使用者空間必須將輸入資料分割成最大為 16 頁的段。
零複製可以與以下程式碼示例一起使用(libkcapi 提供了完整的可工作示例):
int pipes[2];
pipe(pipes);
/* input data in iov */
vmsplice(pipes[1], iov, iovlen, SPLICE_F_GIFT);
/* opfd is the file descriptor returned from accept() system call */
splice(pipes[0], NULL, opfd, NULL, ret, 0);
read(opfd, out, outlen);
Setsockopt 介面¶
除了 read/recv 和 send/write 系統呼叫處理以傳送和檢索受密碼操作影響的資料之外,消費者還需要設定密碼操作的附加資訊。此附加資訊透過 setsockopt 系統呼叫設定,該呼叫必須使用開啟密碼的檔案描述符(即由 accept 系統呼叫返回的檔案描述符)進行呼叫。
每次 setsockopt 呼叫都必須使用 SOL_ALG 級別。
setsockopt 介面允許使用上述 `optname` 設定以下資料:
ALG_SET_KEY -- 設定金鑰。金鑰設定適用於:
skcipher 密碼型別(對稱密碼)
hash 密碼型別(帶金鑰訊息摘要)
AEAD 密碼型別
RNG 密碼型別以提供種子
- ALG_SET_KEY_BY_KEY_SERIAL -- 透過金鑰環 key_serial_t 設定金鑰。
此操作與 ALG_SET_KEY 行為相同。解密資料從金鑰環金鑰中複製,並使用該資料作為對稱加密的金鑰。
傳入的 key_serial_t 必須設定 KEY_(POS|USR|GRP|OTH)_SEARCH 許可權,否則返回 -EPERM。支援的金鑰型別有:user、logon、encrypted 和 trusted。
ALG_SET_AEAD_AUTHSIZE -- 為 AEAD 密碼設定認證標籤大小。對於加密操作,將生成給定大小的認證標籤。對於解密操作,假定提供的密文包含給定大小的認證標籤(參見下面關於 AEAD 記憶體佈局的部分)。
ALG_SET_DRBG_ENTROPY -- 設定隨機數生成器的熵。此選項僅適用於 RNG 密碼型別。
使用者空間 API 示例¶
請參閱 [1] 獲取 libkcapi,它提供了對上述 Netlink 核心介面的易用封裝。[1] 還包含一個呼叫所有 libkcapi API 的測試應用程式。