文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【数据安全】4. Android 文件级加密(File-based Encryption)之密钥管理

2023-09-06 21:39

关注

在前文《【数据安全】3. Android 文件级加密(File-based Encryption)技术介绍》  中介绍了在HLOS中 FBE 的软件流程,而密钥管理则贯穿于整个流程中。

密钥管理中有以下关键对象:

Encryption StorageMaster KeyEncryption Policy
System DE StorageSystem DE Master KeySystem DE Encryption Policy
User.0 DE StorageUser.0 DE Master KeyUser.0 DE Encryption Policy
User.0 CE StorageUser.0 CE  Master KeyUser.0 CE Encryption Policy

这三者的关系可以用一个例子来说明:

  1. 在 userdata 分区中, 位于 System DE Storage 中的每个文件和文件夹都有自己的 System DE Encryption Policy,被记录到文件的扩展属性中;
  2. System DE Encryption Policy 记录了:
    • 加密使用的 key ,即 System DE Master Key;
    • 文件内容的加密算法;
    • 文件名的加密算法;
  3. 在 I/O 时,底层驱动根据文件或文件夹的 System DE Encryption Policy 加密和解密数据;

System DE Master key、User DE Master key 和 User CE Master key 以及使用这些 key 加解密文件的流程基本一致,它们的主要区别是针对不同安全等级的存储位置而已。但是当用户设置锁屏密码后,User CE Master  key 的认证方式会改变,受用户密码的保护。因此本文只介绍 System DE Master Key 和用户设置锁屏密码后的 User CE Master  Key 。

密钥管理涉及的主要几个模块和相关操作:


1.1 VOLD

Encryption Policy 在代码中的定义:

/ /system/extras/libfscrypt/include/fscrypt/fscrypt.hstruct EncryptionOptions {    int version;    int contents_mode;    int filenames_mode;    int flags;    bool use_hw_wrapped_key;    // Ensure that "version" is not valid on creation and so must be explicitly set    EncryptionOptions() : version(0) {}};struct EncryptionPolicy {    EncryptionOptions options;    std::string key_raw_ref;};
// bionic/libc/kernel/uapi/linux/fscrypt.hstruct fscrypt_policy_v1 {  __u8 version;  __u8 contents_encryption_mode;  __u8 filenames_encryption_mode;  __u8 flags;  __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];};#define FSCRYPT_KEY_DESC_PREFIX "fscrypt:"#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8#define FSCRYPT_MAX_KEY_SIZE 64struct fscrypt_key {  __u32 mode;  __u8 raw[FSCRYPT_MAX_KEY_SIZE];  __u32 size;};#define FSCRYPT_POLICY_V2 2#define FSCRYPT_KEY_IDENTIFIER_SIZE 16struct fscrypt_policy_v2 {  __u8 version;  __u8 contents_encryption_mode;  __u8 filenames_encryption_mode;  __u8 flags;  __u8 __reserved[4];  __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];};struct fscrypt_get_policy_ex_arg {  __u64 policy_size;  union {    __u8 version;    struct fscrypt_policy_v1 v1;    struct fscrypt_policy_v2 v2;  } policy;};

1.2 Keymaster HAL/TA

响应 HLOS 的请求,创建 Master Key 并返回给 HLOS。根据密钥安全原则,Master Key 不允许出现在 HLOS,因此只能加密 Master Key 生成 key blob,再返回给 HLOS


1.3 文件系统和 Encryption Policy

按照前文介绍,不同安全等级的存储位置,将使用不同的 key 来加解密文件。为了达成这一点,kernel 文件系统层支持设置文件的加密上下文环境,即 Encryption Policy,并保存到文件的扩展属性。在 kernel 文档 Documentation/filesystems/fscrypt.rst 有相关介绍:

Encryption context------------------An encryption policy is represented on-disk by struct fscrypt_context_v1 or struct fscrypt_context_v2.  It is up to individual filesystems to decide where to store it, but normally it would be stored in a hidden extended attribute.  It should *not* be exposed by the xattr-related system calls such as getxattr() and setxattr() because of the special semantics of the encryption xattr. (In particular, there would be much confusion if an encryption policy were to be added to or removed from anything other than an empty directory.)  These structs are defined as follows::    #define FSCRYPT_KEY_IDENTIFIER_SIZE  16    struct fscrypt_context_v2 {            u8 version;            u8 contents_encryption_mode;            u8 filenames_encryption_mode;            u8 flags;            u8 __reserved[4];            u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];            u8 nonce[FSCRYPT_FILE_NONCE_SIZE];  };

在 user space,可以使用 ioctl syscall 通过

下面展示了分别从 SYSTEM DE,USER DE/CE 存储路径中得到的 Encryption Policy。从 master_key_identifier 字段可以看出它们确实使用不同的 Master Key 来加密。

# ./fbe-get-policy_static /data/misc[Info]: Detected support for FS_IOC_ADD_ENCRYPTION_KEY[/data/misc]:    .version = 2    .flags   = 0xa    .contents_encryption_mode  = 1   (AES_256_XTS)    .filenames_encryption_mode = 4   (AES_256_CTS)    .master_key_identifier[16] = 599F1AA43727D401198DD6936A1F2060    >>>> SYSTEM DE Policy <<<<# ./fbe-get-policy_static /data/system_de/0[Info]: Detected support for FS_IOC_ADD_ENCRYPTION_KEY[/data/system_de/0]:    .version = 2    .flags   = 0xa    .contents_encryption_mode  = 1   (AES_256_XTS)    .filenames_encryption_mode = 4   (AES_256_CTS)    .master_key_identifier[16] = E0DF36C996839C6F1169863ECF6BBC69    >>>> USER 0 DE Policy <<<<# ./fbe-get-policy_static /data/system_ce/0[Info]: Detected support for FS_IOC_ADD_ENCRYPTION_KEY[/data/system_ce/0]:    .version = 2    .flags   = 0xa    .contents_encryption_mode  = 1   (AES_256_XTS)    .filenames_encryption_mode = 4   (AES_256_CTS)    .master_key_identifier[16] = F676012645AF5CEF378AEF0BF361E821    >>>> USER 0 CE Policy <<<<

疑问:在用户的设备中,文件系统可能存放着数以百万的文件,需要每个文件一一设置 Encryption Policy?

答案肯定不是。在文件系统内支持 Encryption Policy 递归的继承,即目录下的所有文件以及子目录均继承父目录的 Encryption Policy。因此我们只需要手动为有限的目录设置 Encryption Policy,那么在这些目录下创建文件或者子目录时,在 kernel 文件系统层,驱动会帮助我们自动给这些文件或者子目录设置与父目录一致的 Encryption Policy

这意味着 Android  系统只需要设置下面这些目录的 Encryption Policy 即可。

Unencrypted Storage    System DE Storage    User.0 DE Storage    User.0 CE Storage
/data/lost+found
/data/unencrypted
/data/media
/data/system_ce
/data/system_de
/data/misc_ce
/data/misc_de
/data/user
/data/user_de
/data/vendor_ce
/data/vendor_de
/data/misc
/data/local
/data/vendor
/data/property
/data/tombstones
/data/dalvik-cache
/data/resource-cache
/data/backup
/data/system
/data/cache
/data/adb
/data/anr
/data/app
/data/app-asec
/data/app-ephemeral
/data/app-lib
/data/app-private
/data/bootchart
/data/dpm
/data/drm
/data/mediadrm
/data/nfc
/data/ota
/data/ota_package
/data/ss
/data/system_de/0
/data/misc_de/0
/data/vendor_de/0
/data/user_de/0
/data/system_ce/0
/data/misc_ce/0
/data/vendor_ce/0
/data/media/0
/data/data

疑问:在实际代码实现中,谁在哪里设置的呢?

① System DE 路径的 Encryption Policy 是由 init 进程通过 init.rc 设置。

# system/core/rootdir/init.rcmkdir /data/bootchart 0755 shell shell encryption=Requiremkdir /data/vendor 0771 root root encryption=Requiremkdir /data/anr 0775 system system encryption=Requiremkdir /data/tombstones 0771 system system encryption=Requiremkdir /data/misc 01771 system misc encryption=Requiremkdir /data/property 0700 root root encryption=Requiremkdir /data/apex 0755 root system encryption=Nonemkdir /data/apex/decompressed 0755 root system encryption=Requiremkdir /data/app-staging 0751 system system encryption=DeleteIfNecessarymkdir /data/apex/ota_reserved 0700 root system encryption=Requiremkdir /data/local 0751 root root encryption=Requiremkdir /data/preloads 0775 system system encryption=Nonemkdir /data/app-private 0771 system system encryption=Requiremkdir /data/app-ephemeral 0771 system system encryption=Require......

② User DE 和 User CE 路径的 Encryption Policy 是由 vold 进程直接设置。

// system/vold/FsCrypt.cppbool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,      int flags) {    if (flags & android::os::IVold::STORAGE_FLAG_DE) {        // DE_sys key        auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);         auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);         auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);         // DE_n key        auto system_de_path = android::vold::BuildDataSystemDePath(user_id);         auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);         auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);         auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);         if (fscrypt_is_native()) {            EncryptionPolicy de_policy;            if (volume_uuid.empty()) {                if (!lookup_policy(s_de_policies, user_id, &de_policy)) return false;                if (!EnsurePolicy(de_policy, system_de_path)) return false;                if (!EnsurePolicy(de_policy, misc_de_path)) return false;                if (!EnsurePolicy(de_policy, vendor_de_path)) return false;            } else {                if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_policy)) return false;            }            if (!EnsurePolicy(de_policy, user_de_path)) return false;        }    }    if (flags & android::os::IVold::STORAGE_FLAG_CE) {        // CE_n key        auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);         auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);         auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id);         auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);         auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);         if (fscrypt_is_native()) {            EncryptionPolicy ce_policy;            if (volume_uuid.empty()) {                if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) return false;                if (!EnsurePolicy(ce_policy, system_ce_path)) return false;                if (!EnsurePolicy(ce_policy, misc_ce_path)) return false;                if (!EnsurePolicy(ce_policy, vendor_ce_path)) return false;            } else {                if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_policy)) return false;            }            if (!EnsurePolicy(ce_policy, media_ce_path)) return false;            if (!EnsurePolicy(ce_policy, user_ce_path)) return false;        }    }    return true;}

疑问:Encryption Policy 中 master_key_identifier 字段的意义是什么?怎么得到的?

master_key_identifier 即表示使用哪个 Master,至于它怎么来的?怎么根据他找到 Master Key,详见下文。


1.4 Keyring

在 I/O 时,可以通过文件系统,从文件的扩展属性中存储的 Encryption Policy 知道使用哪 Master Key 来加解密文件,即 Encryption Policy 的 master_key_identifier 字段。但这仅仅是 Master  Key  的一个索引,那实际的 Master Key 存在哪里呢?在 FBE 的实现中,这个 Master Key 被存储在 kernel keyring 中,可以通过 master_key_identifier 从 kernel keyring 中找到这个 key。

这里需要注意一点,在使能 wrapped key feture 时,Master Key 不会在 HLOS 中以明文的形式存在,更不会出现在 kernel keyring 中 ,在 keyring 中的实际是 Master Key 的 wrapped key,下文直接称 Wrapped Key。关于 Wrapped key 和 Master Key 的关系,下文会详细介绍。

疑问:Keyring 是一个什么样的存在?

参考内核文档:Documentation/security/keys/core.rst。key 和 keyring 在内核中都是以结构体 struct key (include/linux/key.h)表示。

每个 key 和 keyring 都有这些属性:

可以看到 struct key 自身就有权限控制机制,这为 key 安全使用提供了保障。我们重点关注 3 个属性 key type、key description、payload。

key type

key type 在内核中由 struct key_type 表示。定义了一些可以对该类型的 key 执行的操作函数。

key description

一个可打印的字符串。key type 需要提供了一个操作,在 key 和标准字符串之间进行匹配。也就是可以根据 key description 找到 key。

payload

即 struct key 的有效负荷:

Master Key 被注册到 Keyring 后,它的 key description 中包含了 master_key_identifier ,那也就是说我们可以根据 Encryption Policy 中的 master_key_identifier 找到 Master Key。查找的方法可以从下面代码看出。

// fs/crypto/keyring.cstruct key *fscrypt_find_master_key(struct super_block *sb,    const struct fscrypt_key_specifier *mk_spec){struct key *keyring;char description[FSCRYPT_MK_DESCRIPTION_SIZE];keyring = smp_load_acquire(&sb->s_master_keys);if (keyring == NULL)return ERR_PTR(-ENOKEY); format_mk_description(description, mk_spec);return search_fscrypt_keyring(keyring, &key_type_fscrypt, description);}static struct key *search_fscrypt_keyring(struct key *keyring,  struct key_type *type,  const char *description){key_ref_t keyref = make_key_ref(keyring, true );keyref = keyring_search(keyref, type, description, false);if (IS_ERR(keyref)) {if (PTR_ERR(keyref) == -EAGAIN ||     PTR_ERR(keyref) == -EKEYREVOKED) keyref = ERR_PTR(-ENOKEY);return ERR_CAST(keyref);}return key_ref_to_ptr(keyref);}
// include/uapi/linux/fscrypt.hstruct fscrypt_policy_v2 {__u8 version;__u8 contents_encryption_mode;__u8 filenames_encryption_mode;__u8 flags;__u8 __reserved[4];__u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];};struct fscrypt_key_specifier {__u32 type;__u32 __reserved;union {__u8 __reserved[32]; __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];__u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];} u;};

通过

可以很好理解通过 master_key_identifier 找到 Master Key。


2.1 创建 System DE Master Key 流程

下图展示了在设备第一次启动时,创建 System DE Master Key 的完整流程,在这个章节会对其拆分,分别介绍。

System DE Master Key 创建流程

2.1.1 创建 System DE Master Key

在 vold 函数 fscrypt_initialize_systemwide_keys 中,Vold 通过 keymaster HAL 向 keymaster TA 请求创建 Master Key。在请求参数中, km::TAG_STORAGE_KEY 表明了创建一个用于存储器加密的 Master Key,HLOS 将这个 key 用作 System DE Master Key。

这里要注意 keymaster TA 不会直接将 Master Key 的明文直接返回到 HLOS,而是 Master Key Blob。那么 Master Key Blob 是怎么生成的?

  1. KM TA 首选创建一个 AES 算法加密 key,作为 Master Key;
  2. 使用由 SHK 派生出来的 KEK 将 Master Key 加密,并把密文存放到特殊数据结构,即 key blob;
  3. 再使用由 SHK 派生出来的 HMAC Key 通过 HMAC 算法对 key blob 签名;
  4. 这样就生成了 Master Key Blob,返回到 HLOS 的 Vold;

疑问:SHK 是什么?KEK 是什么?HMAC Key 是什么?

① SHK 是设备使能 secure boot 后,生成的一个每个设备唯一、软件或者固件无法导出的 key,TZ 可以从 SHK 派生出各种用途的 key。

② KEK 派生自 SHK ,用于加密 KM TA 生成的 key,因为 key 不允许暴露在 HLOS:

③ HMAC Key 派生自 SHK ,用于对 key blob 签名,保证了  key blob 不被恶意篡改或者检查是否损坏;

疑问:为什么 TZ 要将 key 加密后返回 HLOS 呢?

在实际用户场景中,各种各样的进程可能会创建几十个甚至几百个用于各种各样的任务的 key,加密后返回 HLOS,由使用者自行管理,那么 TZ 就无需维护较大的存储区域以及这些 key 与客户端的联系。


2.1.2 HLOS 把 System DE Master Key Blob 加密后保存到文件系统

在上一个步骤中,KM TA 创建了 Master Key ( km::TAG_STORAGE_KEY FBE),并以 key blob 的形式返回到 HLOS。在 vold 收到 Master Key Blob 后,又将这个 key blob 加密后保存到文件系统中。

创建  KSK 和加密 Master Key 流程图

疑问:Master Key Blob 已经是密文了,为什么 vold 还要将 key blob 二次加密?

这个操作似乎有点多余,如果对 key 加密一次不安全,那么加密两次也不见得更安全,或者为什么不加密更多次呢?

这里我们要从 FBE 的设计来看,Google 把 userdata 分区划分了不同安全等级的存储位置,但仅使用不同的密钥并不能体现出安全等级这个概念,而是要从对密钥的约束来体现,比如经过什么样的认证后,哪些位置的数据才允许访问,言下之意就是对应的加密密钥才允许被使用。那怎么认证呢?

System DE Master Key 的安全认证正是通过二次加密来体现的,但是加密 Master Key 的 key (KEK) 怎么做到这一点呢?System DE 的设计初衷就是相应的存储位置是设备绑定,即这些存储位置的数据只能在特定的设备(CPU、存储器绑定)上才能解密和访问。Master Key 只负责用户加解密文件的数据,但是特定的设备怎么保证呢?

这就是加密 Master Key 的 key 要达成的目标,我们把这个 key 称作 KSK(Key Strorage Key,这不是专有名词,而是按照 Google 代码变量名来命名 )。

从流程图中可以看出,在创建 KSK 时需要指定 app id(即表明谁创建的?)。同样后续使用这个 key 时,也必须指定相同的 appid。appid 的组成中包括设备绑定的信息(每台设备,KSK 的 appid 不一样,因为从它的创建流程可以看出,包括两个随机数 Secdiscardable 和 storage_binding_info.seed)。不仅如此,在 KM TA 内部还会将 secure boot 状态、设备锁状态、安全补丁日期等等信息和 KSK 绑定,在使用 KSK 之前,会校验相关的信息是否严格匹配,只有完全满足后,KSK 才允许用于解密 Master Blob。这些约束的实现都是在 KM TA 完成的。

这里举几个实际的例子说明:

例1:把一台设备的存储器所有数据,dump 到另外一台完全一致的设备上,用户数据为什么无法解密?

        因为 KSK 已经和 secure boot 状态绑定,在另外一台设备上,无法知道 EKE,进而无法解密 KSK blob,KSK 即无法被使用。当然被 KSK 加密的 master key blob 也无法解密,自然获取不到 master key 。实际实现不只如此简单,这里不做详细介绍。

例2:同一台设备,如果 OTA 更新到安全补丁日期升级的软件后,再回滚到旧软件版本,用户数据为什么无法解密?

        因为 KSK 已经和安全补丁日期绑定,安全补丁日期不能减小。安全补丁日期回滚后,KSK 将不可用。

例3:设备第一次开机是在设备锁(也叫 fastboot锁)锁定的情况下开机,那么 unlock 设备锁后,用户数据为什么无法解密?

        因为 KSK 和设备锁的状态绑定,如果设备锁的状态出现反转,KSK 将不可用。

疑问:加密 Master Key Blob 的 key KSK 从何而来 ?

和 Master Key 类似,都是由 KM TA 创建的,并以 key blob 的形式返回 HLOS,由 vold 自行管理。这里注意创建 key 时,参数 km::AuthorizationSet 不一样,这就导致在 KM TA 内部,它们的管理和用途都是不一样。

vold 请求 KM TA 创建 KSK 所使用的 appid的参数、KSK Blob、加密后的 Master Key Blob 被存储到 /data/unencrypted/key/:

# ls /data/unencrypted/key/ -ltotal 16-rw------- 1 root root   268 1970-01-03 04:47 encrypted_key-rw------- 1 root root   194 1970-01-03 04:47 keymaster_key_blob-rw------- 1 root root 16384 1970-01-03 04:47 secdiscardable-rw------- 1 root root    10 1970-01-03 04:47 stretching-rw------- 1 root root     1 1970-01-03 04:47 version

疑问:第一次开机时,为什么要把 KSK blob  和 加密后的 Master Key Blob 保存到文件系统?

后续每次开机,可以直接通过 KSK blob (/data/unencrypted/key/keymaster_key_blob)解密Master Key Blob 的密文(/data/unencrypted/keyencrypted_key)得到 Master Key Blob,这个动作是在 TZ 内部完成的,有了 Master Key Blob,那么可以将 Master Key 安装到 kernel keyring,详见下文。


2.1.3 安装 Master Key 到 kernel keyring

在上一步骤中,已经成功创建了 Master Key,那接下来就是要把 Master Key 注册到 kernel keyring。在文件 I/O 时可以通过  Encryption Policy 的 master_key_identifier 字段从 kernel keyring 中找到 Master Key。

在这个步骤中的两个重点操作:

  1. 安装 Master Key 到 kernel keyring;
  2. 生成 Master Key 的 master_key_identifier;

软件流程图如下所示:

安装 master key 和生成 master_key_identifier

流程图比较复杂,接下来根据疑问来分解图中的步骤。

疑问: Master Key 不是不允许暴露在 HLOS 吗?那注册到 kernel keyring 的 Master Key 从何而来?

实际上是 Master Key 的 Wrapped Key 被注册到 kernel keyring ,毕竟 Master Key 确实不能暴露在 HLOS。那 Wrapped Key 从何而来?

生成 Master key 的 ​​​​​​Wrapped Key 并注册

 从图中可知,生成 Wrapped Key 的过程:

  1. vold 把 Master Key Blob 传送到 KM TA,请求创建 Wrapped Key;
  2. KM TA 使用 HMAC Key 校验 Master Key Blob ;
  3. KM TA 使用 KEK 解密 Master Key Blob 得到 Master Key;
  4. KM TA 使用 Ephemeral Key 通过 AES Wrap Key RFC3394 Algorithm 生成 Master Key 的 Wrapped Key;
    1. Ephemeral Key:从 SHK  派生而来,但是每次设备重启(cold reboot)都会改变,意味着 Wrapped Key 每次设置重启都不一样;
    2. AES Wrap Key RFC3394 Algorithm:详见 Advanced Encryption Standard (AES) Key Wrap Algorithm
  5. KM TA 将生成的 Wrapped Key 返回 HLOS vold;
  6. Vold 通过系统调用 ioctl  FS_IOC_ADD_ENCRYPTION_KEY,将 Wrapped Key 安装到 Kernel Keyring
  • 相反,KM TA 可以使用 Ephemeral Key 通过 AES Unwrap Key RFC3394 Algorithm  将 Wrapped Key 转成 Master Key。因此后面 HLOS 再软件流程中可以把  Wrapped Key 当作 Master Key,在实际执行操作时,KM TA 会将 Wrapped Key 自动转换成 Master Key 后再执行;
  • AES Wrap Key RFC3394 Algorithm 是什么?可以简单的理解为 AES 算法是专门用于数据加解密,而 AES Wrap Key RFC3394 Algorithm 专门用于加解密 AES Key;

疑问:master_key_identifier 是怎么产生的?怎么通过 master_key_identifier 找到 Master Key?

首先给出一张软件流程图,可以看到 master_key_identifier  是在安装 Wrapped Key 的过程中生成的,并且随系统调用返回到 vold 进程。

生成 master_key_identifier

 生成 master_key_identifier  的步骤有:

  1. Kernel 函数 fscrypt_derive_raw_secret 将 Wrapped Key 发送到 TZ KM TA,请求 Master Key 派生 raw secret ;
  2. KM TA 接收到请求后,使用 Ephemeral Key 通过 Unwrap Wrapped Key 得到 Master Key;
  3. KM TA 通过 KDF ,使得 Master Key 派生出一个 key,并将这个派生 key 返回 HLOS Linux kernel
  4. Linux Kernel 使用从 TZ 返回的派生 key 作为 KDF key,继续通过 KDF 派生出一个 key,这个 key 作为 master_key_identifier;

这里截取了一些在 ioctl 中生成 master_key_identifier 的相关代码:

https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:system/vold/KeyUtil.cpphttps://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:system/vold/KeyUtil.cpp

// system/vold/FsCrypt.cppstatic bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options,    const KeyBuffer& key, EncryptionPolicy* policy) {    KeyBuffer ephemeral_wrapped_key;    if (options.use_hw_wrapped_key) {        if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) {            LOG(ERROR) << "Failed to get ephemeral wrapped key";            return false;        }    }    return installKey(mountpoint, options, options.use_hw_wrapped_key ? ephemeral_wrapped_key : key,                      policy);}// system/vold/KeyUtil.cppbool installKey(const std::string& mountpoint, const EncryptionOptions& options,                const KeyBuffer& key, EncryptionPolicy* policy) {    policy->options = options;    // Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we    // have to copy the raw key into it.    KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);    struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();    // Initialize the "key specifier", which is like a name for the key.    switch (options.version) {        case 2:            // A key for a v2 policy is specified by an 16-byte "identifier",            // which is a cryptographic hash of the key itself which the kernel            // computes and returns.  Any user-provided value is ignored; we            // just need to set the specifier type to indicate that we're adding            // this type of key.            arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;            break;    }    arg->raw_size = key.size();    memcpy(arg->raw, key.data(), key.size());    if (!installFsKeyringKey(mountpoint, options, arg)) return false;    if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {        // Retrieve the key identifier that the kernel computed.        policy->key_raw_ref =                std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);    }    std::string ref = keyrefstring(policy->key_raw_ref);    LOG(DEBUG) << "Installed fscrypt key with ref " << ref << " to " << mountpoint;    if (!installProvisioningKey(key, ref, arg->key_spec)) return false;    return true;}

 kernel 从函数  int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) 开始:

