blob: 70a551c0e4bbf554f6202dbd92c6b6a0328af7e8 [file] [log] [blame]
bigbiff7ba75002020-04-11 20:47:09 -04001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "KeyUtil.h"
18
19#include <linux/fs.h>
20#include <iomanip>
21#include <sstream>
22#include <string>
23
24#include <openssl/sha.h>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <keyutils.h>
29
mauronofrio matarrese79820322020-05-25 19:48:56 +020030#include "FsCrypt.h"
bigbiff7ba75002020-04-11 20:47:09 -040031#include "KeyStorage.h"
32#include "Utils.h"
33
mauronofrio matarrese79820322020-05-25 19:48:56 +020034#define MAX_USER_ID 0xFFFFFFFF
35
36using android::hardware::keymaster::V4_0::KeyFormat;
37using android::vold::KeyType;
bigbiff7ba75002020-04-11 20:47:09 -040038namespace android {
39namespace vold {
40
41constexpr int FS_AES_256_XTS_KEY_SIZE = 64;
42
43bool randomKey(KeyBuffer* key) {
44 *key = KeyBuffer(FS_AES_256_XTS_KEY_SIZE);
45 if (ReadRandomBytes(key->size(), key->data()) != 0) {
46 // TODO status_t plays badly with PLOG, fix it.
47 LOG(ERROR) << "Random read failed";
48 return false;
49 }
50 return true;
51}
52
53// Get raw keyref - used to make keyname and to pass to ioctl
54static std::string generateKeyRef(const uint8_t* key, int length) {
55 SHA512_CTX c;
56
57 SHA512_Init(&c);
58 SHA512_Update(&c, key, length);
59 unsigned char key_ref1[SHA512_DIGEST_LENGTH];
60 SHA512_Final(key_ref1, &c);
61
62 SHA512_Init(&c);
63 SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
64 unsigned char key_ref2[SHA512_DIGEST_LENGTH];
65 SHA512_Final(key_ref2, &c);
66
67 static_assert(FS_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, "Hash too short for descriptor");
68 return std::string((char*)key_ref2, FS_KEY_DESCRIPTOR_SIZE);
69}
70
71static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
72 if (key.size() != FS_AES_256_XTS_KEY_SIZE) {
73 LOG(ERROR) << "Wrong size key " << key.size();
74 return false;
75 }
76 static_assert(FS_AES_256_XTS_KEY_SIZE <= sizeof(fs_key->raw), "Key too long!");
77 fs_key->mode = FS_ENCRYPTION_MODE_AES_256_XTS;
78 fs_key->size = key.size();
79 memset(fs_key->raw, 0, sizeof(fs_key->raw));
80 memcpy(fs_key->raw, key.data(), key.size());
81 return true;
82}
83
84static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
85
86static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
87 std::ostringstream o;
88 o << prefix << ":";
89 for (unsigned char i : raw_ref) {
90 o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
91 }
92 return o.str();
93}
94
95// Get the keyring we store all keys in
96static bool fscryptKeyring(key_serial_t* device_keyring) {
97 *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
98 if (*device_keyring == -1) {
99 PLOG(ERROR) << "Unable to find device keyring";
100 return false;
101 }
102 return true;
103}
104
105// Install password into global keyring
106// Return raw key reference for use in policy
107bool installKey(const KeyBuffer& key, std::string* raw_ref) {
108 // Place fscrypt_key into automatically zeroing buffer.
109 KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
110 fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
111
112 if (!fillKey(key, &fs_key)) return false;
mauronofrio matarrese79820322020-05-25 19:48:56 +0200113 if (is_wrapped_key_supported()) {
114 /* When wrapped key is supported, only the first 32 bytes are
115 the same per boot. The second 32 bytes can change as the ephemeral
116 key is different. */
117 *raw_ref = generateKeyRef(fs_key.raw, (fs_key.size)/2);
118 } else {
119 *raw_ref = generateKeyRef(fs_key.raw, fs_key.size);
120 }
bigbiff7ba75002020-04-11 20:47:09 -0400121 key_serial_t device_keyring;
122 if (!fscryptKeyring(&device_keyring)) return false;
123 for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
124 auto ref = keyname(*name_prefix, *raw_ref);
125 key_serial_t key_id =
126 add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
127 if (key_id == -1) {
128 PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
129 return false;
130 }
131 LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
132 << " in process " << getpid();
133 }
134 return true;
135}
136
137bool evictKey(const std::string& raw_ref) {
138 key_serial_t device_keyring;
139 if (!fscryptKeyring(&device_keyring)) return false;
140 bool success = true;
141 for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
142 auto ref = keyname(*name_prefix, raw_ref);
143 auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
144
145 // Unlink the key from the keyring. Prefer unlinking to revoking or
146 // invalidating, since unlinking is actually no less secure currently, and
147 // it avoids bugs in certain kernel versions where the keyring key is
148 // referenced from places it shouldn't be.
149 if (keyctl_unlink(key_serial, device_keyring) != 0) {
150 PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
151 success = false;
152 } else {
153 LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
154 }
155 }
156 return success;
157}
158
159bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
160 const std::string& key_path, const std::string& tmp_path,
mauronofrio matarrese79820322020-05-25 19:48:56 +0200161 std::string* key_ref, bool wrapped_key_supported) {
bigbiff7ba75002020-04-11 20:47:09 -0400162 KeyBuffer key;
163 if (pathExists(key_path)) {
164 LOG(DEBUG) << "Key exists, using: " << key_path;
165 if (!retrieveKey(key_path, key_authentication, &key)) return false;
166 } else {
167 if (!create_if_absent) {
168 LOG(ERROR) << "No key found in " << key_path;
169 return false;
170 }
171 LOG(INFO) << "Creating new key in " << key_path;
mauronofrio matarrese79820322020-05-25 19:48:56 +0200172 if (wrapped_key_supported) {
173 if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false;
174 } else {
175 if (!randomKey(&key)) return false;
176 }
bigbiff7ba75002020-04-11 20:47:09 -0400177 if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
178 }
179
mauronofrio matarrese79820322020-05-25 19:48:56 +0200180 if (wrapped_key_supported) {
181 KeyBuffer ephemeral_wrapped_key;
182 if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
183 LOG(ERROR) << "Failed to export key in retrieveAndInstallKey";
184 return false;
185 }
186 key = std::move(ephemeral_wrapped_key);
187 }
188
bigbiff7ba75002020-04-11 20:47:09 -0400189 if (!installKey(key, key_ref)) {
190 LOG(ERROR) << "Failed to install key in " << key_path;
191 return false;
192 }
193 return true;
194}
195
196bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
197 KeyBuffer* key, bool keepOld) {
198 LOG(ERROR) << "retreiveKey1";
199 if (pathExists(key_path)) {
200 LOG(ERROR) << "Key exists, using: " << key_path;
201 if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
202 } else {
203 if (!create_if_absent) {
204 LOG(ERROR) << "No key found in " << key_path;
205 return false;
206 }
207 LOG(ERROR) << "Creating new key in " << key_path;
208 if (!randomKey(key)) return false;
209 LOG(ERROR) << "retrieveKey1";
210 if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false;
211 }
212 return true;
213}
214
215} // namespace vold
216} // namespace android