0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Kernel密码学算法的软件框架和接口模型

Linux阅码场 来源:Linux阅码场 作者:baron 2022-05-09 10:22 次阅读

说明:
在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14

思考:

1、Linux Kernel中支持哪些密码学算法?分别都是怎么实现的?哪些是C语言实现?哪些是Neon指令实现?哪些是ARM Cryptography Extension硬件实现?这些不同的实现方式,他们之间的关系是怎样的?并列关系?多选一?多选多?

2、应用程序的密码学算法一般又是怎样实现的?应用程序的密码学算法实现,是否依赖Kernel底层的密码学算法?

3、应用程序是如何调用到Kernel底层的密码学算法?Kernel底层的其它模块,如何调用密码学算法?

4、如何在Kernel底层增加一种密码学算法的实现?

5、Kernel的其它模块中,有哪些需要使用密码学算法的场景?
本文术语定义:算法 :算法的种类,如对称密码算法、非对称密码算法...算法实现 :具体的某一类算法,如aes-cbc、aes-ebc、sm4-cbc、twofish-ecb...

目录

1、密码学基础知识

2、Kernel密码学算法的软件框架和接口模型

2.1、Userspace对底层密码算法的访问2.2、Kernelspace对底层密码算法的访问2.3、增加一个算法实现

3、kernel中实现的算法实现

4、crypto engine的实现

5、代码导读

1、密码学基础知识

基本概念,如下请自行学习和理解:

  • 对称密码

  • 非对称密码

  • 数字摘要

  • 随机数

2、Kernel密码学算法的软件框架和接口模型

Linux Kernel系统中实现了很多算法,这些算法被统一归纳为:对称密码算法、数字摘要算法、随机数算法、认证加密算法、非对称密码算法等,并在Kernel层提供了统一操作的接口,供kernel其他模块调用。部分算法又被封装到了网络层,开放暴露给Userspace。其具体的结构/接口模型如下所示:

2bb928b8-cf2c-11ec-bce3-dac502259ad0.png

2.1、Userspace对底层密码算法的访问

Userspace通过netlink接口方式( PF_ALG)调用到底层算法的实现

2bd320e2-cf2c-11ec-bce3-dac502259ad0.png

在Userspace,需指定socket接口 PF_ALG,需指定算法名称(如skcipher)、需指定具体调用的"算法实现"(如aes-cbc),这样命令传输到Kernel层,就能根据这些信息跳转到响应的算法实现层。注意akcipher算法没有暴露给网络层,也就没有开放给Userspace了,所以在User程序中,是无法调用Kernel层的非对称密码算法的。

如下是一个Userspace程序调用kernel底层算法的示例:

(1)建立一个socket会话的流程:

socket(AF_ALG,...)bind()setsockoptacceptsendmsgrecvmsg

(2)相关代码

