diff options
Diffstat (limited to 'keystore/blob.cpp')
-rw-r--r-- | keystore/blob.cpp | 791 |
1 files changed, 0 insertions, 791 deletions
diff --git a/keystore/blob.cpp b/keystore/blob.cpp deleted file mode 100644 index ffdb454b..00000000 --- a/keystore/blob.cpp +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#define LOG_TAG "keystore" - -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> - -#include <log/log.h> - -#include "blob.h" - -#include "keystore_utils.h" - -#include <openssl/evp.h> -#include <openssl/rand.h> - -#include <istream> -#include <ostream> -#include <streambuf> -#include <string> - -#include <android-base/logging.h> -#include <android-base/unique_fd.h> - -namespace { - -constexpr size_t kGcmIvSizeBytes = 96 / 8; - -#if defined(__clang__) -#define OPTNONE __attribute__((optnone)) -#elif defined(__GNUC__) -#define OPTNONE __attribute__((optimize("O0"))) -#else -#error Need a definition for OPTNONE -#endif - -class ArrayEraser { - public: - ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {} - OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); } - - private: - volatile uint8_t* mArr; - size_t mSize; -}; - -/** - * Returns a EVP_CIPHER appropriate for the given key, based on the key's size. - */ -const EVP_CIPHER* getAesCipherForKey(const std::vector<uint8_t>& key) { - const EVP_CIPHER* cipher = EVP_aes_256_gcm(); - if (key.size() == kAes128KeySizeBytes) { - cipher = EVP_aes_128_gcm(); - } - return cipher; -} - -/* - * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at - * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to - * 'tag'. - */ -ResponseCode 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) { - - // There can be 128-bit and 256-bit keys - const EVP_CIPHER* cipher = getAesCipherForKey(key); - - bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new()); - - EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv); - EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */); - - std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]); - uint8_t* out_pos = out_tmp.get(); - int out_len; - - EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len); - out_pos += out_len; - EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len); - out_pos += out_len; - if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) { - ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len, - out_pos - out_tmp.get()); - return ResponseCode::SYSTEM_ERROR; - } - - std::copy(out_tmp.get(), out_pos, out); - EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag); - - return ResponseCode::NO_ERROR; -} - -/* - * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at - * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same - * location as 'in'). - */ -ResponseCode 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) { - - // There can be 128-bit and 256-bit keys - const EVP_CIPHER* cipher = getAesCipherForKey(key); - - bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new()); - - EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv); - EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */); - EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag)); - - std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]); - ArrayEraser out_eraser(out_tmp.get(), len); - uint8_t* out_pos = out_tmp.get(); - int out_len; - - EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len); - out_pos += out_len; - if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) { - ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted"); - return ResponseCode::VALUE_CORRUPTED; - } - out_pos += out_len; - if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) { - ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len, - out_pos - out_tmp.get()); - return ResponseCode::VALUE_CORRUPTED; - } - - std::copy(out_tmp.get(), out_pos, out); - - return ResponseCode::NO_ERROR; -} - -class ArrayStreamBuffer : public std::streambuf { - public: - template <typename T, size_t size> explicit ArrayStreamBuffer(const T (&data)[size]) { - static_assert(sizeof(T) == 1, "Array element size too large"); - std::streambuf::char_type* d = const_cast<std::streambuf::char_type*>( - reinterpret_cast<const std::streambuf::char_type*>(&data[0])); - setg(d, d, d + size); - setp(d, d + size); - } - - protected: - pos_type seekoff(off_type off, std::ios_base::seekdir dir, - std::ios_base::openmode which = std::ios_base::in | - std::ios_base::out) override { - bool in = which & std::ios_base::in; - bool out = which & std::ios_base::out; - if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1; - std::streambuf::char_type* newPosPtr; - switch (dir) { - case std::ios_base::beg: - newPosPtr = pbase(); - break; - case std::ios_base::cur: - // if dir == cur then in xor out due to - // if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1; above - if (in) - newPosPtr = gptr(); - else - newPosPtr = pptr(); - break; - case std::ios_base::end: - // in and out bounds are the same and cannot change, so we can take either range - // regardless of the value of "which" - newPosPtr = epptr(); - break; - } - newPosPtr += off; - if (newPosPtr < pbase() || newPosPtr > epptr()) return -1; - if (in) { - gbump(newPosPtr - gptr()); - } - if (out) { - pbump(newPosPtr - pptr()); - } - return newPosPtr - pbase(); - } -}; - -} // namespace - -Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength, - BlobType type) { - mBlob = std::make_unique<blobv3>(); - memset(mBlob.get(), 0, sizeof(blobv3)); - if (valueLength > kValueSize) { - valueLength = kValueSize; - ALOGW("Provided blob length too large"); - } - if (infoLength + valueLength > kValueSize) { - infoLength = kValueSize - valueLength; - ALOGW("Provided info length too large"); - } - mBlob->length = valueLength; - memcpy(mBlob->value, value, valueLength); - - mBlob->info = infoLength; - memcpy(mBlob->value + valueLength, info, infoLength); - - mBlob->version = CURRENT_BLOB_VERSION; - mBlob->type = uint8_t(type); - - if (type == TYPE_MASTER_KEY || type == TYPE_MASTER_KEY_AES256) { - mBlob->flags = KEYSTORE_FLAG_ENCRYPTED; - } else { - mBlob->flags = KEYSTORE_FLAG_NONE; - } -} - -Blob::Blob(blobv3 b) { - mBlob = std::make_unique<blobv3>(b); -} - -Blob::Blob() { - if (mBlob) *mBlob = {}; -} - -Blob::Blob(const Blob& rhs) { - if (rhs.mBlob) { - mBlob = std::make_unique<blobv3>(*rhs.mBlob); - } -} - -Blob::Blob(Blob&& rhs) : mBlob(std::move(rhs.mBlob)) {} - -Blob& Blob::operator=(const Blob& rhs) { - if (&rhs != this) { - if (mBlob) *mBlob = {}; - if (rhs) { - mBlob = std::make_unique<blobv3>(*rhs.mBlob); - } else { - mBlob = {}; - } - } - return *this; -} - -Blob& Blob::operator=(Blob&& rhs) { - if (mBlob) *mBlob = {}; - mBlob = std::move(rhs.mBlob); - return *this; -} - -template <typename BlobType> static bool rawBlobIsEncrypted(const BlobType& blob) { - if (blob.version < 2) return true; - - return blob.flags & (KEYSTORE_FLAG_ENCRYPTED | KEYSTORE_FLAG_SUPER_ENCRYPTED); -} - -bool Blob::isEncrypted() const { - if (mBlob->version < 2) { - return true; - } - - return mBlob->flags & KEYSTORE_FLAG_ENCRYPTED; -} - -bool Blob::isSuperEncrypted() const { - return mBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED; -} - -bool Blob::isCriticalToDeviceEncryption() const { - return mBlob->flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; -} - -inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) { - return set ? (flags | flag) : (flags & ~flag); -} - -void Blob::setEncrypted(bool encrypted) { - mBlob->flags = setFlag(mBlob->flags, encrypted, KEYSTORE_FLAG_ENCRYPTED); -} - -void Blob::setSuperEncrypted(bool superEncrypted) { - mBlob->flags = setFlag(mBlob->flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED); -} - -void Blob::setCriticalToDeviceEncryption(bool critical) { - mBlob->flags = setFlag(mBlob->flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION); -} - -void Blob::setFallback(bool fallback) { - if (fallback) { - mBlob->flags |= KEYSTORE_FLAG_FALLBACK; - } else { - mBlob->flags &= ~KEYSTORE_FLAG_FALLBACK; - } -} - -static ResponseCode writeBlob(const std::string& filename, Blob blob, blobv3* rawBlob, - const std::vector<uint8_t>& aes_key, State state) { - ALOGV("writing blob %s", filename.c_str()); - - const size_t dataLength = rawBlob->length; - rawBlob->length = htonl(rawBlob->length); - - if (blob.isEncrypted() || blob.isSuperEncrypted()) { - if (state != STATE_NO_ERROR) { - ALOGD("couldn't insert encrypted blob while not unlocked"); - return ResponseCode::LOCKED; - } - - memset(rawBlob->initialization_vector, 0, AES_BLOCK_SIZE); - if (!RAND_bytes(rawBlob->initialization_vector, kGcmIvSizeBytes)) { - ALOGW("Could not read random data for: %s", filename.c_str()); - return ResponseCode::SYSTEM_ERROR; - } - - auto rc = AES_gcm_encrypt(rawBlob->value /* in */, rawBlob->value /* out */, dataLength, - aes_key, rawBlob->initialization_vector, rawBlob->aead_tag); - if (rc != ResponseCode::NO_ERROR) return rc; - } - - size_t fileLength = offsetof(blobv3, value) + dataLength + rawBlob->info; - - char tmpFileName[] = ".tmpXXXXXX"; - { - android::base::unique_fd out(TEMP_FAILURE_RETRY(mkstemp(tmpFileName))); - if (out < 0) { - LOG(ERROR) << "could not open temp file: " << tmpFileName - << " for writing blob file: " << filename.c_str() - << " because: " << strerror(errno); - return ResponseCode::SYSTEM_ERROR; - } - - const size_t writtenBytes = - writeFully(out, reinterpret_cast<uint8_t*>(rawBlob), fileLength); - - if (writtenBytes != fileLength) { - LOG(ERROR) << "blob not fully written " << writtenBytes << " != " << fileLength; - unlink(tmpFileName); - return ResponseCode::SYSTEM_ERROR; - } - } - - if (rename(tmpFileName, filename.c_str()) == -1) { - LOG(ERROR) << "could not rename blob file to " << filename - << " because: " << strerror(errno); - unlink(tmpFileName); - return ResponseCode::SYSTEM_ERROR; - } - - fsyncDirectory(getContainingDirectory(filename)); - - return ResponseCode::NO_ERROR; -} - -ResponseCode LockedKeyBlobEntry::writeBlobs(Blob keyBlob, Blob characteristicsBlob, - const std::vector<uint8_t>& aes_key, - State state) const { - if (entry_ == nullptr) { - return ResponseCode::SYSTEM_ERROR; - } - ResponseCode rc; - if (keyBlob) { - blobv3* rawBlob = keyBlob.mBlob.get(); - rc = writeBlob(entry_->getKeyBlobPath(), std::move(keyBlob), rawBlob, aes_key, state); - if (rc != ResponseCode::NO_ERROR) { - return rc; - } - } - - if (characteristicsBlob) { - blobv3* rawBlob = characteristicsBlob.mBlob.get(); - rc = writeBlob(entry_->getCharacteristicsBlobPath(), std::move(characteristicsBlob), - rawBlob, aes_key, state); - } - return rc; -} - -ResponseCode Blob::readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key, - State state) { - ResponseCode rc; - ALOGV("reading blob %s", filename.c_str()); - std::unique_ptr<blobv3> rawBlob = std::make_unique<blobv3>(); - - const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)); - if (in < 0) { - return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR; - } - - // fileLength may be less than sizeof(mBlob) - const size_t fileLength = readFully(in, (uint8_t*)rawBlob.get(), sizeof(blobv3)); - if (close(in) != 0) { - return ResponseCode::SYSTEM_ERROR; - } - - if (fileLength == 0) { - LOG(ERROR) << __func__ << " VALUE_CORRUPTED file length == 0"; - return ResponseCode::VALUE_CORRUPTED; - } - - if (rawBlobIsEncrypted(*rawBlob)) { - if (state == STATE_LOCKED) { - mBlob = std::move(rawBlob); - return ResponseCode::LOCKED; - } - if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED; - } - - if (fileLength < offsetof(blobv3, value)) { - LOG(ERROR) << __func__ << " VALUE_CORRUPTED blob file too short: " << fileLength; - return ResponseCode::VALUE_CORRUPTED; - } - - if (rawBlob->version == 3) { - const ssize_t encryptedLength = ntohl(rawBlob->length); - - if (rawBlobIsEncrypted(*rawBlob)) { - rc = AES_gcm_decrypt(rawBlob->value /* in */, rawBlob->value /* out */, encryptedLength, - aes_key, rawBlob->initialization_vector, rawBlob->aead_tag); - if (rc != ResponseCode::NO_ERROR) { - // If the blob was superencrypted and decryption failed, it is - // almost certain that decryption is failing due to a user's - // changed master key. - if ((rawBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED) && - (rc == ResponseCode::VALUE_CORRUPTED)) { - return ResponseCode::KEY_PERMANENTLY_INVALIDATED; - } - LOG(ERROR) << __func__ << " AES_gcm_decrypt returned: " << uint32_t(rc); - - return rc; - } - } - } else if (rawBlob->version < 3) { - blobv2& v2blob = reinterpret_cast<blobv2&>(*rawBlob); - const size_t headerLength = offsetof(blobv2, encrypted); - const ssize_t encryptedLength = fileLength - headerLength - v2blob.info; - if (encryptedLength < 0) { - LOG(ERROR) << __func__ << " VALUE_CORRUPTED v2blob file too short"; - return ResponseCode::VALUE_CORRUPTED; - } - - if (rawBlobIsEncrypted(*rawBlob)) { - if (encryptedLength % AES_BLOCK_SIZE != 0) { - LOG(ERROR) << __func__ - << " VALUE_CORRUPTED encrypted length is not a multiple" - " of the AES block size"; - return ResponseCode::VALUE_CORRUPTED; - } - - AES_KEY key; - AES_set_decrypt_key(aes_key.data(), kAesKeySize * 8, &key); - AES_cbc_encrypt(v2blob.encrypted, v2blob.encrypted, encryptedLength, &key, - v2blob.vector, AES_DECRYPT); - key = {}; // clear key - - uint8_t computedDigest[MD5_DIGEST_LENGTH]; - ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH; - MD5(v2blob.digested, digestedLength, computedDigest); - if (memcmp(v2blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) { - LOG(ERROR) << __func__ << " v2blob MD5 digest mismatch"; - return ResponseCode::VALUE_CORRUPTED; - } - } - } - - const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - rawBlob->info; - rawBlob->length = ntohl(rawBlob->length); - if (rawBlob->length < 0 || rawBlob->length > maxValueLength || - rawBlob->length + rawBlob->info + AES_BLOCK_SIZE > - static_cast<ssize_t>(sizeof(rawBlob->value))) { - LOG(ERROR) << __func__ << " raw blob length is out of bounds"; - return ResponseCode::VALUE_CORRUPTED; - } - - if (rawBlob->info != 0 && rawBlob->version < 3) { - // move info from after padding to after data - memmove(rawBlob->value + rawBlob->length, rawBlob->value + maxValueLength, rawBlob->info); - } - - mBlob = std::move(rawBlob); - return ResponseCode::NO_ERROR; -} - -std::tuple<ResponseCode, Blob, Blob> -LockedKeyBlobEntry::readBlobs(const std::vector<uint8_t>& aes_key, State state) const { - std::tuple<ResponseCode, Blob, Blob> result; - auto& [rc, keyBlob, characteristicsBlob] = result; - if (entry_ == nullptr) return rc = ResponseCode::SYSTEM_ERROR, result; - - rc = keyBlob.readBlob(entry_->getKeyBlobPath(), aes_key, state); - if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::UNINITIALIZED) { - return result; - } - - if (entry_->hasCharacteristicsBlob()) { - characteristicsBlob.readBlob(entry_->getCharacteristicsBlobPath(), aes_key, state); - } - return result; -} - -ResponseCode LockedKeyBlobEntry::deleteBlobs() const { - if (entry_ == nullptr) return ResponseCode::NO_ERROR; - - // always try to delete both - ResponseCode rc1 = (unlink(entry_->getKeyBlobPath().c_str()) && errno != ENOENT) - ? ResponseCode::SYSTEM_ERROR - : ResponseCode::NO_ERROR; - if (rc1 != ResponseCode::NO_ERROR) { - ALOGW("Failed to delete key blob file \"%s\"", entry_->getKeyBlobPath().c_str()); - } - ResponseCode rc2 = (unlink(entry_->getCharacteristicsBlobPath().c_str()) && errno != ENOENT) - ? ResponseCode::SYSTEM_ERROR - : ResponseCode::NO_ERROR; - if (rc2 != ResponseCode::NO_ERROR) { - ALOGW("Failed to delete key characteristics file \"%s\"", - entry_->getCharacteristicsBlobPath().c_str()); - } - // then report the first error that occured - if (rc1 != ResponseCode::NO_ERROR) return rc1; - return rc2; -} - -keystore::SecurityLevel Blob::getSecurityLevel() const { - return keystore::flagsToSecurityLevel(mBlob->flags); -} - -void Blob::setSecurityLevel(keystore::SecurityLevel secLevel) { - mBlob->flags &= ~(KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX); - mBlob->flags |= keystore::securityLevelToFlags(secLevel); -} - -std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet> -Blob::getKeyCharacteristics() const { - std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet> result; - auto& [success, hwEnforced, swEnforced] = result; - success = false; - ArrayStreamBuffer buf(mBlob->value); - std::istream in(&buf); - - // only the characteristics cache has both sets - if (getType() == TYPE_KEY_CHARACTERISTICS_CACHE) { - hwEnforced.Deserialize(&in); - } else if (getType() != TYPE_KEY_CHARACTERISTICS) { - // if its not the cache and not the legacy characteristics file we have no business - // here - return result; - } - swEnforced.Deserialize(&in); - success = !in.bad(); - - return result; -} -bool Blob::putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced, - const keystore::AuthorizationSet& swEnforced) { - if (!mBlob) mBlob = std::make_unique<blobv3>(); - mBlob->version = CURRENT_BLOB_VERSION; - ArrayStreamBuffer buf(mBlob->value); - std::ostream out(&buf); - hwEnforced.Serialize(&out); - swEnforced.Serialize(&out); - if (out.bad()) return false; - setType(TYPE_KEY_CHARACTERISTICS_CACHE); - mBlob->length = out.tellp(); - return true; -} - -void LockedKeyBlobEntry::put(const KeyBlobEntry& entry) { - std::unique_lock<std::mutex> lock(locked_blobs_mutex_); - locked_blobs_.erase(entry); - lock.unlock(); - locked_blobs_mutex_cond_var_.notify_all(); -} - -LockedKeyBlobEntry::~LockedKeyBlobEntry() { - if (entry_ != nullptr) put(*entry_); -} - -LockedKeyBlobEntry LockedKeyBlobEntry::get(KeyBlobEntry entry) { - std::unique_lock<std::mutex> lock(locked_blobs_mutex_); - locked_blobs_mutex_cond_var_.wait( - lock, [&] { return locked_blobs_.find(entry) == locked_blobs_.end(); }); - auto [iterator, success] = locked_blobs_.insert(std::move(entry)); - if (!success) return {}; - return LockedKeyBlobEntry(*iterator); -} - -std::set<KeyBlobEntry> LockedKeyBlobEntry::locked_blobs_; -std::mutex LockedKeyBlobEntry::locked_blobs_mutex_; -std::condition_variable LockedKeyBlobEntry::locked_blobs_mutex_cond_var_; - -/* Here is the encoding of key names. This is necessary in order to allow arbitrary - * characters in key names. Characters in [0-~] are not encoded. Others are encoded - * into two bytes. The first byte is one of [+-.] which represents the first - * two bits of the character. The second byte encodes the rest of the bits into - * [0-o]. Therefore in the worst case the length of a key gets doubled. Note - * that Base64 cannot be used here due to the need of prefix match on keys. */ - -std::string encodeKeyName(const std::string& keyName) { - std::string encodedName; - encodedName.reserve(keyName.size() * 2); - auto in = keyName.begin(); - while (in != keyName.end()) { - // Input character needs to be encoded. - if (*in < '0' || *in > '~') { - // Encode the two most-significant bits of the input char in the first - // output character, by counting up from 43 ('+'). - encodedName.append(1, '+' + (uint8_t(*in) >> 6)); - // Encode the six least-significant bits of the input char in the second - // output character, by counting up from 48 ('0'). - // This is safe because the maximum value is 112, which is the - // character 'p'. - encodedName.append(1, '0' + (*in & 0x3F)); - } else { - // No need to encode input char - append as-is. - encodedName.append(1, *in); - } - ++in; - } - return encodedName; -} - -std::string decodeKeyName(const std::string& encodedName) { - std::string decodedName; - decodedName.reserve(encodedName.size()); - auto in = encodedName.begin(); - bool multichar = false; - char c; - while (in != encodedName.end()) { - if (multichar) { - // Second part of a multi-character encoding. Turn off the multichar - // flag and set the six least-significant bits of c to the value originally - // encoded by counting up from '0'. - multichar = false; - decodedName.append(1, c | (uint8_t(*in) - '0')); - } else if (*in >= '+' && *in <= '.') { - // First part of a multi-character encoding. Set the multichar flag - // and set the two most-significant bits of c to be the two bits originally - // encoded by counting up from '+'. - multichar = true; - c = (*in - '+') << 6; - } else { - // Regular character, append as-is. - decodedName.append(1, *in); - } - ++in; - } - // mulitchars at the end get truncated - return decodedName; -} - -std::string KeyBlobEntry::getKeyBlobBaseName() const { - std::stringstream s; - if (masterkey_) { - s << alias_; - } else { - s << uid_ << "_" << encodeKeyName(alias_); - } - return s.str(); -} - -std::string KeyBlobEntry::getKeyBlobPath() const { - std::stringstream s; - if (masterkey_) { - s << user_dir_ << "/" << alias_; - } else { - s << user_dir_ << "/" << uid_ << "_" << encodeKeyName(alias_); - } - return s.str(); -} - -std::string KeyBlobEntry::getCharacteristicsBlobBaseName() const { - std::stringstream s; - if (!masterkey_) s << "." << uid_ << "_chr_" << encodeKeyName(alias_); - return s.str(); -} - -std::string KeyBlobEntry::getCharacteristicsBlobPath() const { - std::stringstream s; - if (!masterkey_) - s << user_dir_ << "/" - << "." << uid_ << "_chr_" << encodeKeyName(alias_); - return s.str(); -} - -bool KeyBlobEntry::hasKeyBlob() const { - int trys = 3; - while (trys--) { - if (!access(getKeyBlobPath().c_str(), R_OK | W_OK)) return true; - if (errno == ENOENT) return false; - LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")" - << " while checking for key blob"; - if (errno != EAGAIN) break; - } - return false; -} - -bool KeyBlobEntry::hasCharacteristicsBlob() const { - int trys = 3; - while (trys--) { - if (!access(getCharacteristicsBlobPath().c_str(), R_OK | W_OK)) return true; - if (errno == ENOENT) return false; - LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")" - << " while checking for key characteristics blob"; - if (errno != EAGAIN) break; - } - return false; -} - -static std::tuple<bool, uid_t, std::string> filename2UidAlias(const std::string& filepath) { - std::tuple<bool, uid_t, std::string> result; - - auto& [success, uid, alias] = result; - - success = false; - - auto filenamebase = filepath.find_last_of('/'); - std::string filename = - filenamebase == std::string::npos ? filepath : filepath.substr(filenamebase + 1); - - if (filename[0] == '.') return result; - - auto sep = filename.find('_'); - if (sep == std::string::npos) return result; - - std::stringstream s(filename.substr(0, sep)); - s >> uid; - if (!s) return result; - - alias = decodeKeyName(filename.substr(sep + 1)); - success = true; - return result; -} - -std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>> -LockedKeyBlobEntry::list(const std::string& user_dir, - std::function<bool(uid_t, const std::string&)> filter) { - std::list<LockedKeyBlobEntry> matches; - - // This is a fence against any concurrent database accesses during database iteration. - // Only the keystore thread can lock entries. So it cannot be starved - // by workers grabbing new individual locks. We just wait here until all - // workers have relinquished their locked files. - std::unique_lock<std::mutex> lock(locked_blobs_mutex_); - locked_blobs_mutex_cond_var_.wait(lock, [&] { return locked_blobs_.empty(); }); - - DIR* dir = opendir(user_dir.c_str()); - if (!dir) { - ALOGW("can't open directory for user: %s", strerror(errno)); - return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::SYSTEM_ERROR, - std::move(matches)}; - } - - struct dirent* file; - while ((file = readdir(dir)) != nullptr) { - // We only care about files. - if (file->d_type != DT_REG) { - continue; - } - - // Skip anything that starts with a "." - if (file->d_name[0] == '.') { - continue; - } - - auto [success, uid, alias] = filename2UidAlias(file->d_name); - - if (!success) { - ALOGW("could not parse key filename \"%s\"", file->d_name); - continue; - } - - if (!filter(uid, alias)) continue; - - auto [iterator, dummy] = locked_blobs_.emplace(alias, user_dir, uid); - matches.push_back(*iterator); - } - closedir(dir); - return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::NO_ERROR, - std::move(matches)}; -} |