非對稱/公鑰密碼金鑰型別

概述

“非對稱”金鑰型別旨在成為公鑰密碼術中使用的金鑰的容器,而不對密碼術的形式或機制或金鑰的形式施加任何特定限制。

非對稱金鑰被賦予一個子型別,該子型別定義了什麼型別的資料與該金鑰關聯,並提供了描述和銷燬該資料的操作。但是,並不要求金鑰資料實際儲存在金鑰中。

可以定義一個完全在核心內的金鑰保留和操作子型別,但也可以提供對密碼硬體(例如 TPM)的訪問,該硬體可用於保留相關金鑰並使用該金鑰執行操作。在這種情況下,非對稱金鑰將僅僅是 TPM 驅動程式的介面。

還提供了資料解析器的概念。資料解析器負責從傳遞給例項化函式的資料 blob 中提取資訊。第一個識別 blob 的資料解析器可以設定金鑰的子型別並定義可以對該金鑰執行的操作。

資料解析器可以將資料 blob 解釋為包含表示金鑰的位,或者將其解釋為對系統中其他位置持有的金鑰的引用(例如,TPM)。

金鑰識別

如果新增的金鑰具有空名稱,則例項化資料解析器有機會預解析金鑰並從金鑰內容中確定應給出的金鑰描述。

然後,可以使用完整匹配或部分匹配來引用金鑰。金鑰型別也可以使用其他標準來引用金鑰。

非對稱金鑰型別的匹配函式可以執行比直接比較描述與標準字串更廣泛的比較。

  1. 如果標準字串的形式為“id:<hexdigits>”,則匹配函式將檢查金鑰的指紋,以檢視“id:”之後給出的十六進位制數字是否與尾部匹配。例如

    keyctl search @s asymmetric id:5acc2142
    

    將匹配具有指紋的金鑰

    1A00 2040 7601 7889 DE11  882C 3823 04AD 5ACC 2142
    
  2. 如果標準字串的形式為“<subtype>:<hexdigits>”,則匹配將匹配 ID,如 (1) 中所示,但添加了僅匹配指定子型別(例如 tpm)的金鑰的限制。例如

    keyctl search @s asymmetric tpm:5acc2142
    

在 /proc/keys 中查詢,金鑰指紋的最後 8 個十六進位制數字與子型別一起顯示

1a39e171 I-----     1 perm 3f010000     0     0 asymmetric modsign.0: DSA 5acc2142 []

訪問非對稱金鑰

對於從核心中對非對稱金鑰的常規訪問,需要包含以下內容

#include <crypto/public_key.h>

這提供了用於處理非對稱/公鑰的函式。那裡定義了三個列舉,用於表示公鑰密碼演算法

enum pkey_algo

這些演算法使用的摘要演算法

enum pkey_hash_algo

和金鑰識別符號表示

enum pkey_id_type

請注意,需要金鑰型別表示型別,因為來自不同標準的金鑰識別符號不一定相容。例如,PGP 透過雜湊金鑰資料加上一些 PGP 特定的元資料來生成金鑰識別符號,而 X.509 具有任意證書識別符號。

金鑰上定義的操作是

  1. 簽名驗證。

其他操作(例如加密)可以使用與驗證所需的相同金鑰資料,但目前不支援,其他操作(例如解密和簽名生成)需要額外的金鑰資料。

簽名驗證

提供了一個操作來執行密碼簽名驗證,使用非對稱金鑰來提供或訪問公鑰

int verify_signature(const struct key *key,
                     const struct public_key_signature *sig);

呼叫者必須已經從某個來源獲得了金鑰,然後可以使用它來檢查簽名。呼叫者必須已解析簽名並將相關位傳輸到 sig 指向的結構。

struct public_key_signature {
        u8 *digest;
        u8 digest_size;
        enum pkey_hash_algo pkey_hash_algo : 8;
        u8 nr_mpi;
        union {
                MPI mpi[2];
                ...
        };
};

使用的演算法必須在 sig->pkey_hash_algo 中註明,並且構成實際簽名的所有 MPI 必須儲存在 sig->mpi[] 中,並且 MPI 的計數必須放置在 sig->nr_mpi 中。

此外,資料必須已被呼叫者摘要,並且生成的雜湊必須由 sig->digest 指向,並且雜湊的大小必須放置在 sig->digest_size 中。

如果簽名匹配,該函式將返回 0,否則返回 -EKEYREJECTED。

如果指定了不受支援的公鑰演算法或公鑰/雜湊演算法組合,或者金鑰不支援該操作,則該函式也可能返回 -ENOTSUPP;如果某些引數具有奇怪的資料,則返回 -EBADMSG 或 -ERANGE;或者如果無法執行分配,則返回 -ENOMEM。如果 key 引數型別錯誤或未完全設定,則可以返回 -EINVAL。

非對稱金鑰子型別

非對稱金鑰具有一個子型別,該子型別定義了可以對該金鑰執行的一組操作,並確定附加為金鑰有效負載的資料。有效負載格式完全由子型別決定。

子型別由金鑰資料解析器選擇,解析器必須初始化所需的資料。非對稱金鑰保留對子型別模組的引用。

