linux加密框架 crypto 算法管理 - 动态和静态算法管理

参考链接
Linux加密框架的算法管理(三)_家有一希的博客-CSDN博客
动态和静态算法管理
静态算法
加密框架中的算法分为静态算法和动态算法两种,其中静态算法指的是以"算法名.ko"形式存在的静态编译的算法模块,如aes.ko表示AES算法模块,md5.ko表示MD5算法模块
静态算法模块是预定义的,在内核启动时或通过request_module函数加载到加密框架中的
在加密框架中,静态算法表示为一个算法说明实例。
动态算法
而动态算法指的是根据算法模式(如CBC、HMAC等)和基础算法(静态算法或动态算法)创建的算法,如"cbc(aes)"表示使用AES算法的CBC模式的新算法,"hmac(md5)"表示使用MD5算法的HMAC模式的新算法,这些算法是根据外部应用需求动态创建并注册到加密框架中
在加密框架中,动态算法表示为一个算法模板实例。
差异
静态算法在密码学上属于算法的范畴。
动态算法在密码学上属于算法应用的范畴。
创建动态算法
发布创建动态算法通知
如果外部应用在查找算法(如"cbc(aes)"算法)时,如果查找未命中,将创建注册用算法幼虫,然后在加密通知链上发布创建动态算法(CRYPTO_MSG_ALG_REQUEST)的通知
如下所示
    ok = crypto_probing_notify(CRYPTO_MSG_ALG_REQUEST, larval);
如果是当前查找线程发布的创建动态算法通知,则larval为待创建算法同名的注册用算法幼虫,用于向创建动态算法的内核线程传递待创建算法的算法名、算法类型等信息,此时算法管理链表如下所示,其中cbc_aes_larval_r表示"cbc(aes)"算法对应的注册用算法幼虫。


加密通知链回调函数cryptomgr_notify根据通知消息类型msg调用不同的执行函数
algboss.c - crypto/algboss.c - Linux source code (v5.15.12) - Bootlin
如下所示


static int cryptomgr_notify(struct notifier_block *this, unsigned long msg,
                void *data)
{
    switch (msg) {
    case CRYPTO_MSG_ALG_REQUEST:
        return cryptomgr_schedule_probe(data);
    case CRYPTO_MSG_ALG_REGISTER:
        return cryptomgr_schedule_test(data);
    case CRYPTO_MSG_ALG_LOADED:
        break;
    }
 
    return NOTIFY_DONE;
}
 其中CRYPTO_MSG_ALG_REQUEST为创建动态算法(也称为算法探测)的通知,执行函数为cryptomgr_schedule_probe;
CRYPTO_MSG_ALG_REGISTER为算法正确性检验的通知,执行函数为cryptomgr_schedule_test。
cryptomgr_schedule_probe函数
cryptomgr_schedule_probe函数的输入参数为算法幼虫larval,返回值为执行结果,NOTIFY_STOP表示执行完毕,但是不表示已成功创建动态算法。
algboss.c - crypto/algboss.c - Linux source code (v5.15.12) - Bootlin
static int cryptomgr_schedule_probe(struct crypto_larval *larval)
{
    struct task_struct *thread;
    struct cryptomgr_param *param;
    const char *name = larval->alg.cra_name;
    const char *p;
    unsigned int len;
    int i;
 
    if (!try_module_get(THIS_MODULE))
        goto err;
 
    param = kzalloc(sizeof(*param), GFP_KERNEL);
    if (!param)
        goto err_put_module;
 
    for (p = name; isalnum(*p) || *p == '-' || *p == '_'; p++)
        ;
 
    len = p - name;
    if (!len || *p != '(')
        goto err_free_param;
 
    memcpy(param->template, name, len);
 
    i = 0;
    for (;;) {
        name = ++p;
 
        for (; isalnum(*p) || *p == '-' || *p == '_'; p++)
            ;
 
        if (*p == '(') {
            int recursion = 0;
 
            for (;;) {
                if (!*++p)
                    goto err_free_param;
                if (*p == '(')
                    recursion++;
                else if (*p == ')' && !recursion--)
                    break;
            }
 
            p++;
        }
 
        len = p - name;
        if (!len)
            goto err_free_param;
 
        param->attrs[i].attr.rta_len = sizeof(param->attrs[i]);
        param->attrs[i].attr.rta_type = CRYPTOA_ALG;
        memcpy(param->attrs[i].data.name, name, len);
 
        param->tb[i + 1] = ¶m->attrs[i].attr;
        i++;
 
        if (i >= CRYPTO_MAX_ATTRS)
            goto err_free_param;
 
        if (*p == ')')
            break;
 
        if (*p != ',')
            goto err_free_param;
    }
 
    if (!i)
        goto err_free_param;
 
    param->tb[i + 1] = NULL;
 
    param->type.attr.rta_len = sizeof(param->type);
    param->type.attr.rta_type = CRYPTOA_TYPE;
    param->type.data.type = larval->alg.cra_flags & ~CRYPTO_ALG_TESTED;
    param->type.data.mask = larval->mask & ~CRYPTO_ALG_TESTED;
    param->tb[0] = ¶m->type.attr;
 
    param->otype = larval->alg.cra_flags;
    param->omask = larval->mask;
 
    crypto_alg_get(&larval->alg);
    param->larval = larval;
 
    thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe");
    if (IS_ERR(thread))
        goto err_put_larval;
 
    return NOTIFY_STOP;
 
err_put_larval:
    crypto_alg_put(&larval->alg);
err_free_param:
    kfree(param);
err_put_module:
    module_put(THIS_MODULE);
err:
    return NOTIFY_OK;
}