// fs/crypto/keyring.cint fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) {struct super_block *sb = file_inode(filp)->i_sb;struct fscrypt_add_key_arg __user *uarg = _uarg;struct fscrypt_add_key_arg arg;struct fscrypt_master_key_secret secret;    copy_from_user(&arg, uarg, sizeof(arg)    memset(&secret, 0, sizeof(secret))    secret.is_hw_wrapped = true    secret.size = arg.raw_size    copy_from_user(secret.raw, uarg->raw, secret.size)    add_master_key(sb, &secret, &arg.key_spec)if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER &&    copy_to_user(uarg->key_spec.u.identifier, arg.key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE))}#define RAW_SECRET_SIZE 32static int add_master_key(struct super_block *sb,  struct fscrypt_master_key_secret *secret,  struct fscrypt_key_specifier *key_spec){int err;if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {u8 _kdf_key[RAW_SECRET_SIZE];u8 *kdf_key = secret->raw;unsigned int kdf_key_size = secret->size;if (secret->is_hw_wrapped) {kdf_key = _kdf_key;kdf_key_size = RAW_SECRET_SIZE;err = fscrypt_derive_raw_secret(sb, secret->raw,secret->size,kdf_key, kdf_key_size);if (err)return err;}err = fscrypt_init_hkdf(&secret->hkdf, kdf_key, kdf_key_size);memzero_explicit(kdf_key, kdf_key_size);if (err)return err;err = fscrypt_hkdf_expand(&secret->hkdf,  HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0,  key_spec->u.identifier,  FSCRYPT_KEY_IDENTIFIER_SIZE);if (err)return err;}return do_add_master_key(sb, secret, key_spec);}

add_master_key 的实现中,3个重要函数:

从上图中可以看到,当 ioctl 返回时,master_key_identifier 也会随之返回,最终保存到文件 /data/unencrypted/ref 文件。

疑问:System DE Master Key 的 master_key_identifier 为什么被保存到 /data/unencrypted/ref (其他 User DE/CE 的 master_key_identifier 并不会被保存到文件)?

因为创建和产生 master_key_identifier 的 流程是在 vold 进程中,而创建 System Device Encrypted (DE) Storage 并给目录设置 Encryption Policy 是 init 进程通过 init.rc 完成的。Google 通过文件存储的方式,让 init 可以得到  System DE Master Key 的 master_key_identifier。

疑问:Wrapped Key 是怎么被安装到 keyring 上的?

这就进入到函数 do_add_master_key 中,观察参数列表:

// fs/crypto/keyring.cstatic int do_add_master_key(struct super_block *sb,     struct fscrypt_master_key_secret *secret,     const struct fscrypt_key_specifier *mk_spec)

到这里,可以先返回上文 1.4 Keyring 章节再次回顾以下 keyring 相关的属性再继续。

直接看 do_add_master_key 执行结果最终示意图(下图是 add 两个 Master Key 的示意图):

 Install Master Key to Keyring

① Userdata 分区 superblock 中,指针 s_master_keys 指向一个 key_type_keyring 类型的 keyring;

