blob: 2338f232d5c22af9d95838f75aa7d6a11efe5842 [file] [log] [blame]
Paul Crowley1ef25582016-01-21 20:26:12 +00001/*
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 "KeyStorage.h"
18
19#include "Keymaster.h"
Paul Crowley63c18d32016-02-10 14:02:47 +000020#include "ScryptParameters.h"
Paul Crowley1ef25582016-01-21 20:26:12 +000021#include "Utils.h"
22
23#include <vector>
24
25#include <errno.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <unistd.h>
30
31#include <openssl/sha.h>
32
33#include <android-base/file.h>
34#include <android-base/logging.h>
35
Paul Crowley63c18d32016-02-10 14:02:47 +000036#include <cutils/properties.h>
37
Paul Crowley1ef25582016-01-21 20:26:12 +000038#include <keymaster/authorization_set.h>
39
Paul Crowley63c18d32016-02-10 14:02:47 +000040extern "C" {
41
42#include "crypto_scrypt.h"
43
44}
45
Paul Crowley1ef25582016-01-21 20:26:12 +000046namespace android {
47namespace vold {
48
Paul Crowley05720802016-02-08 15:55:41 +000049const KeyAuthentication kEmptyAuthentication { "", "" };
50
Paul Crowley1ef25582016-01-21 20:26:12 +000051static constexpr size_t AES_KEY_BYTES = 32;
52static constexpr size_t GCM_NONCE_BYTES = 12;
53static constexpr size_t GCM_MAC_BYTES = 16;
Paul Crowley63c18d32016-02-10 14:02:47 +000054static constexpr size_t SALT_BYTES = 1<<4;
Paul Crowley1ef25582016-01-21 20:26:12 +000055static constexpr size_t SECDISCARDABLE_BYTES = 1<<14;
Paul Crowley63c18d32016-02-10 14:02:47 +000056static constexpr size_t STRETCHED_BYTES = 1<<6;
Paul Crowley1ef25582016-01-21 20:26:12 +000057
Paul Crowley05720802016-02-08 15:55:41 +000058static const char* kCurrentVersion = "1";
Paul Crowley1ef25582016-01-21 20:26:12 +000059static const char* kRmPath = "/system/bin/rm";
60static const char* kSecdiscardPath = "/system/bin/secdiscard";
Paul Crowley63c18d32016-02-10 14:02:47 +000061static const char* kStretch_none = "none";
62static const char* kStretch_nopassword = "nopassword";
63static const std::string kStretchPrefix_scrypt = "scrypt ";
Paul Crowley1ef25582016-01-21 20:26:12 +000064static const char* kFn_encrypted_key = "encrypted_key";
Paul Crowley05720802016-02-08 15:55:41 +000065static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
Paul Crowley63c18d32016-02-10 14:02:47 +000066static const char* kFn_salt = "salt";
Paul Crowley1ef25582016-01-21 20:26:12 +000067static const char* kFn_secdiscardable = "secdiscardable";
Paul Crowley05720802016-02-08 15:55:41 +000068static const char* kFn_stretching = "stretching";
69static const char* kFn_version = "version";
Paul Crowley1ef25582016-01-21 20:26:12 +000070
Paul Crowley13ffd8e2016-01-27 14:30:22 +000071static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
Paul Crowley1ef25582016-01-21 20:26:12 +000072 if (actual != expected) {
73 LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected
74 << " got " << actual;
75 return false;
76 }
77 return true;
78}
79
Paul Crowley13ffd8e2016-01-27 14:30:22 +000080static std::string hashSecdiscardable(const std::string &secdiscardable) {
Paul Crowley1ef25582016-01-21 20:26:12 +000081 SHA512_CTX c;
82
83 SHA512_Init(&c);
84 // Personalise the hashing by introducing a fixed prefix.
85 // Hashing applications should use personalization except when there is a
86 // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
Paul Crowley13ffd8e2016-01-27 14:30:22 +000087 std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512";
88 secdiscardableHashingPrefix.resize(SHA512_CBLOCK);
89 SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size());
Paul Crowley1ef25582016-01-21 20:26:12 +000090 SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
91 std::string res(SHA512_DIGEST_LENGTH, '\0');
92 SHA512_Final(reinterpret_cast<uint8_t *>(&res[0]), &c);
93 return res;
94}
95
Paul Crowley13ffd8e2016-01-27 14:30:22 +000096static bool generateKeymasterKey(Keymaster &keymaster,
97 const keymaster::AuthorizationSet &extraParams,
Paul Crowley1ef25582016-01-21 20:26:12 +000098 std::string &key) {
Paul Crowley13ffd8e2016-01-27 14:30:22 +000099 auto params = keymaster::AuthorizationSetBuilder()
Paul Crowley1ef25582016-01-21 20:26:12 +0000100 .AesEncryptionKey(AES_KEY_BYTES * 8)
101 .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
102 .Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
103 .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE)
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000104 .Authorization(keymaster::TAG_NO_AUTH_REQUIRED) // FIXME integrate with gatekeeper
105 .build();
106 params.push_back(extraParams);
107 return keymaster.generateKey(params, key);
Paul Crowley1ef25582016-01-21 20:26:12 +0000108}
109
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000110static bool encryptWithKeymasterKey(
Paul Crowley1ef25582016-01-21 20:26:12 +0000111 Keymaster &keymaster,
112 const std::string &key,
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000113 const keymaster::AuthorizationSet &extraParams,
Paul Crowley1ef25582016-01-21 20:26:12 +0000114 const std::string &message,
115 std::string &ciphertext) {
116 // FIXME fix repetition
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000117 auto params = keymaster::AuthorizationSetBuilder()
Paul Crowley1ef25582016-01-21 20:26:12 +0000118 .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
119 .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000120 .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE)
121 .build();
122 params.push_back(extraParams);
123 keymaster::AuthorizationSet outParams;
124 auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, outParams);
125 if (!opHandle) return false;
126 keymaster_blob_t nonceBlob;
127 if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000128 LOG(ERROR) << "GCM encryption but no nonce generated";
129 return false;
130 }
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000131 // nonceBlob here is just a pointer into existing data, must not be freed
132 std::string nonce(reinterpret_cast<const char *>(nonceBlob.data), nonceBlob.data_length);
133 if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000134 std::string body;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000135 if (!opHandle.updateCompletely(message, body)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000136
137 std::string mac;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000138 if (!opHandle.finishWithOutput(mac)) return false;
139 if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000140 ciphertext = nonce + body + mac;
141 return true;
142}
143
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000144static bool decryptWithKeymasterKey(
Paul Crowley1ef25582016-01-21 20:26:12 +0000145 Keymaster &keymaster, const std::string &key,
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000146 const keymaster::AuthorizationSet &extraParams,
Paul Crowley1ef25582016-01-21 20:26:12 +0000147 const std::string &ciphertext,
148 std::string &message) {
149 auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000150 auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
Paul Crowley1ef25582016-01-21 20:26:12 +0000151 // FIXME fix repetition
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000152 auto params = addStringParam(keymaster::AuthorizationSetBuilder(), keymaster::TAG_NONCE, nonce)
Paul Crowley1ef25582016-01-21 20:26:12 +0000153 .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
154 .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000155 .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE)
156 .build();
157 params.push_back(extraParams);
Paul Crowley1ef25582016-01-21 20:26:12 +0000158
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000159 auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
160 if (!opHandle) return false;
161 if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
162 if (!opHandle.finish()) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000163 return true;
164}
165
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000166static bool readFileToString(const std::string &filename, std::string &result) {
167 if (!android::base::ReadFileToString(filename, &result)) {
168 PLOG(ERROR) << "Failed to read from " << filename;
169 return false;
170 }
171 return true;
172}
173
174static bool writeStringToFile(const std::string &payload, const std::string &filename) {
175 if (!android::base::WriteStringToFile(payload, filename)) {
176 PLOG(ERROR) << "Failed to write to " << filename;
177 return false;
178 }
179 return true;
180}
181
Paul Crowley63c18d32016-02-10 14:02:47 +0000182static std::string getStretching() {
183 char paramstr[PROPERTY_VALUE_MAX];
184
185 property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
186 return std::string() + kStretchPrefix_scrypt + paramstr;
187}
188
189static bool stretchingNeedsSalt(const std::string &stretching) {
190 return stretching != kStretch_nopassword && stretching != kStretch_none;
191}
192
193static bool stretchSecret(const std::string &stretching, const std::string &secret,
194 const std::string &salt, std::string &stretched) {
195 if (stretching == kStretch_nopassword) {
196 if (!secret.empty()) {
197 LOG(ERROR) << "Password present but stretching is nopasswd";
198 // Continue anyway
199 }
200 stretched.clear();
201 } else if (stretching == kStretch_none) {
202 stretched = secret;
203 } else if (std::equal(kStretchPrefix_scrypt.begin(),
204 kStretchPrefix_scrypt.end(), stretching.begin())) {
205 int Nf, rf, pf;
206 if (!parse_scrypt_parameters(
207 stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf, &rf, &pf)) {
208 LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
209 return false;
210 }
211 stretched.assign(STRETCHED_BYTES, '\0');
212 if (crypto_scrypt(
213 reinterpret_cast<const uint8_t *>(secret.data()), secret.size(),
214 reinterpret_cast<const uint8_t *>(salt.data()), salt.size(),
215 1 << Nf, 1 << rf, 1 << pf,
216 reinterpret_cast<uint8_t *>(&stretched[0]), stretched.size()) != 0) {
217 LOG(ERROR) << "scrypt failed with params: " << stretching;
218 return false;
219 }
220 } else {
221 LOG(ERROR) << "Unknown stretching type: " << stretching;
222 return false;
223 }
224 return true;
225}
226
227static bool buildParams(const KeyAuthentication &auth, const std::string &stretching,
228 const std::string &salt, const std::string &secdiscardable,
229 keymaster::AuthorizationSet &result) {
230 std::string stretched;
231 if (!stretchSecret(stretching, auth.secret, salt, stretched)) return false;
232 auto appId = hashSecdiscardable(secdiscardable) + stretched;
Paul Crowley05720802016-02-08 15:55:41 +0000233 keymaster::AuthorizationSetBuilder paramBuilder;
Paul Crowley05720802016-02-08 15:55:41 +0000234 addStringParam(paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
235 if (!auth.token.empty()) {
236 addStringParam(paramBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
237 }
Paul Crowley63c18d32016-02-10 14:02:47 +0000238 result = paramBuilder.build();
239 return true;
Paul Crowley05720802016-02-08 15:55:41 +0000240}
241
242bool storeKey(const std::string &dir, const KeyAuthentication &auth, const std::string &key) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000243 if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
244 PLOG(ERROR) << "key mkdir " << dir;
245 return false;
246 }
Paul Crowley05720802016-02-08 15:55:41 +0000247 if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000248 std::string secdiscardable;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000249 if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000250 // TODO status_t plays badly with PLOG, fix it.
251 LOG(ERROR) << "Random read failed";
252 return false;
253 }
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000254 if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
Paul Crowley63c18d32016-02-10 14:02:47 +0000255 std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
Paul Crowley05720802016-02-08 15:55:41 +0000256 if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
Paul Crowley63c18d32016-02-10 14:02:47 +0000257 std::string salt;
258 if (stretchingNeedsSalt(stretching)) {
259 if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
260 LOG(ERROR) << "Random read failed";
261 return false;
262 }
263 if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
264 }
265 keymaster::AuthorizationSet extraParams;
266 if (!buildParams(auth, stretching, salt, secdiscardable, extraParams)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000267 Keymaster keymaster;
268 if (!keymaster) return false;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000269 std::string kmKey;
270 if (!generateKeymasterKey(keymaster, extraParams, kmKey)) return false;
271 std::string encryptedKey;
Paul Crowley63c18d32016-02-10 14:02:47 +0000272 if (!encryptWithKeymasterKey(keymaster, kmKey, extraParams, key, encryptedKey)) return false;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000273 if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
274 if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000275 return true;
276}
277
Paul Crowley05720802016-02-08 15:55:41 +0000278bool retrieveKey(const std::string &dir, const KeyAuthentication &auth, std::string &key) {
279 std::string version;
280 if (!readFileToString(dir + "/" + kFn_version, version)) return false;
281 if (version != kCurrentVersion) {
282 LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
283 return false;
284 }
Paul Crowley1ef25582016-01-21 20:26:12 +0000285 std::string secdiscardable;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000286 if (!readFileToString(dir + "/" + kFn_secdiscardable, secdiscardable)) return false;
Paul Crowley63c18d32016-02-10 14:02:47 +0000287 std::string stretching;
288 if (!readFileToString(dir + "/" + kFn_stretching, stretching)) return false;
289 std::string salt;
290 if (stretchingNeedsSalt(stretching)) {
291 if (!readFileToString(dir + "/" + kFn_salt, salt)) return false;
292 }
293 keymaster::AuthorizationSet extraParams;
294 if (!buildParams(auth, stretching, salt, secdiscardable, extraParams)) return false;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000295 std::string kmKey;
296 if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, kmKey)) return false;
297 std::string encryptedMessage;
298 if (!readFileToString(dir + "/" + kFn_encrypted_key, encryptedMessage)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000299 Keymaster keymaster;
300 if (!keymaster) return false;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000301 return decryptWithKeymasterKey(keymaster, kmKey, extraParams, encryptedMessage, key);
Paul Crowley1ef25582016-01-21 20:26:12 +0000302}
303
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000304static bool deleteKey(const std::string &dir) {
305 std::string kmKey;
306 if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, kmKey)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000307 Keymaster keymaster;
308 if (!keymaster) return false;
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000309 if (!keymaster.deleteKey(kmKey)) return false;
Paul Crowley1ef25582016-01-21 20:26:12 +0000310 return true;
311}
312
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000313static bool secdiscardSecdiscardable(const std::string &dir) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000314 if (ForkExecvp(std::vector<std::string> {
315 kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
316 LOG(ERROR) << "secdiscard failed";
317 return false;
318 }
319 return true;
320}
321
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000322static bool recursiveDeleteKey(const std::string &dir) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000323 if (ForkExecvp(std::vector<std::string> {
324 kRmPath, "-rf", dir}) != 0) {
325 LOG(ERROR) << "recursive delete failed";
326 return false;
327 }
328 return true;
329}
330
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000331bool destroyKey(const std::string &dir) {
Paul Crowley1ef25582016-01-21 20:26:12 +0000332 bool success = true;
333 // Try each thing, even if previous things failed.
Paul Crowley13ffd8e2016-01-27 14:30:22 +0000334 success &= deleteKey(dir);
335 success &= secdiscardSecdiscardable(dir);
336 success &= recursiveDeleteKey(dir);
Paul Crowley1ef25582016-01-21 20:26:12 +0000337 return success;
338}
339
340} // namespace vold
341} // namespace android