Kerberos V 加密 API

概述

此 API 提供 Kerberos 5 风格的加密功能,用于密钥派生、加密和校验,可用于网络文件系统,也可用于实现 GSSAPI 所需的低级加密。

支持以下加密类型

KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC
KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC

KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128
KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256
KRB5_CKSUMTYPE_CMAC_CAMELLIA128
KRB5_CKSUMTYPE_CMAC_CAMELLIA256
KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128
KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256

API 可通过以下方式包含

#include <crypto/krb5.h>

小型缓冲区

为了传递小块数据(如密钥),定义了一个缓冲区结构,提供数据指针和数据大小。

struct krb5_buffer {
        unsigned int    len;
        void            *data;
};

编码类型

编码类型由以下结构定义

struct krb5_enctype {
        int             etype;
        int             ctype;
        const char      *name;
        u16             key_bytes;
        u16             key_len;
        u16             Kc_len;
        u16             Ke_len;
        u16             Ki_len;
        u16             prf_len;
        u16             block_len;
        u16             conf_len;
        u16             cksum_len;
        ...
};

对 API 用户感兴趣的字段如下

  • etypectype 分别表示此编码类型用于加密和校验的协议号。它们包含 KRB5_ENCTYPE_*KRB5_CKSUMTYPE_* 常量。

  • name 是编码的正式名称。

  • key_lenkey_bytes 是输入密钥长度和派生密钥长度。(我认为它们只对 DES 不同,这里不支持 DES)。

  • Kc_lenKe_lenKi_len 是派生 Kc、Ke 和 Ki 密钥的大小。Kc 用于校验模式;Ke 和 Ki 用于加密模式。

  • prf_len 是 PRF+ 函数计算结果的大小。

  • block_lenconf_lencksum_len 分别是加密块长度、混淆器长度和校验和长度。这三者都用于加密模式,但只有校验和长度用于校验模式。

编码类型通过以下函数按编号查找

const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);

密钥派生

一旦应用程序选择了加密类型,用于实际加密的密钥就可以从传输密钥派生出来。

PRF+ 计算

为帮助密钥派生,提供了一个计算 Kerberos GSSAPI 机制的 PRF+ 函数

int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
                             const struct krb5_buffer *K,
                             unsigned int L,
                             const struct krb5_buffer *S,
                             struct krb5_buffer *result,
                             gfp_t gfp);

这可用于从源密钥加上额外数据来派生传输密钥,以限制其使用。

加密函数

一旦密钥派生完成,就可以对数据执行加密操作。调用者在准备要传输的消息时,必须在缓冲区中为混淆器(如果需要)和校验和的存储留出间隙。为此提供了一个枚举和一对函数

enum krb5_crypto_mode {
        KRB5_CHECKSUM_MODE,
        KRB5_ENCRYPT_MODE,
};

size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
                                   enum krb5_crypto_mode mode,
                                   size_t data_size, size_t *_offset);

size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
                                 enum krb5_crypto_mode mode,
                                 size_t *_buffer_size, size_t *_offset);

所有这些函数都接受编码类型和加密模式指示(仅校验和或完全加密)。

第一个函数返回容纳给定数据量所需的缓冲区大小;第二个函数返回特定大小的缓冲区可容纳多少数据,并相应地调整所需缓冲区的大小。在这两种情况下,数据在缓冲区内的偏移量也会返回。

当收到消息时,可以通过调用以下函数确定消息中数据的位置和大小

void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
                                   enum krb5_crypto_mode mode,
                                   size_t *_offset, size_t *_len);

调用者向函数提供消息的偏移量和长度,然后函数会更改这些值以指示包含数据的区域(加上任何填充)。由调用者确定有多少填充。

准备函数

提供了两个函数来分配和准备加密对象,以便供动作函数使用

struct crypto_aead *
crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
                               const struct krb5_buffer *TK,
                               u32 usage, gfp_t gfp);
struct crypto_shash *
crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
                             const struct krb5_buffer *TK,
                             u32 usage, gfp_t gfp);

这两个函数都接受编码类型、传输密钥以及用于派生适当子密钥的使用值。它们创建一个适当的加密对象,一个用于加密的 AEAD 模板和一个用于校验和的同步哈希,并在其上设置密钥并进行配置。调用者应将这些句柄传递给下面的动作函数。

加密模式

提供一对函数来加密和解密消息

ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
                            struct crypto_aead *aead,
                            struct scatterlist *sg, unsigned int nr_sg,
                            size_t sg_len,
                            size_t data_offset, size_t data_len,
                            bool preconfounded);
int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
                        struct crypto_aead *aead,
                        struct scatterlist *sg, unsigned int nr_sg,
                        size_t *_offset, size_t *_len);

在这两种情况下,输入和输出缓冲区都由相同的散列列表指示。

对于加密函数,输出缓冲区可能大于所需(返回生成的输出量),并且指示了数据的位置和大小(必须与编码匹配)。如果未设置混淆器,函数将插入一个。

对于解密函数,提供消息在缓冲区中的偏移量和长度,并将其缩小以适应数据。解密函数将验证消息中的任何校验和,如果不匹配则返回错误。

校验和模式

提供一对函数来生成消息的校验和并验证该校验和

ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
                            struct crypto_shash *shash,
                            const struct krb5_buffer *metadata,
                            struct scatterlist *sg, unsigned int nr_sg,
                            size_t sg_len,
                            size_t data_offset, size_t data_len);
int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
                           struct crypto_shash *shash,
                           const struct krb5_buffer *metadata,
                           struct scatterlist *sg, unsigned int nr_sg,
                           size_t *_offset, size_t *_len);

在这两种情况下,输入和输出缓冲区都由相同的散列列表指示。可以传入额外的元数据,这些元数据将在数据之前添加到哈希中。

对于 get_mic 函数,输出缓冲区可能大于所需(返回生成的输出量),并且指示了数据的位置和大小(必须与编码匹配)。

对于验证函数,提供消息在缓冲区中的偏移量和长度,并将其缩小以适应数据。如果校验和不匹配,将返回错误。

krb5enc AEAD 算法

提供了一个名为“krb5enc”的模板 AEAD 加密算法,它在加密明文之前对其进行哈希处理(与 authenc 相反)。crypto_krb5_prepare_encryption() 返回的句柄可能是其中之一,但此 API 的用户没有直接与其交互的要求。

作为参考,其密钥格式以格式号的 BE32 开始。只提供了格式 1,其后是 Ke 密钥长度的 BE32,然后是 Ki 密钥长度的 BE32,接着是 Ke 密钥的字节,然后是 Ki 密钥。

使用特定顺序的单词意味着静态测试数据不需要字节序转换。