②  类型为 key_type_keyring 类型的 keyring

我们将其称为 master keys keyring。从代码中可以看出,该 keyring 的 description 中包括了分区的 Informational name。

// /fs/crypto/keyring.cstatic void format_fs_keyring_description(char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE],const struct super_block *sb){sprintf(description, "fscrypt-%s", sb->s_id);}

可以在 /proc/keys 节点看到该 keyring 的信息:

# cat /proc/keys0baa3b23 I------     1 perm 080b0000     0     0 keyring   fscrypt-dm-8: 6

我们可以将 Userdata 有关的所有 Master Key 链接到该 keyring上,正如上图所示,链接了 2 个 Master Key。

③ 链接到 master keys keyring 上的一个类型为 key_type_fscrypt 的 key

这个 key 的 description 如下所示,可以看到 description 就是 master_key_identifier,意味着后续我们可以根据  master_key_identifier 从 keyring s_master_keys 上找到它。

static void format_mk_description(char description[FSCRYPT_MK_DESCRIPTION_SIZE],const struct fscrypt_key_specifier *mk_spec){sprintf(description, "%*phN",master_key_spec_len(mk_spec), (u8 *)&mk_spec->u);}

可以在 /proc/keys 节点看到该 key 的信息:

# cat /proc/keys26066299 I------  1214 perm 08090000     0     0 ._fscrypt f676012645af5cef378aef0bf361e821

从上图可以看到这个 key 的 payload 指向 struct fscrypt_master_key,里面存储着 Master Key(严格讲是 Wrapped Key);

④ struct fscrypt_master_key,包含三个重要成员:

⑤ mk_users 指向的类型为 key_type_keyring 的 keyring

我们称其为 mk_users keyring。

在多用户场景下,所有安装和共享此 Master Key 的用户,都分别创建一个类型 key_type_fscrypt_user 的 key 链接到 mk_users keyring。作用:跟踪有哪些用户使用此 Master Key,只有所有用户都 remove 此 Master Key,才允许真正 remove 该 Master Key。

mk_users keyring 的 description 如下表示:

static void format_mk_users_keyring_description(char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE],const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]){sprintf(description, "fscrypt-%*phN-users",FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier);}

 可以在 /proc/keys 节点看到该 keyring 的信息:

# cat /proc/keys0a4c3a54 I------     1 perm 080b0000     0     0 keyring   fscrypt-f676012645af5cef378aef0bf361e821-users: 1

⑥ 类型为 key_type_fscrypt_user 的 key

被链接到 mk_users keyring 上,表明哪个用户正在使用此 Master Key。key 的 description 如下表示:

static void format_mk_user_description(char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE],const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]){sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE,mk_identifier, __kuid_val(current_fsuid()));}

注意看代码,uid 是该 key 的 description 一部分。在 /proc/keys 节点中看到该 key 的信息:

# cat /proc/keys2f06f609 I--Q---     1 perm 08010000     0     0 .fscrypt  f676012645af5cef378aef0bf361e821.uid.0

总结,在执行完  do_add_master_key 函数后:


2.2 非第一次启动,vold 加载和安装 System DE Master Key

在第一次设备启动时, vold 创建了 System DE Master Key,并将 key 相关的信息存储到文件系统:

# ls /data/unencrypted/key/ -ltotal 16-rw------- 1 root root   268 1970-01-03 04:47 encrypted_key-rw------- 1 root root   194 1970-01-03 04:47 keymaster_key_blob-rw------- 1 root root 16384 1970-01-03 04:47 secdiscardable-rw------- 1 root root    10 1970-01-03 04:47 stretching-rw------- 1 root root     1 1970-01-03 04:47 version

在后续每次启动时,vold 加载 /data/unencrypted/key/ 目录下的文件,根据下面图示流程,即可得到 System DE Master Key 的 Wrapped Key,并将其安装到 kernel keyring。

非第一次启动, vold 加载和安装 System DE Master Key

2.3 创建和校验 System DE Storage

到此密钥准备已经完成:

接下来就是在 init.rc 中为 System DE Storage 的目录设置 Encryption Policy。

# system/core/rootdir/init.rcmkdir /data/bootchart 0755 shell shell encryption=Requiremkdir /data/vendor 0771 root root encryption=Requiremkdir /data/anr 0775 system system encryption=Requiremkdir /data/tombstones 0771 system system encryption=Requiremkdir /data/misc 01771 system misc encryption=Requiremkdir /data/property 0700 root root encryption=Requiremkdir /data/apex 0755 root system encryption=Nonemkdir /data/apex/decompressed 0755 root system encryption=Requiremkdir /data/app-staging 0751 system system encryption=DeleteIfNecessarymkdir /data/apex/ota_reserved 0700 root system encryption=Requiremkdir /data/local 0751 root root encryption=Requiremkdir /data/preloads 0775 system system encryption=Nonemkdir /data/app-private 0771 system system encryption=Requiremkdir /data/app-ephemeral 0771 system system encryption=Require

注:一定得是在 init.rc 中创建 System DE Storage,因为 init.rc 中的  mkdir 指令被封装,不仅支持创建文件夹,还会设置或校验 Encryption Policy。

 当执行 init.rc 中 mkdir 指令时,mkdir 先确认文件夹已经存在,接着设置或校验 Encryption Policy:

前文提到过,如果 mkdir 设置 Encryption Policy 失败,那么会自动 reboot 到 recovery,强制格式化 userdata 分区。这样安全性很高,但是一旦出现问题,代价也很高,会造成用户数据丢失。

比如我们创建一个保存 log 的文件夹 /data/log,从安全的角度来讲,该文件夹的内容需要加密,但是对用户来说,log 数据并不重要。在上述的流程中,这个文件夹的 Encryption Policy 一旦异常,则导致用户数据全部丢失,是非常不合理的。为了解决类似问题,在 Android 11 中,Google 在 mkdir 中引入参数 'encryption',可以更灵活的控制不同文件夹的 Encryption Policy 设置和校验策略:

对于上述的 /data/log 文件夹的处理,我们就可以使用:

mkdir /data/log 0771 system system encryption=Attempt

或者

mkdir /data/log 0771 system system encryption=DeleteIfNecessary

下面是 mkdir 的代码实现截取,详见 system/core/init/builtins.cpphttps://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:system/core/init/builtins.cpp;bpv=0;bpt=1

const BuiltinFunctionMap& GetBuiltinFunctionMap() {    constexpr std::size_t kMax = std::numeric_limits::max();    // clang-format off    static const BuiltinFunctionMap builtin_functions = {        {"mkdir",                   {1,     6,    {true,   do_mkdir}}},        // TODO: Do mount operations in vendor_init.    }}// mkdir  [mode] [owner] [group] [

2.4 一切就绪

到此为止,System DE Master Key 以及 System DE Storage 已经就绪:

可以从下面的信息看出:

# xxd /data/unencrypted/ref00000000: 599f 1aa4 3727 d401 198d d693 6a1f 2060  Y...7'......j. `
# ./fbe-get-policy_static /data/misc[Info]: Detected support for FS_IOC_ADD_ENCRYPTION_KEY[/data/misc]:    .version = 2    .flags   = 0xa    .contents_encryption_mode  = 1   (AES_256_XTS)    .filenames_encryption_mode = 4   (AES_256_CTS)    .master_key_identifier[16] = 599F1AA43727D401198DD6936A1F2060    >>>> SYSTEM DE Policy <<<<
# cat /proc/keys0baa3b23 I------     1 perm 080b0000     0     0 keyring   fscrypt-dm-8: 60f65b28e I------   904 perm 08090000     0     0 ._fscrypt 599f1aa43727d401198dd6936a1f20602db96af7 I------     1 perm 080b0000     0     0 keyring   fscrypt-599f1aa43727d401198dd6936a1f2060-users: 137df89e6 I--Q---     1 perm 08010000     0     0 .fscrypt  599f1aa43727d401198dd6936a1f2060.uid.00903c23d I--Q---     1 perm 39010000     0     0 fscrypt-p 599f1aa43727d401198dd6936a1f2060: 108 [2]

接下来,在 System DE Storage 创建的任何文件或者文件夹,都会自动继承父文件夹的 Encryption Policy。

在 I/O 时,kernel 文件系统会根据 Encryption Policy 的 master_key_identifier,从 kernel keyring 中找到 Master Key,并随 BIO 传递到底层,对数据加密和解密。


当用户设置用户锁屏密码后, User CE Master  key 的认证方式会改变,受用户密码保护,只有密码校验成功后,才能得到 User.0 CE Master Key,并将其安装到 kernel keyring。

User.0 CE Master Key 相关的认证数据被存储到目录 /data/misc/vold/user_keys/ce/0/current 下。我们对比一下未设置锁屏密码和设置锁屏密码后,该目录下的文件差异:

> adb shell ls /data/misc/vold/user_keys/ce/0/current -l-rw------- 1 root root   268 2022-11-05 22:08 encrypted_key-rw------- 1 root root   194 2022-11-05 22:08 keymaster_key_blob-rw------- 1 root root 16384 2022-11-05 22:08 secdiscardable-rw------- 1 root root    10 2022-11-05 22:08 stretching-rw------- 1 root root     1 2022-11-05 22:08 version
> adb shell ls /data/misc/vold/user_keys/ce/0/current -l-rw------- 1 root root   268 2022-11-05 22:11 encrypted_key-rw------- 1 root root 16384 2022-11-05 22:11 secdiscardable-rw------- 1 root root     4 2022-11-05 22:11 stretching-rw------- 1 root root     1 2022-11-05 22:11 version

可以看到设置锁屏密码后,少了文件 keymaster_key_blob。在介绍 System DE Master Key 时已经知道,keymaster_key_blob 是 KM TA 创建的一个用于加密 Master Key Blob 的 key(上文称其为 KSK),用于保证 Master Key Blob 的安全性,作为设备绑定认证的一种实现,即 KSK 只能在唯一的设备(和CPU、存储器等器件绑定)上可用。

在用户设置锁屏密码后,认证更加严格,不仅要唯一的设备,而且需要用户输入正确的锁屏密码。因此,Master Key Blob  不再用 KSK 加密,它已经不能满足需求。

疑问:encrypted_key 仍然存在,表明仍然使用对 Master Key Blob 加密的方式实现认证。但是加密 Master Key Blob 的 key 从哪里来?和用户锁屏密码存在什么关系?

我们先看以下用户设置锁屏密码时,导致 /data/misc/vold/user_keys/ce/0/current 下文件内容变化的流程:

AuthenticationToken 的实现:

// frameworks/base/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.javastatic class AuthenticationToken {private final byte mVersion;private @NonNull byte[] mSyntheticPassword;private @Nullable byte[] mEncryptedEscrowSplit0;private @Nullable byte[] mEscrowSplit1;AuthenticationToken(byte version) {mVersion = version;}private byte[] deriveSubkey(byte[] personalization) {if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {return (new SP800Derive(mSyntheticPassword)).withContext(personalization, PERSONALISATION_CONTEXT);} else {return SyntheticPasswordCrypto.personalisedHash(personalization,mSyntheticPassword);}}public byte[] deriveDiskEncryptionKey() {return deriveSubkey(PERSONALIZATION_FBE_KEY);}private void recreate(byte[] escrowSplit0, byte[] escrowSplit1) {mSyntheticPassword = bytesToHex(SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_SPLIT, escrowSplit0, escrowSplit1));}public void setEscrowData(@Nullable byte[] encryptedEscrowSplit0,@Nullable byte[] escrowSplit1) {mEncryptedEscrowSplit0 = encryptedEscrowSplit0;mEscrowSplit1 = escrowSplit1;}static AuthenticationToken create() {AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);byte[] escrowSplit0 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);byte[] escrowSplit1 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);result.recreate(escrowSplit0, escrowSplit1);byte[] encrypteEscrowSplit0 = SyntheticPasswordCrypto.encrypt(result.mSyntheticPassword,PERSONALIZATION_E0, escrowSplit0);result.setEscrowData(encrypteEscrowSplit0,  escrowSplit1);return result;}}

结合代码和流程图可以看到,用户设置密码后,框架会直接调到 vold 执行更换 MasterKey 认证方式:

  1. 调用函数 fscrypt_add_user_key_auth(user_id, serial, secret_hex),增加对 CE Master Key 的认证方式:
    1. 首先,加载 keymaster_key_blob ,使用 KSK 解密 encrypted_key,得到 Master CE Key Blob;
    2. 接着,生成新的 appid,appid 由 fscrypt_add_user_key_auth 传入的参数 secret_hex 组成;
    3. 将 appid Hash 成 AES Key;
    4. 使用生成的 AES Key 加密  Master CE Key Blob,得到 encrypted_key;
    5. 将新的认证数据存储到 /data/misc/vold/user_keys/ce/0/cx0000000000 下;
    6. 此时 /data/misc/vold/user_keys/ce/0 下已经存在两种 CE Master Key 的认证方式,如下所示:
      # 老的认证方式,通过 KM key KSK 认证> adb shell ls /data/misc/vold/user_keys/ce/0/currentencrypted_keykeymaster_key_blobsecdiscardablestretchingversion# 新的认证方式,通过用户密码校验认证> adb shell ls /data/misc/vold/user_keys/ce/0/cx0000000000encrypted_keysecdiscardablestretchingversion
  2. 调用 fscrypt_fixate_newest_user_key_auth,清除老的认证方式,改用用户密码认证,完成后,/data/misc/vold/user_keys/ce/0/cx0000000000 的内容将覆盖 /data/misc/vold/user_keys/ce/0/current。vold 会打出如下的 log :
    vold    : fscrypt_fixate_newest_user_key_auth 0vold    : Deleting key /data/misc/vold/user_keys/ce/0/current/keymaster_key_blob from Keymastervold    : /system/bin/secdiscardvold    :     --vold    :     /data/misc/vold/user_keys/ce/0/current/encrypted_keyvold    :     /data/misc/vold/user_keys/ce/0/current/secdiscardablevold    :     /data/misc/vold/user_keys/ce/0/current/keymaster_key_blobvold    : /system/bin/rmvold    :     -rfvold    :     /data/misc/vold/user_keys/ce/0/currentvold    : Renaming /data/misc/vold/user_keys/ce/0/cx0000000000 to /data/misc/vold/user_keys/ce/0/current

从上面流程可以看到,函数 fscrypt_add_user_key_auth(user_id, serial, secret_hex)中的参数 secret_hex 是关键,可以认为 secret_hex 被用于加密 Master CE Key Blob。

讨论:用户密码认证的方式并不是很完美,有很大的优化空间:

  secret_hex 的值每次都是固定的,这意味着某个环节存在漏洞导致 secret_hex 泄露,那么很轻易的得到 Master CE Key Blob,导致用户密码认证的方式彻底失效;

即使用户更换密码或者关闭密码后重新设置密码,secret_hex 也不会改变,因为 SyntheticPassword 是固定的,导致风险大大增加;


FBE 密钥管理就介绍到这里,到此,System DE Master Key 、User DE/CE Master Key 已经就绪,System DE Storage、User DE/CE Storage 也已经就绪。在 I/O 时,底层文件系统、通用块层、UFS 驱动、ICE 驱动将配合完成数据流式加解密。在后文《【数据安全】5. Android 文件级加密(File-based Encryption)之高效 I/O》 详细介绍。


(*^_^*)有任何疑问,除了评论区还可以 对我发起悬赏提问 获取快速和高质量的回复 (*^_^*)

来源地址:https://blog.csdn.net/cs_tech/article/details/127593457

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