1

由于我将使用 Linux 的内置加密 API 用于不同的目的,因此我一直在仔细阅读源代码。
在试图了解发生了什么时,我对代码的实例处理部分感到震惊。

代码是 C,不过这部分显然是面向对象的。因此,当用户需要执行某种加密时,它会要求分配转换实例,这将涉及在特定加密模式下使用特定算法。这对(算法模式)将在加密 API 中以这种方式处理:

  1. “纯”算法存储在一个struct crypto_alg结构中。
  2. 模式是通过 a 定义的struct crypto_template,它说明了如何初始化特定的struct crypto_instance

以下是结构的定义crypto_instancecrypto_spawn

struct crypto_instance {
    struct crypto_alg alg;
    /*
     * The alg struct will be filled according to the template 'alloc' method
     */
    struct crypto_template *tmpl;
    struct hlist_node list;

    void *__ctx[] CRYPTO_MINALIGN_ATTR;
};

struct crypto_spawn {
    struct list_head list; // Embedded list_head to list the spawns
    struct crypto_alg *alg; // Ptr to the underlying algorithm
    struct crypto_instance *inst;
    const struct crypto_type *frontend;
    u32 mask;
}

然后,对我来说,它看起来像是一种层次结构:在crypto_spawn这里管理由 a 定义的加密模式crypto_instance和由 a 定义的加密算法的组合crypto_alg。在模型对象方面,我们可以看到crypto_instance继承crypto_alg,并且在给定算法上考虑“级联”模板是完全合理的。

我不明白的是为什么crypto_spawncrypto_instance结构没有合并在一起。当我遇到实例分配代码时,这个问题让我更加震惊:

struct crypto_instance *crypto_alloc_instance(const char *name,
                                struct crypto_alg *alg)
{
    struct crypto_instance *inst;
    struct crypto_spawn *spawn;
    int err;

    inst = crypto_alloc_instance2(name, alg, 0);
    if (IS_ERR(inst))
        goto out;

    spawn = crypto_instance_ctx(inst);
    /*
     * This line expands to :
     * spawn = inst->__ctx;
     */
    err = crypto_init_spawn(spawn, alg, inst,
                CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC);

    if (err)
        goto err_free_inst;

    return inst;

err_free_inst:
    kfree(inst);
    inst = ERR_PTR(err);

out:
    return inst;
}

crypto_alloc_instance2只是分配物理内存来保存结构并填充结构的inst->alg名称字段:

void *crypto_alloc_instance2(const char *name, struct crypto_alg *alg,
                        unsigned int head)
{
    struct crypto_instance *inst;
    char *p;
    int err;

    p = kzalloc(head + sizeof(*inst) + sizeof(struct crypto_spawn),
                GFP_KERNEL);
    if (!p)
        return ERR_PTR(-ENOMEM);

    inst = (void *)(p + head);
    /* Names editing + error checking ... */
    return p;
}

如您所见,spawn 是“物理上”绑定到实例的,那么为什么要单独定义它们。当我试图了解整个事情是如何混合和处理时,我发现它非常令人不安。

目前,我想到的唯一原因是它允许 API使对象的底层算法变得不透明crypto_instance。但是由于结构的最后一个字节很容易通过spawn = inst->__ctx指令给出 spawn,所以它不是很不透明。

回想一下这堆代码和注释,问题是: 是什么原因导致开发人员在实例和生成结构之间进行这种“分离”?

提前感谢您的任何启发!

注意:我添加了标签加密,因为我认为对此标签感兴趣的开发人员可能已经查看了 Linux 内核加密部分。

4

0 回答 0