当前位置: 首页 > 工具软件 > Crypt > 使用案例 >

Linux内核中的dm-crypt模块的异步IO改造---blog11

吴丁雷
2023-12-01

2021SC@SUDSC

为了更深一步了解cryptsetup加密的工作原理,找到了cryptsetup LUCK设备使用的相关代码——crypt_luks_usage.c。主要包含以下几个函数:

  • crypt_init()
  • crypt_format()
  • crypt_keyslot_add_by_volume_key()
  • crypt_keyslot_add_by_passphrase()
  • crypt_load()
  • crypt_activate_by_passphrase()
  • crypt_get_active_device()
  • crypt_init_by_name()
  • crypt_deactivate()

下面先一一给出简单介绍

一、函数介绍

1. crypt_init()

每次需要使用 cryptsetup 或 dmcrypt device 执行某些操作时,都需要一个有效的上下文。开始工作的第一步是 crypt_init() 调用。一般使用块设备的路径或常规文件的路径来调用它。如果不提供路径,则初始化空上下文。

2. crypt_format()

相互设备上的标头和有效载荷,由于下面的函数是基于LUKS设备进行的,所以使用格式操作在上下文中设置设备类型,以防LUKS标头写入块设备的开头。下面代码的具体使用是LUKS标头和数据都存储在同一设备上的场景。除此之外,还可以单独存储标题和数据。
但是,有一点需要注意,crypt_format()是破坏性操作,它会覆盖部分支持块设备。

3. crypt_keyslot_add_by_volume_key()

在成功对LUKS设备进行crypt_format() 操作后,密钥不会以持久方式存储在设备上,密钥槽区域是一个超出 LUKS 标头的数组,其中密钥使用用户输入密码以加密形式存储。创建新的密钥槽有两种基本方法,除了这一种外,下面也是一种。
这一种通过加密存储在设备上下文中的 volume_key 直接创建一个新的密钥槽。如果密码短语参数为 NULL,则应提供密码短语或提示用户。

4. crypt_keyslot_add_by_passphrase()

通过打开现有的活动密钥槽,从中提取密钥并将其存储到由新密码保护的新密钥槽中,为密钥创建新的密钥槽。

5. crypt_load()

函数将标头从支持块设备加载到设备上下文中。

6. crypt_activate_by_passphrase()

通过用户为包含 volume_key 的密钥槽提供的密码激活加密设备。如果keyslot参数设置为CRYPT_ANY_SLOT,那么。就会查找密钥槽都试图直到volume key被找到。

7. crypt_get_active_device()

此调用返回包含活动设备的运行时属性的结构。

8. crypt_init_by_name()

如果需要对活动设备(已经具有其相应映射的设备)进行操作并且错过了存储在 *crypt_device 引用中的有效设备上下文,那么,可以使用此调用。函数尝试从 DM 获取后备设备的路径,为其初始化上下文并加载 LUKS 标头。

9. crypt_deactivate()

停用加密设备(删除 DM 映射并从内核安全地擦除卷密钥)。

二、具体代码及分析

为了便于书写,分析直接以注释的方式写到了代码中间。

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <libcryptsetup.h>
 
static int format_and_add_keyslots(const char *path)
{
        struct crypt_device *cd;
        int r;
 
        /*
         crypt_init() 调用用于初始化 crypt_device 上下文
         path 参数指定设备路径
         对于路径,可以使用指向文件或块设备的链接。
         如果设备之间存在环路,那么将自动分离。
         */
 
        r = crypt_init(&cd, path);
        if (r < 0) {
                printf("crypt_init() failed for %s.\n", path);
                return r;
        }
 
        printf("Context is attached to block device %s.\n", crypt_get_device_name(cd));
 
        /*
         到目前为止,还没有数据写入设备。
         */
        printf("Device %s will be formatted as a LUKS device after 5 seconds.\n"
               "Press CTRL+C now if you want to cancel this operation.\n", path);
        sleep(5);
 
        /*
        uuid 和 volume_key 为 null,这就意味着它们是在 crypt_format() 调用后生成的。
         */
        r = crypt_format(cd,            //crypt 上下文
                         CRYPT_LUKS2,   //LUKS2 是一种新的 LUKS 格式;将 CRYPT_LUKS1 用于 LUKS1
                         "aes",         //使用的密码
                         "xts-plain64", //使用块模式和 IV
                         NULL,          //生成 UUID
                         NULL,          //从 RNG 生成volume_key
                         512 / 8,       //512bit 密钥 - 这里是 XTS 模式下的 AES-256,大小以字节为单位
                         NULL);         // 默认参数
 
        if (r < 0) {
                printf("crypt_format() failed on device %s\n", crypt_get_device_name(cd));
                crypt_free(cd);
                return r;
        }
 
        /*
         该设备现在包含一个 LUKS 标头,但没有活动的密钥槽。
         crypt_keyslot_add_* 调用以加密形式将volume_key 存储到keyslot 中。
         格式化后,volume key存储在内部。
         */
        r = crypt_keyslot_add_by_volume_key(cd,                 //crypt 上下文
                                            CRYPT_ANY_SLOT,     //只使用第一个空闲槽
                                            NULL,               // 使用内部 volume key 
                                            0,                  /* 未使用(volume key的大小) */
                                            "foo",              //密码 - NULL 表示查询
                                            3);                 //密码的大小
 
        if (r < 0) {
                printf("Adding keyslot failed.\n");
                crypt_free(cd);
                return r;
        }
 
        printf("The first keyslot is initialized.\n");
 
        /*
         添加另一个密钥槽,现在使用第一个密钥槽进行身份验证。
         它使用密钥槽第一个volume_key 解密,并使用指定的密码创建一个新的密钥。
         */
        r = crypt_keyslot_add_by_passphrase(cd,                 //crypt 上下文
                                            CRYPT_ANY_SLOT,     //只使用第一个空闲槽
                                            "foo", 3,           //旧密钥槽的密码
                                            "bar", 3);          //新密钥槽的密码
        if (r < 0) {
                printf("Adding keyslot failed.\n");
                crypt_free(cd);
                return r;
        }
 
        printf("The second keyslot is initialized.\n");
 
        crypt_free(cd);
        return 0;
}
 
