繁体   English   中英

linux 加密 api 中的散列表

[英]Scatterlist in linux crypto api

我开始学习如何在 linux 中使用 Crypto API。 它提供了使用分散列表结构将明文传输到分组密码功能。 通过在内存页面上存储明文的位置来处理明文的散列表。 struct scatterlist 的简单定义是:

struct scatterlist {
      unsigned long   page_link;      //number of virtual page in kernel space where data buffer is stored
      unsigned int    offset;         //offset from page start address to data buffer start address
      unsigned int    length;         //data buffer length
      dma_addr_t      dma_address;    //i don't know the purpose of this variable at the moment
};

为了获得处理明文缓冲区的void sg_init_one(struct scatterlist *, const void *, unsigned int);变量,我们使用下一个函数: void sg_init_one(struct scatterlist *, const void *, unsigned int); . 为了从 scatterlist 变量中获取缓冲区起始地址,我们使用下一个函数: void *sg_virt(struct scatterlist *sg) 例如:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>

u8 plaintext_global[16]={0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

static int __init simple_init (void){

u8 *ptr_to_local, *ptr_to_global;
u8 palintext_local[16]={0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
struct scatterlist sg[2];
sg_init_one(&sg[0], plaintext_local, 16);
sg_init_one(&sg[1], plaintext_global, 16);
printk("sg[0].page_link=%u\n", sg[0].page_link);
printk("sg[0].offset=%u\n", sg[0].offset);
printk("sg[0].length=%u\n", sg[0].length);
printk("sg[1].page_link=%u\n", sg[1].page_link);
printk("sg[1].offset=%u\n", sg[1].offset);
printk("sg[1].length=%u\n", sg[1].length);
ptr_to_local=sg_virt(&sg[0]);
ptr_to_global=sg_virt(&sg[1]);
printk("plaintext_local start address:%p\n", plaintext_local);
printk("sg_virt(&sg[0]):%p\n", ptr_to_local);
printk("plaintext_global start address:%p\n", plaintext_global);
printk("sg_virt(&sg[1]):%p\n", ptr_to_global);
}

并在 insmod 这个模块之后在 dmesg 中输出:

sg[0].page_link=31209922
sg[0].offset=3168
sg[0].length=16
sg[1].page_link=16853378
sg[1].offset=0
sg[1].length=16
plaintext_local start address:ffff8800770e7c60
sg_virt(&sg[0]):ffff8800770e7c60
plaintext_global start address:ffffffffc04a6000
sg_virt(&sg[1]):ffff8800404a6000

第一个问题是为什么本地明文缓冲区 sg_virt 返回的值与本地缓冲区地址相同,但全局明文缓冲区的 sg_virt 返回值有另一个前缀而不是全局缓冲区地址?

下一个。 现在我使用加密 api:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
u8 aes_in[]={0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
u8 aes_key[]={0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
u8 aes_out[]={0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a};
static int __init simple_init (void){

struct crypto_blkcipher *blk;
struct blkcipher_desc desc;
struct scatterlist sg[3];
u8 encrypted[100];
u8 decrypted[100];
blk=crypto_alloc_blkcipher("ecb(aes)",0,0);
crypto_blkcipher_setkey(blk, aes_key, 16);
sg_init_one(&sg[0], aes_in, 16);
sg_init_one(&sg[1], encrypted, 16);
sg_init_one(&sg[2], decrypted, 16);
desc.tfm=blk;
desc.flags=0;
sg_copy_from_buffer(&sg[0],1,aes_128_in, 16);
crypto_blkcipher_encrypt(&desc, &sg[1], &sg[0], 16);
crypto_blkcipher_decrypt(&desc, &sg[2], &sg[1], 16);
crypto_free_blkcipher(blk);
}

加密数据:69 c4 e0 d8 6a 7b 04 30 d8 cd b7 80 70 b4 c5 5a

解密数据:00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff

下一个问题,sg_copy_from_buffer 函数具体做了什么? 没有这个功能加密数据不对:

没有 sg_copy_from_buffer 的加密数据:03 07 23 fc 20 11 42 c6 60 b3 36 07 eb c8 c9 62

没有 sg_copy_from_buffer 的加密数据:00 00 00 00 00 00 00 00 58 51 02 a0 f7 7f 00 00

对于您的第一个问题,分散列表将您提供的缓冲区保存为内部struct page (“页面链接实际上是指向结构页面的指针”),您可以将其视为物理地址(不完全是,而是struct page确实代表一个唯一的物理页面)。

这意味着 scatterlist 将首先通过sg_init_one将缓冲区的虚拟地址转换为相应的物理地址,最后调用宏函数___pa来执行此操作。 当您调用sg_virt ,它将通过另一个宏___va存储的物理地址转换回虚拟地址

实际上, ___pa用于将线性映射地址范围内内核映像地址范围内的虚拟地址转换为其对应的物理地址 ___va用于在线性映射地址范围内将物理地址转换为其对应的虚拟地址 上述地址范围转换地址时,它们可能会给出错误的输出。

但是,您提供给分散列表的全局缓冲区位于内核映像地址范围之后内核模块地址范围内,而本地缓冲区位于内核映像地址范围之前的内核堆栈地址范围内。 两者都不在内核线性映射地址范围内,所以经过“___pa”和“___va”转换后,很可能是错误的

根据你的测试,本地缓冲区地址是正确的,全局缓冲区地址是错误的,这是因为本地缓冲区地址在内核映像地址范围之前,全局缓冲区地址在内核映像地址范围之后,所以它们被转换在“__pa”中以不同的方式但在“___va”中以相同的方式。 您可以从 linux 内核源代码/arch/x86/include/asm/page.h/arch/x86/include/asm/page_64.h的以下代码片段中看到它。

// This function is the implementation of ___pa on x86-64
static inline unsigned long __phys_addr_nodebug(unsigned long x)
{
    // __START_KERNEL_map is the start address of the kernel image address range.
    unsigned long y = x - __START_KERNEL_map;

    // You can see that this function behaves differently depending on x and __START_KERNEL_map
    /* use the carry flag to determine if x was < __START_KERNEL_map */
    // phys_base is the start of system's physical address
    // PAGE_OFFSET is the start of linear mapping address range
    x = y + ((x > y) ? phys_base : (__START_KERNEL_map - PAGE_OFFSET));

    return x;
}
// This is the implementation of ___va on x86-64
#define __va(x)         ((void *)((unsigned long)(x)+PAGE_OFFSET))

对于内核映像地址范围之前的虚拟地址, ___va只是加上一个偏移量, ___pa只是减去相同的偏移量,所以本地缓冲区地址是对的。 但是,对于内核映像地址之后的虚拟地址, ___va做同样的工作,但___pa表现不同,所以全局缓冲区地址是错误的。

对于 x86-64 linux 内核内存布局,请参考 Documentation/x86/x86_64/mm.rst 中的 linux 源代码

注意只有kmalloc分配的缓冲区会停留在内核线性映射地址范围内,所以你应该始终使用kmalloc为linux内核加密操作分配内存。

对于您的第二个问题, scatterlist 可以由单个struct scatterlist 然而,它实际上是为管理内存块列表而设计的,每个块都由一个struct scatterlist表示。 使用sg_copy_from_buffer ,您可以将存储在连续缓冲区中的数据复制到由多个struct scatterlist管理的内存块列表。 简而言之, sg_copy_from_buffer 与加密无关。

更多细节请参考以下内核源代码文件。

/include/linux/scatterlist.h 
/lib/scatterlist.c
/arch/x86/include/asm/page.h
/arch/x86/include/asm/page_64.h

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM