OpenWrt下mac80211内核模块加密密钥加载与使用方法问询
嘿,针对你在OpenWrt mac80211内核模块里处理HMAC-SHA256密钥的问题,我来分享下实际开发中的最佳实践和解决方案:
你提到的两种方式各有优劣,这里再补充几个更适配OpenWrt场景的方案,按灵活性和安全性排序:
方案一:OpenWrt UCI配置+用户空间传递
这是最贴合OpenWrt生态的理想方案:- 在OpenWrt系统配置里(比如
/etc/config/network或自定义配置文件)添加密钥字段,用uci工具统一管理; - 编写一个用户空间初始化脚本(放在
/etc/init.d/下),在模块加载前读取配置中的密钥,通过sysfs或netlink传递给内核模块; - 内核模块中实现对应的sysfs节点或netlink处理器,接收并存储密钥。
优点:无需重刷系统就能修改密钥,配置文件可设置0600权限限制访问,安全性和灵活性都拉满。
- 在OpenWrt系统配置里(比如
方案二:内核模块参数传递
把密钥定义为模块参数,代码示例如下:static u8 hmac_key[32]; module_param_array(hmac_key, byte, NULL, 0400); MODULE_PARM_DESC(hmac_key, "32-byte HMAC-SHA256 key (comma-separated bytes)");加载模块时通过命令行传递密钥:
insmod your_module.ko hmac_key=0xff,0xaa,0x33,0x00,... # 补全32个字节在OpenWrt里可以把这个加载命令写到模块启动脚本中,配合uci配置动态生成参数。优点是不用修改模块代码就能换密钥,缺点是加载时密钥会出现在
dmesg或/proc/modules里,需要严格控制权限。方案三:硬编码密钥
就是你说的直接定义u8 key[32] = {0xff, 0xaa, ...};,优点是实现最简单,缺点是修改密钥必须重新编译模块并重刷系统,仅适合密钥长期固定的场景。如果用这个方案,记得编译时开启内核内存保护,把密钥所在内存页设为只读,防止被篡改。
这完全取决于你的需求场景:
如果密钥仅用于当前模块,无需和其他内核组件共享:直接用
u8数组更简单高效,没必要引入keyring的复杂机制。但要注意内存安全:- 用
set_memory_ro()将密钥所在内存页设为只读; - 绝对禁止在日志或调试信息中泄露密钥;
- 模块卸载时用
explicit_bzero()清空密钥内存(普通memset可能被编译器优化掉)。
- 用
如果需要安全存储、权限控制或跨模块共享密钥:使用Linux内核的keyring机制更合适。内核keyring会自动处理密钥的内存保护和权限校验,防止未授权访问。不过对于mac80211这种场景,keyring会增加额外复杂度,没有明确需求的话不建议用。
示例代码
1. 模块参数加载密钥+内存保护
#include <linux/module.h> #include <linux/mm.h> #include <linux/crypto.h> #include <linux/scatterlist.h> static u8 hmac_key[32]; module_param_array(hmac_key, byte, NULL, 0400); MODULE_PARM_DESC(hmac_key, "32-byte HMAC-SHA256 key (comma-separated bytes)"); static struct crypto_shash *hmac_tfm; static int __init hmac_module_init(void) { int ret; // 简单校验密钥是否有效(可根据需求加强) bool key_empty = true; for (int i = 0; i < 32; i++) { if (hmac_key[i] != 0) { key_empty = false; break; } } if (key_empty) { pr_err("HMAC key not provided\n"); return -EINVAL; } // 将密钥内存设为只读 ret = set_memory_ro((unsigned long)hmac_key, 1); if (ret) { pr_warn("Failed to set key memory read-only\n"); } // 初始化HMAC-SHA256加密上下文 hmac_tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); if (IS_ERR(hmac_tfm)) { pr_err("Failed to allocate HMAC-SHA256 transform\n"); return PTR_ERR(hmac_tfm); } ret = crypto_shash_setkey(hmac_tfm, hmac_key, sizeof(hmac_key)); if (ret) { pr_err("Failed to set HMAC key\n"); crypto_free_shash(hmac_tfm); return ret; } pr_info("HMAC module initialized successfully\n"); return 0; } static void __exit hmac_module_exit(void) { // 清空密钥内存,防止残留 explicit_bzero(hmac_key, sizeof(hmac_key)); // 恢复内存可写(如果之前设置了只读) set_memory_rw((unsigned long)hmac_key, 1); crypto_free_shash(hmac_tfm); } module_init(hmac_module_init); module_exit(hmac_module_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("HMAC-SHA256 module for mac80211");
2. Sysfs接收密钥(简化版)
如果用sysfs传递密钥,模块中可以添加一个sysfs节点:
static ssize_t hmac_key_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { if (count != sizeof(hmac_key)) { return -EINVAL; } memcpy(hmac_key, buf, sizeof(hmac_key)); // 设置内存只读 set_memory_ro((unsigned long)hmac_key, 1); // 重新加载HMAC密钥 crypto_shash_setkey(hmac_tfm, hmac_key, sizeof(hmac_key)); return count; } static struct kobj_attribute hmac_key_attr = __ATTR(hmac_key, 0600, NULL, hmac_key_store); static struct attribute *attrs[] = {&hmac_key_attr.attr, NULL}; static struct attribute_group attr_group = {.attrs = attrs}; static struct kobject *hmac_kobj; // 在init函数中注册sysfs节点 hmac_kobj = kobject_create_and_add("hmac_module", kernel_kobj); if (!hmac_kobj) { return -ENOMEM; } ret = sysfs_create_group(hmac_kobj, &attr_group); if (ret) { kobject_put(hmac_kobj); return ret; }
用户空间可以通过以下命令写入密钥:
echo -n -e '\xff\xaa\x33\x00...' > /sys/kernel/hmac_module/hmac_key
- 密钥加载优先选UCI配置+用户空间传递或模块参数,尽量避免硬编码;
- 密钥使用优先选直接u8数组+内存保护,除非有明确的共享或权限控制需求才用keyring;
- 无论哪种方式,都要严格保障密钥的内存安全,防止泄露或篡改。
内容的提问来源于stack exchange,提问作者Yi Zhao