static int linux_af_alg_socket(const char *type, const char *name){  struct sockaddr_alg sa;  int s;
  s = socket(AF_ALG, SOCK_SEQPACKET, 0);  if (s < 0) {    LogErr("%s: Failed to open AF_ALG socket: %s
",         __func__, strerror(errno));    return -1;  }
  os_memset(&sa, 0, sizeof(sa));  sa.salg_family = AF_ALG;  os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));  os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_name));  if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {    LogErr("%s: Failed to bind AF_ALG socket(%s,%s): %s
",__func__, (char *) sa.salg_type, (char *) sa.salg_name, strerror(errno));    close(s);    return -1;  }
  return s;}
static struct linux_af_alg_skcipher *linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len){  struct linux_af_alg_skcipher *skcipher;
  skcipher = os_zalloc(sizeof(*skcipher));  if (!skcipher)    goto fail;  skcipher->t = -1;
  skcipher->s = linux_af_alg_socket(TYPE_NAME, alg);  if (skcipher->s < 0)    goto fail;
  if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {    LogErr("%s: setsockopt(ALG_SET_KEY) failed: %s
",         __func__, strerror(errno));    goto fail;  }
  skcipher->t = accept(skcipher->s, NULL, NULL);  if (skcipher->t < 0) {    LogErr("%s: accept on AF_ALG socket failed: %s
",         __func__, strerror(errno));    goto fail;  }
  return skcipher;fail:  linux_af_alg_skcipher_deinit(skcipher);  return NULL;}
static int aes_128_cbc_oper(char *alg_name, const u8 *key,size_t key_len, int enc, const u8 *iv, u8 *data, size_t data_len){  struct linux_af_alg_skcipher *skcipher;  char buf[100];  struct iovec io[1];  struct msghdr msg;  struct cmsghdr *hdr;  ssize_t ret;  u32 *op;  struct af_alg_iv *alg_iv;  size_t iv_len = AES_BLOCK_SIZE;
  skcipher = linux_af_alg_skcipher(alg_name, key, key_len);//alg_name = "__cbc-aes-asr-ce"  if (!skcipher)    return -1;
  io[0].iov_base = (void *) data;  io[0].iov_len = data_len;  os_memset(&msg, 0, sizeof(msg));  os_memset(buf, 0, sizeof(buf));  msg.msg_control = buf;  msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +    CMSG_SPACE(sizeof(*alg_iv) + iv_len);  msg.msg_iov = io;  msg.msg_iovlen = 1;
  hdr = CMSG_FIRSTHDR(&msg);  hdr->cmsg_level = SOL_ALG;  hdr->cmsg_type = ALG_SET_OP;  hdr->cmsg_len = CMSG_LEN(sizeof(u32));  op = (u32 *) CMSG_DATA(hdr);  *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
  hdr = CMSG_NXTHDR(&msg, hdr);  hdr->cmsg_level = SOL_ALG;  hdr->cmsg_type = ALG_SET_IV;  hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);  alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);  if(NULL != iv){    alg_iv->ivlen = iv_len;    os_memcpy(alg_iv->iv, iv, iv_len);  }else  {    alg_iv->ivlen = 0;  }
  ret = sendmsg(skcipher->t, &msg, 0);  if (ret < 0) {    LogErr("%s: sendmsg failed: %s
",         __func__, strerror(errno));    linux_af_alg_skcipher_deinit(skcipher);    return -1;  }
  ret = recvmsg(skcipher->t, &msg, 0);  if (ret < 0) {    LogErr("%s: recvmsg failed: %s
",         __func__, strerror(errno));    linux_af_alg_skcipher_deinit(skcipher);    return -1;  }  if ((size_t) ret < data_len) {    LogErr(         "%s: recvmsg not return full data (%d/%d)
",         __func__, (int) ret, (int) data_len);    linux_af_alg_skcipher_deinit(skcipher);    return -1;  }
  //s_to_binary(data,data_len);  linux_af_alg_skcipher_deinit(skcipher);  return 0;}
2.2、Kernelspace对底层密码算法的访问

Kernel程序对底层算法的调用采用函数直接调用的方式。流程为:kernel程序--->算法中间层--->算法实现层. 算法中间层 就是暴露给kernel其它模块的API函数。

如下是一个kernel中调用底层算法的一个示例(因skcipher为例):

static int test_skcipher(void){        struct crypto_skcipher *tfm = NULL;        struct skcipher_request *req = NULL;        u8 *data = NULL;        const size_t datasize = 512; /* data size in bytes */        struct scatterlist sg;        DECLARE_CRYPTO_WAIT(wait);        u8 iv[16];  /* AES-256-XTS takes a 16-byte IV */        u8 key[64]; /* AES-256-XTS takes a 64-byte key */        int err;
        /*         * Allocate a tfm (a transformation object) and set the key.         *         * In real-world use, a tfm and key are typically used for many         * encryption/decryption operations.  But in this example, we'll just do a         * single encryption operation with it (which is not very efficient).         */
        tfm = crypto_alloc_skcipher("xts(aes)", 0, 0);        if (IS_ERR(tfm)) {                pr_err("Error allocating xts(aes) handle: %ld
", PTR_ERR(tfm));                return PTR_ERR(tfm);        }
        get_random_bytes(key, sizeof(key));        err = crypto_skcipher_setkey(tfm, key, sizeof(key));        if (err) {                pr_err("Error setting key: %d
", err);                goto out;        }
        /* Allocate a request object */        req = skcipher_request_alloc(tfm, GFP_KERNEL);        if (!req) {                err = -ENOMEM;                goto out;        }
        /* Prepare the input data */        data = kmalloc(datasize, GFP_KERNEL);        if (!data) {                err = -ENOMEM;                goto out;        }        get_random_bytes(data, datasize);
        /* Initialize the IV */        get_random_bytes(iv, sizeof(iv));
        /*         * Encrypt the data in-place.         *         * For simplicity, in this example we wait for the request to complete         * before proceeding, even if the underlying implementation is asynchronous.         *         * To decrypt instead of encrypt, just change crypto_skcipher_encrypt() to         * crypto_skcipher_decrypt().         */        sg_init_one(&sg, data, datasize);        skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |                                           CRYPTO_TFM_REQ_MAY_SLEEP,                                      crypto_req_done, &wait);        skcipher_request_set_crypt(req, &sg, &sg, datasize, iv);        err = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);        if (err) {                pr_err("Error encrypting data: %d
", err);                goto out;        }
        pr_debug("Encryption was successful
");out:        crypto_free_skcipher(tfm);        skcipher_request_free(req);        kfree(data);        return err;}
2.3、增加一个算法实现

增加一个"算法的实现" 只需要:

  • 定义一个算法的结构体变量并初始化,其实就是实现其中的成员函数

  • 将该算法实现注册到系统中。

结构体的定义并初始化:

static struct skcipher_alg aes_algs[] = {   {    .base.cra_name    = "__ecb(aes)",    .base.cra_driver_name  = "__ecb-aes-neonbs",    .base.cra_priority  = 250,    .base.cra_blocksize  = AES_BLOCK_SIZE,    .base.cra_ctxsize  = sizeof(struct aesbs_ctx),    .base.cra_module  = THIS_MODULE,    .base.cra_flags    = CRYPTO_ALG_INTERNAL,
    .min_keysize    = AES_MIN_KEY_SIZE,    .max_keysize    = AES_MAX_KEY_SIZE,    .walksize    = 8 * AES_BLOCK_SIZE,    .setkey      = aesbs_setkey,    .encrypt    = ecb_encrypt,    .decrypt    = ecb_decrypt,  }, 
  {    .base.cra_name    = "__cbc(aes)",    .base.cra_driver_name  = "__cbc-aes-neonbs",    .base.cra_priority  = 250,    .base.cra_blocksize  = AES_BLOCK_SIZE,    .base.cra_ctxsize  = sizeof(struct aesbs_cbc_ctx),    .base.cra_module  = THIS_MODULE,    .base.cra_flags    = CRYPTO_ALG_INTERNAL,
    .min_keysize    = AES_MIN_KEY_SIZE,    .max_keysize    = AES_MAX_KEY_SIZE,    .walksize    = 8 * AES_BLOCK_SIZE,    .ivsize      = AES_BLOCK_SIZE,    .setkey      = aesbs_cbc_setkey,    .encrypt    = cbc_encrypt,    .decrypt    = cbc_decrypt,  }};

成员函数的实现,例如:

static int ecb_encrypt(struct skcipher_request *req){  return __ecb_crypt(req, aesbs_ecb_encrypt);}

将该算法实现注册到系统中:

static int __init aes_init(void){...  err = crypto_register_skciphers(aes_algs, ARRAY_SIZE(aes_algs));...}module_init(aes_init);
小小总结一下, 如果您要增加一个算法实现,那么您就是需要实现定义如下结构体,并调用 crypto_register_xxx()注册到kernel系统中:
  • skcipher_alg
  • akcipher_alg
  • ahash_alg
  • rng_alg
  • aead_alg

3、kernel中实现的算法实现

思考:

  • 对称密码底层是怎样实现的?纯软?硬件?Neon指令?CE指令?

  • 非对称密码底层是怎样实现的?

  • Hash、rng、aead 又都是怎样实现的?

实现算法的方式:

  • (1)在armv8/armv9的芯片中,有ARM-CE指令可以进行aes/hash/md5计算,

  • (2)在armv8/armv9的芯片中,也有ARM-NEON指令也可以进行aes/hash/md5计算

  • (3)arm的security IP中,有cryptocell之类的加密芯片

  • (4)另外SOC厂商也可能集成自己设计的crypto engine加解密芯片

  • (5)除此之外,还有C语言、汇编程序等编程语言实现的纯软实现

毫无疑问,在效率这块肯定是:(3)(4) > (1) > (2) > (5).另外从"实现算法的方式" 来看,如果是rng、aead、rsa之类的算法,那么就不能用ARM-CE这种方式,只有编程语言实现、Neon指令实现、crypto engine(含arm security IP)这几种方式了。

kernel怎么玩的?:

  • 针对 crypto engine(含arm security IP) 这种,先当SOC硬件不支持,跳过此场景。

  • 针对rng、aead、rsa,那么kernel有一套纯软的实现 (似乎没有看到arm neon指令的实现)

  • 针对aes、hash,有arm-ce的实现、arm neon指令的实现、纯软的实现,三者三选一(通过宏开关,只能选1)

crypto engine的实现:如果自定义了crypto engine的实现,那么要看你具体的设计,是设计成“取代原有算法实现”,还是设计成“新增算法实现”。如果是前者,那么对于aes/hash,则变成了四选一的了(crypto engine实现、arm-ce的实现、arm neon指令的实现、纯软)。如果是后者,这和原有实现不冲突。

有关aes/hash底层实现三选一的开关

(1) 开启下面两个宏,使用ARM Neon指令的实现 CONFIG_CRYPTO_AES_ARM64_CE_BLK CONFIG_CRYPTO_AES_ARM64_NEON_BLK(2) 在(1) 的基础之上,再开启如下宏,使用ARM CE指令的实现 USE_V8_CRYPTO_EXTENSIONS(3) 以上三个宏都不开启的情况下,使用默认的纯软实现

4、crypto engine的实现

(以ARM Security IP的cryptocell 712为例)

2bf47fbc-cf2c-11ec-bce3-dac502259ad0.png

在Linux Kernel中开启 CONFIG_CRYPTO_DEV_CCREE宏控即可起用该实现, 代码路径如下:

2c0e11ca-cf2c-11ec-bce3-dac502259ad0.png

以为aes-cbc为例,其实现的名字 和 Kernel中默认是算法实现的名字是一致的,即使这种实现方式是取代原有算法实现


{  .name = "cbc(aes)",  .driver_name = "cbc-aes-ccree",  .blocksize = AES_BLOCK_SIZE,  .template_skcipher = {    .setkey = cc_cipher_setkey,    .encrypt = cc_cipher_encrypt,    .decrypt = cc_cipher_decrypt,    .min_keysize = AES_MIN_KEY_SIZE,    .max_keysize = AES_MAX_KEY_SIZE,    .ivsize = AES_BLOCK_SIZE,  },  .cipher_mode = DRV_CIPHER_CBC,  .flow_mode = S_DIN_to_AES,  .min_hw_rev = CC_HW_REV_630,  .std_body = CC_STD_NIST,}

4、代码导读

在网络层、算法中间层、算法实现层有着丰富的结构体类型?那么怎么去阅读代码?怎弄清各个层面之间的逻辑呢?事实上我们只要理清这些结构体之间的关系,将其抽象成模型,就会变得更加容易理解了。

如下是以Userspace调用底层的对称密码函数为例总结的一张数据结构图:

2c29c6e0-cf2c-11ec-bce3-dac502259ad0.png

sock通信进入网络层后(algifskcipher.c),构建skcipherrequest结构体,通过该结构体,就能寻址到底层的算法实现,继而完成算法实现的调用。这些总结一下就是:

  • skcipher_request //网络层构建的结构体

  • cryptoskcipher // kernel中间层构建的结构体,如果是kernel层调用底层算法,那么就从构建cryptocipher结构体开始。

  • skcipher_alg //算法实现层的结构体,描述着具体的算法实现,有实现厂商自己添加。

上述复杂的结构体流程,进一步抽象,就变成如下这个样子:

2c495140-cf2c-11ec-bce3-dac502259ad0.png

既然如此,那么我们还可以举一反三一下:

2c5cddc8-cf2c-11ec-bce3-dac502259ad0.png

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4587

    浏览量

    92478
  • Linux
    +关注

    关注

    87

    文章

    11199

    浏览量

    208691
  • 代码
    +关注

    关注

    30

    文章

    4719

    浏览量

    68211

原文标题:一文了解Linux Kernel中密码学算法的设计与应用

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    AI算法/模型/框架/模型库的含义、区别与联系

    在人工智能(Artificial Intelligence,简称AI)的广阔领域中,算法模型框架模型库是构成其技术生态的重要基石。它们各自承担着不同的角色,但又紧密相连,共同推动
    的头像 发表于 07-17 17:11 2633次阅读

    AI大模型与AI框架的关系

    多个领域取得显著成果。而AI框架则是为开发和训练AI模型提供的一套标准接口、特性库和工具包,它集成了算法的封装、数据的调用以及计算资源的使用,是AI
    的头像 发表于 07-15 11:42 849次阅读

    鸿蒙开发:Universal Keystore Kit密钥管理服务 密钥派生介绍及算法规格

    密码学中,密钥派生函数(Key derivation function,KDF)使用伪随机函数从诸如主密码密码的秘密值中派生出一个或多个密钥。
    的头像 发表于 07-11 16:30 280次阅读

    鸿蒙开发接口安全:【@ohos.security.huks (通用密钥库系统)】

    向应用提供密钥库能力,包括密钥管理及密钥的密码学操作等功能。 HUKS所管理的密钥可以由应用导入或者由应用调用HUKS接口生成。
    的头像 发表于 06-05 17:40 1180次阅读
    鸿蒙开发<b class='flag-5'>接口</b>安全:【@ohos.security.huks (通用密钥库系统)】

    RED Semiconductor宣布推出算法微处理器ISA和硬件设计RISC-V

    4月2日,RED Semiconductor(以下简称 "RED")宣布推出算法微处理器 ISA(指令集架构)和硬件设计 VISC,将 RISC-V 的功能扩展到边缘人工智能、自动驾驶和密码学领域。
    的头像 发表于 04-03 17:31 528次阅读

    NVIDIA宣布推出基于Omniverse Cloud API构建的全新软件框架

    NVIDIA 在 GTC 大会上宣布推出基于 Omniverse Cloud API(应用编程接口)构建的全新软件框架
    的头像 发表于 03-25 09:09 475次阅读

    恩智浦:向后量子密码学迁移,我们应该怎么做?

    在之前的博文中,我们介绍了由美国国家标准与技术研究院 (NIST) 主导的后量子密码学 (PQC) 标准化进程,以及未来可能采用的部分PQC标准。在这篇博文中,我们探讨PQC迁移过程中面临的一些挑战
    的头像 发表于 03-22 09:39 1601次阅读
    恩智浦:向后量子<b class='flag-5'>密码学</b>迁移,我们应该怎么做?

    OpenVINO™协同Semantic Kernel:优化大模型应用性能新路径

    作为主要面向 RAG 任务方向的框架,Semantic Kernel 可以简化大模型应用开发过程,而在 RAG 任务中最常用的深度学习模型就是 Embedding 和 Text com
    的头像 发表于 03-08 10:41 596次阅读
    OpenVINO™协同Semantic <b class='flag-5'>Kernel</b>:优化大<b class='flag-5'>模型</b>应用性能新路径

    自制测试框架(设置界面密码1)

    (设置界面密码为1) 目前支持的指令如下: 1.没有任何编程基础的人快速实现简易自动化制作; 2.测试后可以直接把数据和结果直接写入到MES; 3.可持续增加新的功能及模块; 4.快速实现点
    发表于 03-02 19:33

    谷歌模型框架是什么软件?谷歌模型框架怎么用?

    谷歌模型框架通常指的是谷歌开发的用于机器学习和人工智能的软件框架,其中最著名的是TensorFlow。TensorFlow是一个开源的机器学习框架
    的头像 发表于 03-01 16:25 763次阅读

    数据语料库、算法框架和算力芯片在AI大模型中的作用和影响

    数据语料库、算法框架和算力芯片的确是影响AI大模型发展的三大重要因素。
    的头像 发表于 03-01 09:42 914次阅读

    谷歌模型框架是什么?有哪些功能和应用?

    谷歌模型框架(Google Model Framework)并不是一个特定的框架,而是指谷歌开发的一系列软件框架,用于支持机器学习和人工智能
    的头像 发表于 02-29 18:11 1376次阅读

    什么是后量子密码学?量子计算机vs经典计算机

    后量子密码学(Post-Quantum Cryptography,PQC)是在经典计算机上定义和执行算法,研究量子计算机和经典计算机都无法破解的新密码系统。后量子密码学的提出是为了抵抗
    的头像 发表于 12-19 11:42 1554次阅读

    基于车辆运动和动力学模型的横向控制算法

    ,因此,该方法比较适合应用于低速曲率较小的路径跟踪控制场景中。 基于运动模型的横向控制算法中,Pure Pursuit 和 Stanley 前轮反馈算法在中低速场景下,他们的路径跟踪
    的头像 发表于 11-15 17:02 744次阅读

    基于车辆运动模型的控制方法

    车辆运动模型 基于车辆运动模型的控制方法主要考虑了车辆的运动约束对车辆控制的影响。 平时见到的汽车属于 阿克曼转向,通常可以简化为两轮
    的头像 发表于 11-15 16:16 514次阅读
    基于车辆运动<b class='flag-5'>学</b><b class='flag-5'>模型</b>的控制方法