2021SC@SUDSC
为了更深一步了解cryptsetup加密的工作原理,找到了cryptsetup LUCK设备使用的相关代码——crypt_luks_usage.c。主要包含以下几个函数:
下面先一一给出简单介绍
每次需要使用 cryptsetup 或 dmcrypt device 执行某些操作时,都需要一个有效的上下文。开始工作的第一步是 crypt_init() 调用。一般使用块设备的路径或常规文件的路径来调用它。如果不提供路径,则初始化空上下文。
相互设备上的标头和有效载荷,由于下面的函数是基于LUKS设备进行的,所以使用格式操作在上下文中设置设备类型,以防LUKS标头写入块设备的开头。下面代码的具体使用是LUKS标头和数据都存储在同一设备上的场景。除此之外,还可以单独存储标题和数据。
但是,有一点需要注意,crypt_format()是破坏性操作,它会覆盖部分支持块设备。
在成功对LUKS设备进行crypt_format() 操作后,密钥不会以持久方式存储在设备上,密钥槽区域是一个超出 LUKS 标头的数组,其中密钥使用用户输入密码以加密形式存储。创建新的密钥槽有两种基本方法,除了这一种外,下面也是一种。
这一种通过加密存储在设备上下文中的 volume_key 直接创建一个新的密钥槽。如果密码短语参数为 NULL,则应提供密码短语或提示用户。
通过打开现有的活动密钥槽,从中提取密钥并将其存储到由新密码保护的新密钥槽中,为密钥创建新的密钥槽。
函数将标头从支持块设备加载到设备上下文中。
通过用户为包含 volume_key 的密钥槽提供的密码激活加密设备。如果keyslot参数设置为CRYPT_ANY_SLOT,那么。就会查找密钥槽都试图直到volume key被找到。
此调用返回包含活动设备的运行时属性的结构。
如果需要对活动设备(已经具有其相应映射的设备)进行操作并且错过了存储在 *crypt_device 引用中的有效设备上下文,那么,可以使用此调用。函数尝试从 DM 获取后备设备的路径,为其初始化上下文并加载 LUKS 标头。
停用加密设备(删除 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。其中,密钥槽是可以动态生成的,而不需要使用人员来维护,在提高安全性的同时,在一定程度减少了工具的使用难度。