• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 #define LOG_TAG "keystore"
18 
19 #include <arpa/inet.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 
24 #include <log/log.h>
25 
26 #include "blob.h"
27 
28 #include "keystore_utils.h"
29 
30 #include <openssl/evp.h>
31 #include <openssl/rand.h>
32 
33 #include <istream>
34 #include <ostream>
35 #include <streambuf>
36 #include <string>
37 
38 #include <android-base/logging.h>
39 #include <android-base/unique_fd.h>
40 
41 namespace {
42 
43 constexpr size_t kGcmIvSizeBytes = 96 / 8;
44 
45 #if defined(__clang__)
46 #define OPTNONE __attribute__((optnone))
47 #elif defined(__GNUC__)
48 #define OPTNONE __attribute__((optimize("O0")))
49 #else
50 #error Need a definition for OPTNONE
51 #endif
52 
53 class ArrayEraser {
54   public:
ArrayEraser(uint8_t * arr,size_t size)55     ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
~ArrayEraser()56     OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
57 
58   private:
59     volatile uint8_t* mArr;
60     size_t mSize;
61 };
62 
63 /**
64  * Returns a EVP_CIPHER appropriate for the given key, based on the key's size.
65  */
getAesCipherForKey(const std::vector<uint8_t> & key)66 const EVP_CIPHER* getAesCipherForKey(const std::vector<uint8_t>& key) {
67     const EVP_CIPHER* cipher = EVP_aes_256_gcm();
68     if (key.size() == kAes128KeySizeBytes) {
69         cipher = EVP_aes_128_gcm();
70     }
71     return cipher;
72 }
73 
74 /*
75  * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
76  * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
77  * 'tag'.
78  */
AES_gcm_encrypt(const uint8_t * in,uint8_t * out,size_t len,const std::vector<uint8_t> & key,const uint8_t * iv,uint8_t * tag)79 ResponseCode AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
80                              const std::vector<uint8_t>& key, const uint8_t* iv, uint8_t* tag) {
81 
82     // There can be 128-bit and 256-bit keys
83     const EVP_CIPHER* cipher = getAesCipherForKey(key);
84 
85     bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
86 
87     EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
88     EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
89 
90     std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
91     uint8_t* out_pos = out_tmp.get();
92     int out_len;
93 
94     EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
95     out_pos += out_len;
96     EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
97     out_pos += out_len;
98     if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
99         ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
100               out_pos - out_tmp.get());
101         return ResponseCode::SYSTEM_ERROR;
102     }
103 
104     std::copy(out_tmp.get(), out_pos, out);
105     EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
106 
107     return ResponseCode::NO_ERROR;
108 }
109 
110 /*
111  * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
112  * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
113  * location as 'in').
114  */
AES_gcm_decrypt(const uint8_t * in,uint8_t * out,size_t len,const std::vector<uint8_t> key,const uint8_t * iv,const uint8_t * tag)115 ResponseCode AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
116                              const std::vector<uint8_t> key, const uint8_t* iv,
117                              const uint8_t* tag) {
118 
119     // There can be 128-bit and 256-bit keys
120     const EVP_CIPHER* cipher = getAesCipherForKey(key);
121 
122     bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
123 
124     EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
125     EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
126     EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
127 
128     std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
129     ArrayEraser out_eraser(out_tmp.get(), len);
130     uint8_t* out_pos = out_tmp.get();
131     int out_len;
132 
133     EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
134     out_pos += out_len;
135     if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
136         ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
137         return ResponseCode::VALUE_CORRUPTED;
138     }
139     out_pos += out_len;
140     if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
141         ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
142               out_pos - out_tmp.get());
143         return ResponseCode::VALUE_CORRUPTED;
144     }
145 
146     std::copy(out_tmp.get(), out_pos, out);
147 
148     return ResponseCode::NO_ERROR;
149 }
150 
151 class ArrayStreamBuffer : public std::streambuf {
152   public:
ArrayStreamBuffer(const T (& data)[size])153     template <typename T, size_t size> explicit ArrayStreamBuffer(const T (&data)[size]) {
154         static_assert(sizeof(T) == 1, "Array element size too large");
155         std::streambuf::char_type* d = const_cast<std::streambuf::char_type*>(
156             reinterpret_cast<const std::streambuf::char_type*>(&data[0]));
157         setg(d, d, d + size);
158         setp(d, d + size);
159     }
160 
161   protected:
seekoff(off_type off,std::ios_base::seekdir dir,std::ios_base::openmode which=std::ios_base::in|std::ios_base::out)162     pos_type seekoff(off_type off, std::ios_base::seekdir dir,
163                      std::ios_base::openmode which = std::ios_base::in |
164                                                      std::ios_base::out) override {
165         bool in = which & std::ios_base::in;
166         bool out = which & std::ios_base::out;
167         if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1;
168         std::streambuf::char_type* newPosPtr;
169         switch (dir) {
170         case std::ios_base::beg:
171             newPosPtr = pbase();
172             break;
173         case std::ios_base::cur:
174             // if dir == cur then in xor out due to
175             // if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1; above
176             if (in)
177                 newPosPtr = gptr();
178             else
179                 newPosPtr = pptr();
180             break;
181         case std::ios_base::end:
182             // in and out bounds are the same and cannot change, so we can take either range
183             // regardless of the value of "which"
184             newPosPtr = epptr();
185             break;
186         }
187         newPosPtr += off;
188         if (newPosPtr < pbase() || newPosPtr > epptr()) return -1;
189         if (in) {
190             gbump(newPosPtr - gptr());
191         }
192         if (out) {
193             pbump(newPosPtr - pptr());
194         }
195         return newPosPtr - pbase();
196     }
197 };
198 
199 }  // namespace
200 
Blob(const uint8_t * value,size_t valueLength,const uint8_t * info,uint8_t infoLength,BlobType type)201 Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
202            BlobType type) {
203     mBlob = std::make_unique<blobv3>();
204     memset(mBlob.get(), 0, sizeof(blobv3));
205     if (valueLength > kValueSize) {
206         valueLength = kValueSize;
207         ALOGW("Provided blob length too large");
208     }
209     if (infoLength + valueLength > kValueSize) {
210         infoLength = kValueSize - valueLength;
211         ALOGW("Provided info length too large");
212     }
213     mBlob->length = valueLength;
214     memcpy(mBlob->value, value, valueLength);
215 
216     mBlob->info = infoLength;
217     memcpy(mBlob->value + valueLength, info, infoLength);
218 
219     mBlob->version = CURRENT_BLOB_VERSION;
220     mBlob->type = uint8_t(type);
221 
222     if (type == TYPE_MASTER_KEY || type == TYPE_MASTER_KEY_AES256) {
223         mBlob->flags = KEYSTORE_FLAG_ENCRYPTED;
224     } else {
225         mBlob->flags = KEYSTORE_FLAG_NONE;
226     }
227 }
228 
Blob(blobv3 b)229 Blob::Blob(blobv3 b) {
230     mBlob = std::make_unique<blobv3>(b);
231 }
232 
Blob()233 Blob::Blob() {
234     if (mBlob) *mBlob = {};
235 }
236 
Blob(const Blob & rhs)237 Blob::Blob(const Blob& rhs) {
238     if (rhs.mBlob) {
239         mBlob = std::make_unique<blobv3>(*rhs.mBlob);
240     }
241 }
242 
Blob(Blob && rhs)243 Blob::Blob(Blob&& rhs) : mBlob(std::move(rhs.mBlob)) {}
244 
operator =(const Blob & rhs)245 Blob& Blob::operator=(const Blob& rhs) {
246     if (&rhs != this) {
247         if (mBlob) *mBlob = {};
248         if (rhs) {
249             mBlob = std::make_unique<blobv3>(*rhs.mBlob);
250         } else {
251             mBlob = {};
252         }
253     }
254     return *this;
255 }
256 
operator =(Blob && rhs)257 Blob& Blob::operator=(Blob&& rhs) {
258     if (mBlob) *mBlob = {};
259     mBlob = std::move(rhs.mBlob);
260     return *this;
261 }
262 
rawBlobIsEncrypted(const BlobType & blob)263 template <typename BlobType> static bool rawBlobIsEncrypted(const BlobType& blob) {
264     if (blob.version < 2) return true;
265 
266     return blob.flags & (KEYSTORE_FLAG_ENCRYPTED | KEYSTORE_FLAG_SUPER_ENCRYPTED);
267 }
268 
isEncrypted() const269 bool Blob::isEncrypted() const {
270     if (mBlob->version < 2) {
271         return true;
272     }
273 
274     return mBlob->flags & KEYSTORE_FLAG_ENCRYPTED;
275 }
276 
isSuperEncrypted() const277 bool Blob::isSuperEncrypted() const {
278     return mBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
279 }
280 
isCriticalToDeviceEncryption() const281 bool Blob::isCriticalToDeviceEncryption() const {
282     return mBlob->flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
283 }
284 
setFlag(uint8_t flags,bool set,KeyStoreFlag flag)285 inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
286     return set ? (flags | flag) : (flags & ~flag);
287 }
288 
setEncrypted(bool encrypted)289 void Blob::setEncrypted(bool encrypted) {
290     mBlob->flags = setFlag(mBlob->flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
291 }
292 
setSuperEncrypted(bool superEncrypted)293 void Blob::setSuperEncrypted(bool superEncrypted) {
294     mBlob->flags = setFlag(mBlob->flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
295 }
296 
setCriticalToDeviceEncryption(bool critical)297 void Blob::setCriticalToDeviceEncryption(bool critical) {
298     mBlob->flags = setFlag(mBlob->flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
299 }
300 
setFallback(bool fallback)301 void Blob::setFallback(bool fallback) {
302     if (fallback) {
303         mBlob->flags |= KEYSTORE_FLAG_FALLBACK;
304     } else {
305         mBlob->flags &= ~KEYSTORE_FLAG_FALLBACK;
306     }
307 }
308 
writeBlob(const std::string & filename,Blob blob,blobv3 * rawBlob,const std::vector<uint8_t> & aes_key,State state)309 static ResponseCode writeBlob(const std::string& filename, Blob blob, blobv3* rawBlob,
310                               const std::vector<uint8_t>& aes_key, State state) {
311     ALOGV("writing blob %s", filename.c_str());
312 
313     const size_t dataLength = rawBlob->length;
314     rawBlob->length = htonl(rawBlob->length);
315 
316     if (blob.isEncrypted() || blob.isSuperEncrypted()) {
317         if (state != STATE_NO_ERROR) {
318             ALOGD("couldn't insert encrypted blob while not unlocked");
319             return ResponseCode::LOCKED;
320         }
321 
322         memset(rawBlob->initialization_vector, 0, AES_BLOCK_SIZE);
323         if (!RAND_bytes(rawBlob->initialization_vector, kGcmIvSizeBytes)) {
324             ALOGW("Could not read random data for: %s", filename.c_str());
325             return ResponseCode::SYSTEM_ERROR;
326         }
327 
328         auto rc = AES_gcm_encrypt(rawBlob->value /* in */, rawBlob->value /* out */, dataLength,
329                                   aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
330         if (rc != ResponseCode::NO_ERROR) return rc;
331     }
332 
333     size_t fileLength = offsetof(blobv3, value) + dataLength + rawBlob->info;
334 
335     char tmpFileName[] = ".tmpXXXXXX";
336     {
337         android::base::unique_fd out(TEMP_FAILURE_RETRY(mkstemp(tmpFileName)));
338         if (out < 0) {
339             LOG(ERROR) << "could not open temp file: " << tmpFileName
340                        << " for writing blob file: " << filename.c_str()
341                        << " because: " << strerror(errno);
342             return ResponseCode::SYSTEM_ERROR;
343         }
344 
345         const size_t writtenBytes =
346             writeFully(out, reinterpret_cast<uint8_t*>(rawBlob), fileLength);
347 
348         if (writtenBytes != fileLength) {
349             LOG(ERROR) << "blob not fully written " << writtenBytes << " != " << fileLength;
350             unlink(tmpFileName);
351             return ResponseCode::SYSTEM_ERROR;
352         }
353     }
354 
355     if (rename(tmpFileName, filename.c_str()) == -1) {
356         LOG(ERROR) << "could not rename blob file to " << filename
357                    << " because: " << strerror(errno);
358         unlink(tmpFileName);
359         return ResponseCode::SYSTEM_ERROR;
360     }
361 
362     fsyncDirectory(getContainingDirectory(filename));
363 
364     return ResponseCode::NO_ERROR;
365 }
366 
writeBlobs(Blob keyBlob,Blob characteristicsBlob,const std::vector<uint8_t> & aes_key,State state) const367 ResponseCode LockedKeyBlobEntry::writeBlobs(Blob keyBlob, Blob characteristicsBlob,
368                                             const std::vector<uint8_t>& aes_key,
369                                             State state) const {
370     if (entry_ == nullptr) {
371         return ResponseCode::SYSTEM_ERROR;
372     }
373     ResponseCode rc;
374     if (keyBlob) {
375         blobv3* rawBlob = keyBlob.mBlob.get();
376         rc = writeBlob(entry_->getKeyBlobPath(), std::move(keyBlob), rawBlob, aes_key, state);
377         if (rc != ResponseCode::NO_ERROR) {
378             return rc;
379         }
380     }
381 
382     if (characteristicsBlob) {
383         blobv3* rawBlob = characteristicsBlob.mBlob.get();
384         rc = writeBlob(entry_->getCharacteristicsBlobPath(), std::move(characteristicsBlob),
385                        rawBlob, aes_key, state);
386     }
387     return rc;
388 }
389 
readBlob(const std::string & filename,const std::vector<uint8_t> & aes_key,State state)390 ResponseCode Blob::readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key,
391                             State state) {
392     ResponseCode rc;
393     ALOGV("reading blob %s", filename.c_str());
394     std::unique_ptr<blobv3> rawBlob = std::make_unique<blobv3>();
395 
396     const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
397     if (in < 0) {
398         return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
399     }
400 
401     // fileLength may be less than sizeof(mBlob)
402     const size_t fileLength = readFully(in, (uint8_t*)rawBlob.get(), sizeof(blobv3));
403     if (close(in) != 0) {
404         return ResponseCode::SYSTEM_ERROR;
405     }
406 
407     if (fileLength == 0) {
408         LOG(ERROR) << __func__ << " VALUE_CORRUPTED file length == 0";
409         return ResponseCode::VALUE_CORRUPTED;
410     }
411 
412     if (rawBlobIsEncrypted(*rawBlob)) {
413         if (state == STATE_LOCKED) {
414             mBlob = std::move(rawBlob);
415             return ResponseCode::LOCKED;
416         }
417         if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED;
418     }
419 
420     if (fileLength < offsetof(blobv3, value)) {
421         LOG(ERROR) << __func__ << " VALUE_CORRUPTED blob file too short: " << fileLength;
422         return ResponseCode::VALUE_CORRUPTED;
423     }
424 
425     if (rawBlob->version == 3) {
426         const ssize_t encryptedLength = ntohl(rawBlob->length);
427 
428         if (rawBlobIsEncrypted(*rawBlob)) {
429             rc = AES_gcm_decrypt(rawBlob->value /* in */, rawBlob->value /* out */, encryptedLength,
430                                  aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
431             if (rc != ResponseCode::NO_ERROR) {
432                 // If the blob was superencrypted and decryption failed, it is
433                 // almost certain that decryption is failing due to a user's
434                 // changed master key.
435                 if ((rawBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED) &&
436                     (rc == ResponseCode::VALUE_CORRUPTED)) {
437                     return ResponseCode::KEY_PERMANENTLY_INVALIDATED;
438                 }
439                 LOG(ERROR) << __func__ << " AES_gcm_decrypt returned: " << uint32_t(rc);
440 
441                 return rc;
442             }
443         }
444     } else if (rawBlob->version < 3) {
445         blobv2& v2blob = reinterpret_cast<blobv2&>(*rawBlob);
446         const size_t headerLength = offsetof(blobv2, encrypted);
447         const ssize_t encryptedLength = fileLength - headerLength - v2blob.info;
448         if (encryptedLength < 0) {
449             LOG(ERROR) << __func__ << " VALUE_CORRUPTED v2blob file too short";
450             return ResponseCode::VALUE_CORRUPTED;
451         }
452 
453         if (rawBlobIsEncrypted(*rawBlob)) {
454             if (encryptedLength % AES_BLOCK_SIZE != 0) {
455                 LOG(ERROR) << __func__
456                            << " VALUE_CORRUPTED encrypted length is not a multiple"
457                               " of the AES block size";
458                 return ResponseCode::VALUE_CORRUPTED;
459             }
460 
461             AES_KEY key;
462             AES_set_decrypt_key(aes_key.data(), kAesKeySize * 8, &key);
463             AES_cbc_encrypt(v2blob.encrypted, v2blob.encrypted, encryptedLength, &key,
464                             v2blob.vector, AES_DECRYPT);
465             key = {};  // clear key
466 
467             uint8_t computedDigest[MD5_DIGEST_LENGTH];
468             ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
469             MD5(v2blob.digested, digestedLength, computedDigest);
470             if (memcmp(v2blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
471                 LOG(ERROR) << __func__ << " v2blob MD5 digest mismatch";
472                 return ResponseCode::VALUE_CORRUPTED;
473             }
474         }
475     }
476 
477     const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - rawBlob->info;
478     rawBlob->length = ntohl(rawBlob->length);
479     if (rawBlob->length < 0 || rawBlob->length > maxValueLength ||
480         rawBlob->length + rawBlob->info + AES_BLOCK_SIZE >
481             static_cast<ssize_t>(sizeof(rawBlob->value))) {
482         LOG(ERROR) << __func__ << " raw blob length is out of bounds";
483         return ResponseCode::VALUE_CORRUPTED;
484     }
485 
486     if (rawBlob->info != 0 && rawBlob->version < 3) {
487         // move info from after padding to after data
488         memmove(rawBlob->value + rawBlob->length, rawBlob->value + maxValueLength, rawBlob->info);
489     }
490 
491     mBlob = std::move(rawBlob);
492     return ResponseCode::NO_ERROR;
493 }
494 
495 std::tuple<ResponseCode, Blob, Blob>
readBlobs(const std::vector<uint8_t> & aes_key,State state) const496 LockedKeyBlobEntry::readBlobs(const std::vector<uint8_t>& aes_key, State state) const {
497     std::tuple<ResponseCode, Blob, Blob> result;
498     auto& [rc, keyBlob, characteristicsBlob] = result;
499     if (entry_ == nullptr) return rc = ResponseCode::SYSTEM_ERROR, result;
500 
501     rc = keyBlob.readBlob(entry_->getKeyBlobPath(), aes_key, state);
502     if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::UNINITIALIZED) {
503         return result;
504     }
505 
506     if (entry_->hasCharacteristicsBlob()) {
507         characteristicsBlob.readBlob(entry_->getCharacteristicsBlobPath(), aes_key, state);
508     }
509     return result;
510 }
511 
deleteBlobs() const512 ResponseCode LockedKeyBlobEntry::deleteBlobs() const {
513     if (entry_ == nullptr) return ResponseCode::NO_ERROR;
514 
515     // always try to delete both
516     ResponseCode rc1 = (unlink(entry_->getKeyBlobPath().c_str()) && errno != ENOENT)
517                            ? ResponseCode::SYSTEM_ERROR
518                            : ResponseCode::NO_ERROR;
519     if (rc1 != ResponseCode::NO_ERROR) {
520         ALOGW("Failed to delete key blob file \"%s\"", entry_->getKeyBlobPath().c_str());
521     }
522     ResponseCode rc2 = (unlink(entry_->getCharacteristicsBlobPath().c_str()) && errno != ENOENT)
523                            ? ResponseCode::SYSTEM_ERROR
524                            : ResponseCode::NO_ERROR;
525     if (rc2 != ResponseCode::NO_ERROR) {
526         ALOGW("Failed to delete key characteristics file \"%s\"",
527               entry_->getCharacteristicsBlobPath().c_str());
528     }
529     // then report the first error that occured
530     if (rc1 != ResponseCode::NO_ERROR) return rc1;
531     return rc2;
532 }
533 
getSecurityLevel() const534 keystore::SecurityLevel Blob::getSecurityLevel() const {
535     return keystore::flagsToSecurityLevel(mBlob->flags);
536 }
537 
setSecurityLevel(keystore::SecurityLevel secLevel)538 void Blob::setSecurityLevel(keystore::SecurityLevel secLevel) {
539     mBlob->flags &= ~(KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX);
540     mBlob->flags |= keystore::securityLevelToFlags(secLevel);
541 }
542 
543 std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet>
getKeyCharacteristics() const544 Blob::getKeyCharacteristics() const {
545     std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet> result;
546     auto& [success, hwEnforced, swEnforced] = result;
547     success = false;
548     ArrayStreamBuffer buf(mBlob->value);
549     std::istream in(&buf);
550 
551     // only the characteristics cache has both sets
552     if (getType() == TYPE_KEY_CHARACTERISTICS_CACHE) {
553         hwEnforced.Deserialize(&in);
554     } else if (getType() != TYPE_KEY_CHARACTERISTICS) {
555         // if its not the cache and not the legacy characteristics file we have no business
556         // here
557         return result;
558     }
559     swEnforced.Deserialize(&in);
560     success = !in.bad();
561 
562     return result;
563 }
putKeyCharacteristics(const keystore::AuthorizationSet & hwEnforced,const keystore::AuthorizationSet & swEnforced)564 bool Blob::putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced,
565                                  const keystore::AuthorizationSet& swEnforced) {
566     if (!mBlob) mBlob = std::make_unique<blobv3>();
567     mBlob->version = CURRENT_BLOB_VERSION;
568     ArrayStreamBuffer buf(mBlob->value);
569     std::ostream out(&buf);
570     hwEnforced.Serialize(&out);
571     swEnforced.Serialize(&out);
572     if (out.bad()) return false;
573     setType(TYPE_KEY_CHARACTERISTICS_CACHE);
574     mBlob->length = out.tellp();
575     return true;
576 }
577 
put(const KeyBlobEntry & entry)578 void LockedKeyBlobEntry::put(const KeyBlobEntry& entry) {
579     std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
580     locked_blobs_.erase(entry);
581     lock.unlock();
582     locked_blobs_mutex_cond_var_.notify_all();
583 }
584 
~LockedKeyBlobEntry()585 LockedKeyBlobEntry::~LockedKeyBlobEntry() {
586     if (entry_ != nullptr) put(*entry_);
587 }
588 
get(KeyBlobEntry entry)589 LockedKeyBlobEntry LockedKeyBlobEntry::get(KeyBlobEntry entry) {
590     std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
591     locked_blobs_mutex_cond_var_.wait(
592         lock, [&] { return locked_blobs_.find(entry) == locked_blobs_.end(); });
593     auto [iterator, success] = locked_blobs_.insert(std::move(entry));
594     if (!success) return {};
595     return LockedKeyBlobEntry(*iterator);
596 }
597 
598 std::set<KeyBlobEntry> LockedKeyBlobEntry::locked_blobs_;
599 std::mutex LockedKeyBlobEntry::locked_blobs_mutex_;
600 std::condition_variable LockedKeyBlobEntry::locked_blobs_mutex_cond_var_;
601 
602 /* Here is the encoding of key names. This is necessary in order to allow arbitrary
603  * characters in key names. Characters in [0-~] are not encoded. Others are encoded
604  * into two bytes. The first byte is one of [+-.] which represents the first
605  * two bits of the character. The second byte encodes the rest of the bits into
606  * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
607  * that Base64 cannot be used here due to the need of prefix match on keys. */
608 
encodeKeyName(const std::string & keyName)609 std::string encodeKeyName(const std::string& keyName) {
610     std::string encodedName;
611     encodedName.reserve(keyName.size() * 2);
612     auto in = keyName.begin();
613     while (in != keyName.end()) {
614         // Input character needs to be encoded.
615         if (*in < '0' || *in > '~') {
616             // Encode the two most-significant bits of the input char in the first
617             // output character, by counting up from 43 ('+').
618             encodedName.append(1, '+' + (uint8_t(*in) >> 6));
619             // Encode the six least-significant bits of the input char in the second
620             // output character, by counting up from 48 ('0').
621             // This is safe because the maximum value is 112, which is the
622             // character 'p'.
623             encodedName.append(1, '0' + (*in & 0x3F));
624         } else {
625             // No need to encode input char - append as-is.
626             encodedName.append(1, *in);
627         }
628         ++in;
629     }
630     return encodedName;
631 }
632 
decodeKeyName(const std::string & encodedName)633 std::string decodeKeyName(const std::string& encodedName) {
634     std::string decodedName;
635     decodedName.reserve(encodedName.size());
636     auto in = encodedName.begin();
637     bool multichar = false;
638     char c;
639     while (in != encodedName.end()) {
640         if (multichar) {
641             // Second part of a multi-character encoding. Turn off the multichar
642             // flag and set the six least-significant bits of c to the value originally
643             // encoded by counting up from '0'.
644             multichar = false;
645             decodedName.append(1, c | (uint8_t(*in) - '0'));
646         } else if (*in >= '+' && *in <= '.') {
647             // First part of a multi-character encoding. Set the multichar flag
648             // and set the two most-significant bits of c to be the two bits originally
649             // encoded by counting up from '+'.
650             multichar = true;
651             c = (*in - '+') << 6;
652         } else {
653             // Regular character, append as-is.
654             decodedName.append(1, *in);
655         }
656         ++in;
657     }
658     // mulitchars at the end get truncated
659     return decodedName;
660 }
661 
getKeyBlobBaseName() const662 std::string KeyBlobEntry::getKeyBlobBaseName() const {
663     std::stringstream s;
664     if (masterkey_) {
665         s << alias_;
666     } else {
667         s << uid_ << "_" << encodeKeyName(alias_);
668     }
669     return s.str();
670 }
671 
getKeyBlobPath() const672 std::string KeyBlobEntry::getKeyBlobPath() const {
673     std::stringstream s;
674     if (masterkey_) {
675         s << user_dir_ << "/" << alias_;
676     } else {
677         s << user_dir_ << "/" << uid_ << "_" << encodeKeyName(alias_);
678     }
679     return s.str();
680 }
681 
getCharacteristicsBlobBaseName() const682 std::string KeyBlobEntry::getCharacteristicsBlobBaseName() const {
683     std::stringstream s;
684     if (!masterkey_) s << "." << uid_ << "_chr_" << encodeKeyName(alias_);
685     return s.str();
686 }
687 
getCharacteristicsBlobPath() const688 std::string KeyBlobEntry::getCharacteristicsBlobPath() const {
689     std::stringstream s;
690     if (!masterkey_)
691         s << user_dir_ << "/"
692           << "." << uid_ << "_chr_" << encodeKeyName(alias_);
693     return s.str();
694 }
695 
hasKeyBlob() const696 bool KeyBlobEntry::hasKeyBlob() const {
697     int trys = 3;
698     while (trys--) {
699         if (!access(getKeyBlobPath().c_str(), R_OK | W_OK)) return true;
700         if (errno == ENOENT) return false;
701         LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
702                      << " while checking for key blob";
703         if (errno != EAGAIN) break;
704     }
705     return false;
706 }
707 
hasCharacteristicsBlob() const708 bool KeyBlobEntry::hasCharacteristicsBlob() const {
709     int trys = 3;
710     while (trys--) {
711         if (!access(getCharacteristicsBlobPath().c_str(), R_OK | W_OK)) return true;
712         if (errno == ENOENT) return false;
713         LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
714                      << " while checking for key characteristics blob";
715         if (errno != EAGAIN) break;
716     }
717     return false;
718 }
719 
filename2UidAlias(const std::string & filepath)720 static std::tuple<bool, uid_t, std::string> filename2UidAlias(const std::string& filepath) {
721     std::tuple<bool, uid_t, std::string> result;
722 
723     auto& [success, uid, alias] = result;
724 
725     success = false;
726 
727     auto filenamebase = filepath.find_last_of('/');
728     std::string filename =
729         filenamebase == std::string::npos ? filepath : filepath.substr(filenamebase + 1);
730 
731     if (filename[0] == '.') return result;
732 
733     auto sep = filename.find('_');
734     if (sep == std::string::npos) return result;
735 
736     std::stringstream s(filename.substr(0, sep));
737     s >> uid;
738     if (!s) return result;
739 
740     alias = decodeKeyName(filename.substr(sep + 1));
741     success = true;
742     return result;
743 }
744 
745 std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>>
list(const std::string & user_dir,std::function<bool (uid_t,const std::string &)> filter)746 LockedKeyBlobEntry::list(const std::string& user_dir,
747                          std::function<bool(uid_t, const std::string&)> filter) {
748     std::list<LockedKeyBlobEntry> matches;
749 
750     // This is a fence against any concurrent database accesses during database iteration.
751     // Only the keystore thread can lock entries. So it cannot be starved
752     // by workers grabbing new individual locks. We just wait here until all
753     // workers have relinquished their locked files.
754     std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
755     locked_blobs_mutex_cond_var_.wait(lock, [&] { return locked_blobs_.empty(); });
756 
757     DIR* dir = opendir(user_dir.c_str());
758     if (!dir) {
759         ALOGW("can't open directory for user: %s", strerror(errno));
760         return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::SYSTEM_ERROR,
761                                                                          std::move(matches)};
762     }
763 
764     struct dirent* file;
765     while ((file = readdir(dir)) != nullptr) {
766         // We only care about files.
767         if (file->d_type != DT_REG) {
768             continue;
769         }
770 
771         // Skip anything that starts with a "."
772         if (file->d_name[0] == '.') {
773             continue;
774         }
775 
776         auto [success, uid, alias] = filename2UidAlias(file->d_name);
777 
778         if (!success) {
779             ALOGW("could not parse key filename \"%s\"", file->d_name);
780             continue;
781         }
782 
783         if (!filter(uid, alias)) continue;
784 
785         auto [iterator, dummy] = locked_blobs_.emplace(alias, user_dir, uid);
786         matches.push_back(*iterator);
787     }
788     closedir(dir);
789     return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::NO_ERROR,
790                                                                      std::move(matches)};
791 }
792