cryptomgr_schedule_probe函数执行流程如下所示。


1)在cryptomgr_schedule_probe函数函数中,需要从算法幼虫的算法名中解析出算法模板名和基础算法名,规则如下:
正则表达式
a)基本规则:算法模板名(基础算法名1,…,基础算法名n);
b)算法模板名的有效字符包括0到9、a到z、A到Z、-、等;
c)基础算法名的有效字符包括0到9、a到z、A到Z、-、、(、)等;
d)所有基础算法名都必须包含在紧跟算法模板名后的()之内,当有多个基础算法时,基础算法名以",“间隔;
e)算法模板名与第一个”("之间不能有任何其他字符;
f)基础算法名最多不超过32个。
例子
例如,算法名为"hmac(md5)"时,解析出的算法模板为hmac,基础算法为静态算法md5;
算法名为"authenc(hmac(md5),cbc(aes))"时,解析出的算法模板为authenc,基础算法为动态算法hmac(md5)和cbc(aes)。
cryptomgr_param
2)传递给内核线程的参数数据结构为struct cryptomgr_param,定义如下所示:
algboss.c - crypto/algboss.c - Linux source code (v5.15.12) - Bootlin
struct cryptomgr_param {
    struct rtattr *tb[CRYPTO_MAX_ATTRS + 2];
 
    struct {
        struct rtattr attr;
        struct crypto_attr_type data;
    } type;
 
    struct {
        struct rtattr attr;
        struct crypto_attr_alg data;
    } attrs[CRYPTO_MAX_ATTRS];
 
    char template[CRYPTO_MAX_ALG_NAME];
 
    struct crypto_larval *larval;
 
    u32 otype;
    u32 omask;
};

参数介绍 
tb:参数名(T)列表,不含参数值,以NULL结尾。第1个为算法类型(CRYPTOA_TYPE),后续均为基础算法名(CRYPTOA_ALG)。
type:算法类型,TLV结构,包含算法类型和屏蔽位。
attrs:基础算法名列表,TLV结构。
larval:算法幼虫名,即待创建的动态算法的算法名。
template:算法模板名。
otype:原始的算法类型,传递给内核线程的算法类型中清除了算法已检测标志位。
omask:原始的算法类型屏蔽位,传递给内核线程的算法类型屏蔽位中清除了算法已检测标志位。
填充完参数param后,创建名为"cryptomgr_probe"的内核线程,称为算法探测线程,其执行函数为cryptomgr_probe,如下所示。
thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe");
    if (IS_ERR(thread))
        goto err_put_larval;
创建完算法探测线程后,算法查找线程调用crypto_larval_wait等待算法探测结束,如下所示。

    if (ok == NOTIFY_STOP)
        alg = crypto_larval_wait(larval);
算法检测线程(非算法探测线程)通过注册用算法幼虫的完成量通知查找线程算法探测结束
struct completion *completion;完成量,指向算法幼虫的完成量。
目前的版本  V5.15.12已经删除这个变量
cryptomgr_probe函数

cryptomgr_probe函数是算法探测线程的执行函数,输入参数为创建动态算法所需的参数
algboss.c - crypto/algboss.c - Linux source code (v5.15.12) - Bootlin
static int cryptomgr_probe(void *data)
{
    struct cryptomgr_param *param = data;
    struct crypto_template *tmpl;
    int err;
 
    tmpl = crypto_lookup_template(param->template);
    if (!tmpl)
        goto out;
 
    do {
        err = tmpl->create(tmpl, param->tb);
    } while (err == -EAGAIN && !signal_pending(current));
 
    crypto_tmpl_put(tmpl);
 
out:
    complete_all(¶m->larval->completion);
    crypto_alg_put(¶m->larval->alg);
    kfree(param);
    module_put_and_exit(0);
}

处理流程如下所示