子型別定義結構可以在中找到

#include <keys/asymmetric-subtype.h>

看起來像下面這樣

struct asymmetric_key_subtype {
        struct module           *owner;
        const char              *name;

        void (*describe)(const struct key *key, struct seq_file *m);
        void (*destroy)(void *payload);
        int (*query)(const struct kernel_pkey_params *params,
                     struct kernel_pkey_query *info);
        int (*eds_op)(struct kernel_pkey_params *params,
                      const void *in, void *out);
        int (*verify_signature)(const struct key *key,
                                const struct public_key_signature *sig);
};

非對稱金鑰使用其 payload[asym_subtype] 成員指向此結構。

所有者和名稱欄位應設定為擁有的模組和子型別的名稱。目前,名稱僅用於列印語句。

子型別定義了許多操作

  1. describe()。

    必需。這允許子型別在 /proc/keys 中針對金鑰顯示一些內容。例如,可以顯示公鑰演算法型別的名稱。金鑰型別將在此之後顯示金鑰標識字串的尾部。

  2. destroy()。

    必需。這應該釋放與金鑰關聯的記憶體。非對稱金鑰將負責釋放指紋並釋放對子型別模組的引用。

  3. query()。

    必需。這是一個用於查詢金鑰功能的函式。

  4. eds_op()。

    可選。這是加密、解密和簽名建立操作的入口點(這些操作透過引數結構中的操作 ID 來區分)。子型別可以執行任何它喜歡的操作來實現操作,包括解除安裝到硬體。

  5. verify_signature()。

    可選。這是簽名驗證的入口點。子型別可以執行任何它喜歡的操作來實現操作,包括解除安裝到硬體。

例項化資料解析器

非對稱金鑰型別通常不想儲存或處理儲存金鑰資料的原始資料 blob。每次想要使用它時,它都必須解析它並進行錯誤檢查。此外,blob 的內容可能具有可以對其執行的各種檢查(例如,自簽名、有效期),並且可能包含有關金鑰的有用資料(識別符號、功能)。

此外,blob 可能表示指向包含金鑰的某些硬體的指標,而不是金鑰本身。

可以實現解析器的 blob 格式示例包括

  • OpenPGP 資料包流 [RFC 4880]。

  • X.509 ASN.1 流。

  • 指向 TPM 金鑰的指標。

  • 指向 UEFI 金鑰的指標。

  • PKCS#8 私鑰 [RFC 5208]。

  • PKCS#5 加密私鑰 [RFC 2898]。

在金鑰例項化期間,將嘗試列表中的每個解析器,直到有一個不返回 -EBADMSG。

解析器定義結構可以在中找到

#include <keys/asymmetric-parser.h>

看起來像下面這樣

struct asymmetric_key_parser {
        struct module   *owner;
        const char      *name;

        int (*parse)(struct key_preparsed_payload *prep);
};

所有者和名稱欄位應設定為擁有的模組和解析器的名稱。

目前解析器只定義了一個操作,它是必需的

  1. parse()。

    呼叫此函式以從金鑰建立和更新路徑中預解析金鑰。特別是,它在分配金鑰_之前_在金鑰建立期間被呼叫,因此,如果呼叫者拒絕這樣做,則允許提供金鑰的描述。

    呼叫者傳遞一個指向以下結構的指標,其中除資料、datalen 和 quotalen 之外的所有欄位都已清除 [請參閱核心金鑰保留服務]

    struct key_preparsed_payload {
            char            *description;
            void            *payload[4];
            const void      *data;
            size_t          datalen;
            size_t          quotalen;
    };
    

    例項化資料位於資料指向的 blob 中,大小為 datalen。不允許 parse() 函式完全更改這兩個值,並且不應更改任何其他值_除非_它們識別 blob 格式,並且不會返回 -EBADMSG 來指示它不是它們的。

    如果解析器對 blob 感到滿意,它應該為金鑰提出一個描述並將其附加到 ->description,->payload[asym_subtype] 應設定為指向要使用的子型別,->payload[asym_crypto] 應設定為指向該子型別的已初始化資料,->payload[asym_key_ids] 應指向一個或多個十六進位制指紋,並且應更新 quotalen 以指示此金鑰應占用多少配額。

    清除時,附加到 ->payload[asym_key_ids] 和 ->description 的資料將被kfree(),並且附加到 ->payload[asm_crypto] 的資料將被傳遞給子型別的 ->destroy() 方法進行處理。將放置 ->payload[asym_subtype] 指向的子型別的模組引用。

    如果無法識別資料格式,則應返回 -EBADMSG。如果已識別,但由於某種原因無法設定金鑰,則應返回其他一些負錯誤程式碼。成功時,應返回 0。

    金鑰的指紋字串可能會被部分匹配。對於諸如 RSA 和 DSA 之類的公鑰演算法,這很可能是金鑰指紋的可列印十六進位制版本。

提供了註冊和登出解析器的函式

int register_asymmetric_key_parser(struct asymmetric_key_parser *parser);
void unregister_asymmetric_key_parser(struct asymmetric_key_parser *subtype);

解析器可能沒有相同的名稱。否則,這些名稱僅用於在除錯訊息中顯示。