summaryrefslogtreecommitdiff
path: root/keystore/blob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'keystore/blob.cpp')
-rw-r--r--keystore/blob.cpp791
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)};
-}