算法模板要么实现create接口要么实现alloc接口,两者必居其一
其中create接口不仅创建算法模板实例,还实现算法模板实例注册,而alloc接口只创建算法模板实例,因此还需要调用者再进行算法模板实例注册。一般情况下,哈希算法的算法模板实现create接口,如HMAC模板提供的create接口为hmac_create函数,而分组算法的算法模板实现提供alloc接口,如CBC模板提供的alloc接口为crypto_cbc_alloc函数。
如果在算法探测过程中出现错误,则需要在线程退出前调用crypto_larval_error函数完成收尾工作。crypto_larval_error函数的输入参数为算法幼虫名(即待创建的动态算法名)param->larval、原始算法类型param->otype和原始算法类型屏蔽位param->omask
注意事项
crypto_larval_error 版本5.15.11已经不再支持
如果以输入参数为条件查找到算法幼虫,则唤醒在其完成量上等待的线程;如果查找到的是已注册的算法,在算法检测线程中已唤醒在注册用算法幼虫完成量上等待的线程,如下所示。
问题:
create和alloc必须要二者选一实现,但是我没有找到证据 进行论证


注册动态算法crypto_register_instance
crypto_register_instance函数用于注册动态算法(即算法模板实例),输入参数包括算法模板tmpl和算法模板实例inst,处理流程如下所示。
algapi.c - crypto/algapi.c - Linux source code (v5.15.12) - Bootlin
int crypto_register_instance(struct crypto_template *tmpl,
                 struct crypto_instance *inst)
{
    struct crypto_larval *larval;
    struct crypto_spawn *spawn;
    int err;
 
    err = crypto_check_alg(&inst->alg);
    if (err)
        return err;
 
    inst->alg.cra_module = tmpl->module;
    inst->alg.cra_flags |= CRYPTO_ALG_INSTANCE;
 
    down_write(&crypto_alg_sem);
 
    larval = ERR_PTR(-EAGAIN);
    for (spawn = inst->spawns; spawn;) {
        struct crypto_spawn *next;
 
        if (spawn->dead)
            goto unlock;
 
        next = spawn->next;
        spawn->inst = inst;
        spawn->registered = true;
 
        crypto_mod_put(spawn->alg);
 
        spawn = next;
    }
 
    larval = __crypto_register_alg(&inst->alg);
    if (IS_ERR(larval))
        goto unlock;
 
    hlist_add_head(&inst->list, &tmpl->instances);
    inst->tmpl = tmpl;
 
unlock:
    up_write(&crypto_alg_sem);
 
    err = PTR_ERR(larval);
    if (IS_ERR(larval))
        goto err;
 
    crypto_wait_for_test(larval);
    err = 0;
 
err:
    return err;
}
EXPORT_SYMBOL_GPL(crypto_register_instance);

1)算法注册由通用算法注册函数__crypto_register_alg完成,输入参数为算法模板实例对应的通用算法说明inst->alg,返回值为检测用算法幼虫larval。
2)算法模板和算法模板实例的关联代码如下:
    hlist_add_head(&inst->list, &tmpl->instances);
    inst->tmpl = tmpl;
即将算法模板实例添加到算法模板的实例链表中,同时设置算法模板实例归属的算法模板。
3)和静态算法相同,动态算法注册的最后一步是算法正确性检验,调用crypto_wait_for_test函数实现。
4)crypto_register_instance函数中接口调用情况如下所示。


5)注册同步哈希算法模板实例时使用的函数是shash_register_instance,其输入参数包括算法模板tmpl和同步哈希算法模板实例inst,处理流程如下所示。


 shash.c - crypto/shash.c - Linux source code (v5.15.12) - Bootlin
int shash_register_instance(struct crypto_template *tmpl,
                struct shash_instance *inst)
{
    int err;
 
    if (WARN_ON(!inst->free))
        return -EINVAL;
 
    err = shash_prepare_alg(&inst->alg);
    if (err)
        return err;
 
    return crypto_register_instance(tmpl, shash_crypto_instance(inst));
}
EXPORT_SYMBOL_GPL(shash_register_instance);
和同步哈希静态算法相同,在注册前首先调用shash_prepare_alg函数检测同步哈希算法模板实例对应的同步哈希算法(inst->alg)的有效性,同时进行注册前的准备工作。注册前准备工作中最重要的一步是将算法类型常量设置为crypto_shash_type。
同步哈希动态算法的注册工作是由通用动态算法注册函数crypto_register_instance完成。crypto_register_instance函数处理的是通用的算法模板实例,因此调用shash_crypto_instance函数获取同步哈希算法模板实例inst对应的通用算法模板实例。
shash_register_instance函数中接口调用情况如下所示。

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/CHYabc123456hh/article/details/122309259

你可能感兴趣的:(Linux内核-Crypto,linux,算法,服务器)