blob: e1ecf38cae892c5104533c364e0a7fac7cad4b98 [file] [log] [blame]
<html devsite><head>
<title>元数据加密</title>
<meta name="project_path" value="/_project.yaml"/>
<meta name="book_path" value="/_book.yaml"/>
</head>
<body>
<!--
Copyright 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
Android 7.0 及更高版本支持<a href="/security/encryption/file-based">文件级加密</a> (FBE)。采用 FBE 时,可以使用不同的密钥对不同的文件进行加密,并且可以对加密文件进行单独解密。这些密钥可用于加密文件内容和文件名。采用 FBE 时,其他信息(例如目录布局、文件大小、权限和创建/修改时间)不会被加密。这些统称为“文件系统元数据”。
</p>
<p>
Android 9 引入了对存在硬件支持的元数据加密的支持。借助元数据加密,启动时出现的单个密钥会加密未通过 FBE 进行加密的任何内容。该密钥受到 Keymaster 的保护,而 Keymaster 受到验证启动的保护。
</p>
<h2 id="implementation">实现</h2>
<p>
您可以在搭载 Android 9 的新设备上设置元数据加密,只需设置元数据文件系统,更改 init 序列,然后在设备的 fstab 文件中开启元数据加密即可进行设置。
</p>
<h3 id="hardware-requirements">硬件要求</h3>
<p>
元数据加密只能在数据分区首次进行格式化时设置。因此,该功能仅适用于新设备;OTA 不应更改此设置。
</p>
<p>
为了支持当前的元数据加密,您的硬件必须支持使用<a href="https://blog.google/topics/connected-workspaces/pixel-security-better-faster-stronger/">内嵌加密引擎</a>进行文件级加密。<code>fstab.hardware</code> 中的用户数据分区的 <code>fileencryption=ice</code> 指令指明了这一点。
</p>
<p>
此外,内核中必须存在并启用 <code>dm-default-key</code> 模块。
</p>
<h3 id="set-up-metadata-filesystem">设置元数据文件系统</h3>
<p>
由于在元数据加密密钥出现之前,用户数据分区中的所有内容均无法读取,因此分区表必须留出一个名为“元数据分区”的单独分区,用于存储保护该密钥的 Keymaster Blob。该元数据分区的大小应为 16MB。</p>
<p>
<code>fstab.hardware</code> 必须针对在该分区上的元数据系统纳入一个条目,并将其装载到 <code>/metadata</code>(包括 <code>formattable</code> 标记),以确保在启动时对其进行格式化。f2fs 文件系统不适用于较小的分区;在较小分区中,我们建议您改为使用 ext4。例如:</p>
<pre class="prettyprint">/dev/block/bootdevice/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard wait,check,formattable</pre>
<p>要确保 <code>/metadata</code> 装载点存在,请在 <code>BoardConfig-common.mk</code> 中添加下面这行代码:</p>
<pre class="prettyprint">BOARD_USES_METADATA_PARTITION := true</pre>
<h3 id="changes-to-the-init-sequence">更改 init 序列</h3>
<p>
在使用元数据加密时,必须在装载 <code>/data</code> 之前运行 <code>vold</code>。为了确保其提前足够长的时间开始运行,请将以下节添加到 <code>init.hardware.rc</code> 中:</p>
<pre class="prettyprint"># We need vold early for metadata encryption
on early-fs
start vold</pre>
<p>
Keymaster 必须在 init 尝试装载 <code>/data</code> 之前运行并准备就绪。
</p>
<p>
<code>init.hardware.rc</code> 应该已经包含一个 <code>mount_all</code> 指令,用于将 <code>/data</code> 本身装载到 <code>on
late-fs</code> 节中。请在这行代码前面添加以下指令,以执行 <code>wait_for_keymaster</code> 服务:</p>
<pre class="prettyprint">on late-fs
# Wait for keymaster
exec_start wait_for_keymaster
# Mount RW partitions which need run fsck
mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --late</pre>
<h3 id="switching-on-metadata-encryption">开启元数据加密</h3>
<p>
最后,将 <code>keydirectory=/metadata/vold/metadata_encryption</code> 添加到用户数据的 <code>fstab.hardware</code> 条目中:</p>
<pre class="prettyprint">/dev/block/bootdevice/by-name/userdata /data f2fs noatime,nosuid,nodev,discard latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable</pre>
<h2 id="validation">验证</h2>
<p>在实现元数据加密时,请注意以下常见问题并测试您的实现。
</p>
<h3 id="common-issues">常见问题</h3>
<p>
在调用 <code>mount_all</code>(用于装载元数据加密的 <code>/data</code> 分区)时,<code>init</code> 会执行 vdc 工具。vdc 工具会通过 <code>vold</code> 连接到 <code>binder</code>,以设置元数据加密的设备并装载分区。在此调用期间,<code>init</code> 会被屏蔽,并且在 <code>mount_all</code> 完成之前,尝试读取或设置 <code>init</code> 属性的操作也会被屏蔽。在此阶段,如果 <code>vold</code> 的任何一部分工作在读取或设置某个属性时被直接或间接屏蔽,则会导致死锁。请务必确保 <code>vold</code> 能够完成读取密钥、与 Keymaster 交互以及装载数据目录的工作,而无需与 <code>init</code> 进一步交互。
</p>
<p>如果 Keymaster 在 <code>mount_all</code> 运行时没有完全启动,就不会响应 <code>vold</code>,直到从 <code>init</code> 读取到某些属性为止,从而导致上述死锁。按照相关规定将 <code>exec_start wait_for_keymaster</code> 放置在相关的 <code>mount_all</code> 调用之前,可确保 Keymaster 提前完全运行,从而避免此类死锁。
</p>
<h3 id="metadata-encryption-test">元数据加密测试</h3>
<p>
我们会将这些测试放到上游,与此同时,请向 <code>Android.bp</code> 中添加几行代码,并将 <code>check_encryption.cpp</code> 添加到 <code><a href="https://android.googlesource.com/platform/system/vold/+/master">platform/system/vold</a></code> 以测试您的实现。</p>
<h4 id="changes-to-android-bp"><code>Android.bp</code> 的更改</h4>
<p>下面列出了对 <code>Android.bp</code> 的更改。
</p>
<pre class="prettyprint">...
}
cc_binary {
name: "vold",
defaults: [
"vold_default_flags",
"vold_default_libs",
],
srcs: ["main.cpp"],
static_libs: ["libvold"],
product_variables: {
arc: {
static_libs: [
"arc_services_aidl",
"libarcobbvolume",
],
},
},
init_rc: [
"vold.rc",
"wait_for_keymaster.rc",
],
required: [
<strong>"check_encryption",</strong>
"mke2fs",
"vold_prepare_subdirs",
"wait_for_keymaster",
],
}
...
<strong>
}
cc_binary {
name: "check_encryption",
defaults: ["vold_default_flags"],
srcs: [
"FileDeviceUtils.cpp",
"check_encryption.cpp",
],
shared_libs: [
"libbase",
],
}</strong></pre>
<h4 id="add-check_encryption-cpp">添加 check_encryption.cpp</h4>
<p><code>check_encryption.cpp</code> 添加到 <code><a href="https://android.googlesource.com/platform/system/vold/+/master">platform/system/vold</a></code> 中。</p>
<pre class="prettyprint">/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FileDeviceUtils.h"
#include &lt;cmath&gt;
#include &lt;string&gt;
#include &lt;assert.h&gt;
#include &lt;stdio.h&gt;
#include &lt;android-base/file.h&gt;
#include &lt;android-base/logging.h&gt;
#include &lt;android-base/unique_fd.h&gt;
using android::base::unique_fd;
using android::base::ReadFileToString;
using android::base::WriteStringToFile;
namespace android {
namespace vold {
const size_t sectorsize = 1 &lt;&lt; 12;
const int sectorcount = 1024;
static double randomness_score(const std::string&amp; checkme) {
unsigned int freq[256] = {0};
unsigned int sum = 256;
double loglaplace = 0;
for (auto b : checkme) {
loglaplace -= 8 + log2(static_cast&lt;double&gt;(++freq[static_cast&lt;uint8_t&gt;(b)]) / (sum++));
}
return loglaplace;
LOG(INFO) &lt;&lt; "Score: " &lt;&lt; loglaplace; // if negative, not random
return loglaplace &lt; 0;
}
static bool run_test(const std::string&amp; device) {
unique_fd device_fd(open(device.c_str(), O_RDONLY | O_CLOEXEC));
if (device_fd.get() == -1) {
PLOG(ERROR) &lt;&lt; "Failed to open " &lt;&lt; device;
return false;
}
int randompassed = 0;
auto buf = std::string(sectorsize, '\0');
for (int i = 0; i &lt; sectorcount; i++) {
auto l = read(device_fd.get(), &amp;buf[0], buf.size());
if (l &lt; 1) {
PLOG(ERROR) &lt;&lt; "Failed read on sector " &lt;&lt; i;
return false;
}
if (((size_t)l) != buf.size()) {
LOG(ERROR) &lt;&lt; "Short read on sector " &lt;&lt; i;
return false;
}
auto score = randomness_score(buf);
if (score &gt;= 0) {
randompassed++;
LOG(INFO) &lt;&lt; "Passed randomness check on sector " &lt;&lt; i &lt;&lt; " with score " &lt;&lt; score;
} else {
LOG(ERROR) &lt;&lt; "Failed randomness check on sector " &lt;&lt; i &lt;&lt; " with score " &lt;&lt; score;
}
}
LOG(INFO) &lt;&lt; "Passed randomness check on " &lt;&lt; randompassed &lt;&lt; "/" &lt;&lt; sectorcount &lt;&lt; " sectors";
return randompassed == sectorcount;
}
} // namespace vold
} // namespace android
int main(int argc, const char* const argv[]) {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(const_cast&lt;char**&gt;(argv), android::base::StderrLogger);
if (argc != 2) {
LOG(ERROR) &lt;&lt; "Usage: " &lt;&lt; argv[0] &lt;&lt; " &lt;device&gt;";
LOG(ERROR) &lt;&lt; "example: " &lt;&lt; argv[0] &lt;&lt; " /dev/block/bootdevice/by-name/userdata";
return -1;
}
android::vold::run_test(std::string(argv[1]));
return 0;
}</pre>
</body></html>