static int activate_and_check_status(const char *path, const char *device_name)
{
        struct crypt_device *cd;
        struct crypt_active_device cad;
        int r;
 
        /*
         LUKS 设备激活
         */
        r = crypt_init(&cd, path);
        if (r < 0) {
                printf("crypt_init() failed for %s.\n", path);
                return r;
        }
 
        /*
         crypt_load() 用于从块设备加载现有的 LUKS 标头
         */
        r = crypt_load(cd,              // crypt 上下文
                       CRYPT_LUKS,      //请求类型 - 这里是任何类型的 LUKS
                       NULL);           //附加参数(未使用)
 
        if (r < 0) {
                printf("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
                crypt_free(cd);
                return r;
        }
 
        /*
        设备激活会创建一个具有指定名称的设备映射器设备。
         */
        r = crypt_activate_by_passphrase(cd,            // crypt 上下文
                                         device_name,   //要激活的设备名称
                                         CRYPT_ANY_SLOT,//密钥槽的使用
                                         "foo", 3,      //密码短语
                                         CRYPT_ACTIVATE_READONLY); //标志位
        if (r < 0) {
                printf("Device %s activation failed.\n", device_name);
                crypt_free(cd);
                return r;
        }
 
        printf("%s device %s/%s is active.\n", crypt_get_type(cd), crypt_get_dir(), device_name);
        printf("\tcipher used: %s\n", crypt_get_cipher(cd));
        printf("\tcipher mode: %s\n", crypt_get_cipher_mode(cd));
        printf("\tdevice UUID: %s\n", crypt_get_uuid(cd));
 
        /*
         获取有关活动设备的信息。
         */
        r = crypt_get_active_device(cd, device_name, &cad);
        if (r < 0) {
                printf("Get info about active device %s failed.\n", device_name);
                crypt_deactivate(cd, device_name);
                crypt_free(cd);
                return r;
        }
 
        printf("Active device parameters for %s:\n"
                "\tDevice offset (in sectors): %" PRIu64 "\n"
                "\tIV offset (in sectors)    : %" PRIu64 "\n"
                "\tdevice size (in sectors)  : %" PRIu64 "\n"
                "\tread-only flag            : %s\n",
                device_name, cad.offset, cad.iv_offset, cad.size,
                cad.flags & CRYPT_ACTIVATE_READONLY ? "1" : "0");
 
        crypt_free(cd);
        return 0;
}
 
static int handle_active_device(const char *device_name)
{
        struct crypt_device *cd;
        int r;
 
        /*
         crypt_init_by_name() 通过活动设备映射器名称初始化上下文。
         */
        r = crypt_init_by_name(&cd, device_name);
        if (r < 0) {
                printf("crypt_init_by_name() failed for %s.\n", device_name);
                return r;
        }
 
        if (crypt_status(cd, device_name) == CRYPT_ACTIVE)
                printf("Device %s is still active.\n", device_name);
        else {
                printf("Something failed perhaps, device %s is not active.\n", device_name);
                crypt_free(cd);
                return -1;
        }
 
        /*
         crypt_deactivate() 用于停用设备
         */
        r = crypt_deactivate(cd, device_name);
        if (r < 0) {
                printf("crypt_deactivate() failed.\n");
                crypt_free(cd);
                return r;
        }
 
        printf("Device %s is now deactivated.\n", device_name);
 
        crypt_free(cd);
        return 0;
}
 
int main(int argc, char **argv)
{
        if (geteuid()) {
                printf("Using of libcryptsetup requires super user privileges.\n");
                return 1;
        }
 
        if (argc != 2) {
                printf("usage: ./crypt_luks_usage <path>\n"
                        "<path> refers to either a regular file or a block device.\n"
                        "       WARNING: the file or device will be wiped.\n");
                return 2;
        }
 
        if (format_and_add_keyslots(argv[1]))
                return 3;
 
        if (activate_and_check_status(argv[1], "example_device"))
                return 4;
 
        if (handle_active_device("example_device"))
                return 5;
 
        return 0;
}

为了便于书写,分析直接以注释的方式写到了代码中间。

三、总结

上面介绍了LUKS加密工具的使用,从代码的角度更深入的了解了cryptsetup。其中,密钥槽是可以动态生成的,而不需要使用人员来维护,在提高安全性的同时,在一定程度减少了工具的使用难度。

 类似资料: