| <html devsite> |
| <head> |
| <title>Metadata Encryption</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 and later supports |
| <a href="/security/encryption/file-based">file-based encryption</a> (FBE). FBE |
| allows different files to be encrypted with different keys that can be unlocked |
| independently. These keys are used to encrypt both file contents and file names. |
| When FBE is used, other information, such as directory layouts, file sizes, |
| permissions, and creation/modification times, is not encrypted. Collectively, |
| this is known as filesystem metadata. |
| </p> |
| <p> |
| Android 9 introduces support for metadata encryption where hardware support is |
| present. With metadata encryption, a single key present at boot time encrypts |
| whatever content is not encrypted by FBE. This key is protected by Keymaster, |
| which in turn is protected by verified boot. |
| </p> |
| <h2 id="implementation">Implementation</h2> |
| <p> |
| You can set up metadata encryption on new devices running Android 9 by setting |
| up the metadata filesystem, changing the init sequence, and turning on metadata |
| encryption in the device's fstab file. |
| </p> |
| <h3 id="hardware-requirements">Hardware requirements</h3> |
| <p> |
| Metadata encryption can only be set up when the data partition is first |
| formatted. As a result, this feature is only for new devices; this is not |
| something an OTA should change. |
| </p> |
| <p> |
| To support metadata encryption currently, your hardware needs to support an <a |
| href="https://blog.google/topics/connected-workspaces/pixel-security-better-faster-stronger/">inline |
| crypto engine</a> to use for file-based encryption. This is indicated by a |
| <code>fileencryption=ice</code> directive for the userdata partition in |
| <code>fstab.hardware</code>. |
| </p> |
| <p> |
| Additionally, the <code>dm-default-key</code> module must be present and enabled |
| in the kernel. |
| </p> |
| <h3 id="set-up-metadata-filesystem">Set up metadata filesystem</h3> |
| <p> |
| Because nothing in the userdata partition can be read until the metadata |
| encryption key is present, the partition table must set aside a separate |
| partition called the "metadata partition" for storing the keymaster blobs that |
| protect this key. The metadata partition should be 16MB. |
| </p> |
| <p> |
| <code>fstab.hardware</code> must include an entry for the metadata filesystem |
| that lives on that partition mounting it at <code>/metadata</code>, including |
| the <code>formattable</code> flag to ensure it is formatted at boot time. The |
| f2fs filesystem does not work on smaller partitions; we recommend using ext4 |
| instead. For example: |
| </p> |
| |
| <pre |
| class="prettyprint">/dev/block/bootdevice/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard wait,check,formattable</pre> |
| <p> |
| To ensure the <code>/metadata</code> mount point exists, add the following line |
| to <code>BoardConfig-common.mk</code>: |
| </p> |
| |
| <pre |
| class="prettyprint">BOARD_USES_METADATA_PARTITION := true</pre> |
| |
| <h3 id="changes-to-the-init-sequence">Changes to the init sequence</h3> |
| <p> |
| When metadata encryption is used, <code>vold</code> must be running before |
| <code>/data</code> is mounted. To ensure that it is started early enough, add |
| the following stanza to <code>init.hardware.rc</code>: |
| </p> |
| |
| |
| <pre |
| class="prettyprint"># We need vold early for metadata encryption |
| on early-fs |
| start vold</pre> |
| <p> |
| Keymaster must be running and ready before init attempts to mount |
| <code>/data</code>. |
| </p> |
| <p> |
| <code>init.hardware.rc</code> should already contain a <code>mount_all</code> |
| instruction which mounts <code>/data</code> itself in the <code>on |
| late-fs</code> stanza. Before this line, add the directive to exec the |
| <code>wait_for_keymaster</code> service: |
| </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">Switching on metadata encryption</h3> |
| <p> |
| Finally add <code>keydirectory=/metadata/vold/metadata_encryption</code> to the |
| <code>fstab.hardware</code> entry for userdata: |
| </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">Validation</h2> |
| <p>While implementing metadata encryption, be mindful of these common issues |
| and test your implementation. |
| </p> |
| |
| <h3 id="common-issues">Common issues</h3> |
| <p> |
| During the call to <code>mount_all</code>, which mounts the metadata-encrypted |
| <code>/data</code> partition, <code>init</code> executes the vdc tool. The vdc |
| tool connects to <code>vold</code> over <code>binder</code> to set up the |
| metadata-encrypted device and mount the partition. For the duration of this |
| call, <code>init</code> is blocked, and attempts to either read or set |
| <code>init</code> properties will block until <code>mount_all</code> finishes. |
| If, at this stage, any part of <code>vold</code>'s work is directly or |
| indirectly blocked on reading or setting a property, deadlock will result. It is |
| important to ensure that <code>vold</code> can complete the work of reading the |
| keys, interacting with Keymaster, and mounting the data directory without |
| interacting further with <code>init</code>. |
| </p> |
| <p> |
| If Keymaster is not fully started when <code>mount_all</code> runs, it will not |
| respond to <code>vold</code> until it has read certain properties from |
| <code>init</code>, resulting in exactly the deadlock described. Placing |
| <code>exec_start wait_for_keymaster</code> above the relevant |
| <code>mount_all</code> invocation as set out ensures that Keymaster is fully |
| running in advance and so avoids this deadlock. |
| </p> |
| |
| <h3 id="metadata-encryption-test">Metadata encryption test</h3> |
| <p> |
| We're upstreaming these tests, but in the meantime, add a few lines to |
| <code>Android.bp</code> and add <code>check_encryption.cpp</code> to <code><a |
| href="https://android.googlesource.com/platform/system/vold/+/master">platform/system/vold</a></code> |
| to test your implementation.</p> |
| |
| <h4 id="changes-to-android-bp">Changes to <code>Android.bp</code></h4> |
| <p> |
| Changes to <code>Android.bp</code> called out below. |
| </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">Add check_encryption.cpp</h4> |
| |
| <p>Add <code>check_encryption.cpp</code> to <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 <cmath> |
| #include <string> |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/unique_fd.h> |
| using android::base::unique_fd; |
| using android::base::ReadFileToString; |
| using android::base::WriteStringToFile; |
| |
| namespace android { |
| namespace vold { |
| const size_t sectorsize = 1 << 12; |
| const int sectorcount = 1024; |
| static double randomness_score(const std::string& checkme) { |
| unsigned int freq[256] = {0}; |
| unsigned int sum = 256; |
| double loglaplace = 0; |
| for (auto b : checkme) { |
| loglaplace -= 8 + log2(static_cast<double>(++freq[static_cast<uint8_t>(b)]) / (sum++)); |
| } |
| return loglaplace; |
| LOG(INFO) << "Score: " << loglaplace; // if negative, not random |
| return loglaplace < 0; |
| } |
| static bool run_test(const std::string& device) { |
| unique_fd device_fd(open(device.c_str(), O_RDONLY | O_CLOEXEC)); |
| if (device_fd.get() == -1) { |
| PLOG(ERROR) << "Failed to open " << device; |
| return false; |
| } |
| int randompassed = 0; |
| auto buf = std::string(sectorsize, '\0'); |
| for (int i = 0; i < sectorcount; i++) { |
| auto l = read(device_fd.get(), &buf[0], buf.size()); |
| if (l < 1) { |
| PLOG(ERROR) << "Failed read on sector " << i; |
| return false; |
| } |
| if (((size_t)l) != buf.size()) { |
| LOG(ERROR) << "Short read on sector " << i; |
| return false; |
| } |
| auto score = randomness_score(buf); |
| if (score >= 0) { |
| randompassed++; |
| LOG(INFO) << "Passed randomness check on sector " << i << " with score " << score; |
| } else { |
| LOG(ERROR) << "Failed randomness check on sector " << i << " with score " << score; |
| } |
| } |
| LOG(INFO) << "Passed randomness check on " << randompassed << "/" << sectorcount << " 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<char**>(argv), android::base::StderrLogger); |
| if (argc != 2) { |
| LOG(ERROR) << "Usage: " << argv[0] << " <device>"; |
| LOG(ERROR) << "example: " << argv[0] << " /dev/block/bootdevice/by-name/userdata"; |
| return -1; |
| } |
| android::vold::run_test(std::string(argv[1])); |
| return 0; |
| }</pre> |
| </body> |
| </html> |