/* * Copyright (C) 2022 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. */ #include "FakeFingerprintEngine.h" #include #include "Fingerprint.h" #include #include #include #include "util/CancellationSignal.h" #include "util/Util.h" using namespace ::android::fingerprint::virt; using ::android::base::ParseInt; namespace aidl::android::hardware::biometrics::fingerprint { FakeFingerprintEngine::FakeFingerprintEngine() : mRandom(std::mt19937::default_seed), mWorkMode(WorkMode::kIdle) {} void FakeFingerprintEngine::generateChallengeImpl(ISessionCallback* cb) { BEGIN_OP(0); std::uniform_int_distribution dist; auto challenge = dist(mRandom); FingerprintHalProperties::challenge(challenge); cb->onChallengeGenerated(challenge); } void FakeFingerprintEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) { BEGIN_OP(0); FingerprintHalProperties::challenge({}); cb->onChallengeRevoked(challenge); } void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, const std::future& cancel) { BEGIN_OP(0); // Do proper HAT verification in the real implementation. if (hat.mac.empty()) { LOG(ERROR) << "Fail: hat"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } updateContext(WorkMode::kEnroll, cb, const_cast&>(cancel), 0, hat); } void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t operationId, const std::future& cancel) { BEGIN_OP(0); updateContext(WorkMode::kAuthenticate, cb, const_cast&>(cancel), operationId, keymaster::HardwareAuthToken()); } void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb, const std::future& cancel) { BEGIN_OP(0); auto detectInteractionSupported = FingerprintHalProperties::detect_interaction().value_or(false); if (!detectInteractionSupported) { LOG(ERROR) << "Detect interaction is not supported"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } updateContext(WorkMode::kDetectInteract, cb, const_cast&>(cancel), 0, keymaster::HardwareAuthToken()); } void FakeFingerprintEngine::updateContext(WorkMode mode, ISessionCallback* cb, std::future& cancel, int64_t operationId, const keymaster::HardwareAuthToken& hat) { mCancel = std::move(cancel); mWorkMode = mode; mCb = cb; mOperationId = operationId; mHat = hat; } void FakeFingerprintEngine::fingerDownAction() { bool isTerminal = false; LOG(INFO) << __func__; switch (mWorkMode) { case WorkMode::kAuthenticate: isTerminal = onAuthenticateFingerDown(mCb, mOperationId, mCancel); break; case WorkMode::kEnroll: isTerminal = onEnrollFingerDown(mCb, mHat, mCancel); break; case WorkMode::kDetectInteract: isTerminal = onDetectInteractFingerDown(mCb, mCancel); break; default: LOG(WARNING) << "unexpected mode: on fingerDownAction(), " << (int)mWorkMode; break; } if (isTerminal) { mWorkMode = WorkMode::kIdle; } } bool FakeFingerprintEngine::onEnrollFingerDown(ISessionCallback* cb, const keymaster::HardwareAuthToken&, const std::future& cancel) { BEGIN_OP(getLatency(FingerprintHalProperties::operation_enroll_latency())); // Force error-out auto err = FingerprintHalProperties::operation_enroll_error().value_or(0); if (err != 0) { LOG(ERROR) << "Fail: operation_enroll_error"; auto ec = convertError(err); cb->onError(ec.first, ec.second); return true; } // Format is ":,...: auto nextEnroll = FingerprintHalProperties::next_enrollment().value_or(""); auto parts = Util::split(nextEnroll, ":"); if (parts.size() != 3) { LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll; cb->onError(Error::VENDOR, 0 /* vendorError */); return true; } auto enrollmentId = std::stoi(parts[0]); auto progress = parseEnrollmentCapture(parts[1]); for (size_t i = 0; i < progress.size(); i += 2) { auto left = (progress.size() - i) / 2 - 1; auto duration = progress[i][0]; auto acquired = progress[i + 1]; auto N = acquired.size(); for (int j = 0; j < N; j++) { SLEEP_MS(duration / N); if (shouldCancel(cancel)) { LOG(ERROR) << "Fail: cancel"; cb->onError(Error::CANCELED, 0 /* vendorCode */); return true; } auto ac = convertAcquiredInfo(acquired[j]); cb->onAcquired(ac.first, ac.second); } if (left == 0 && !IS_TRUE(parts[2])) { // end and failed LOG(ERROR) << "Fail: requested by caller: " << nextEnroll; FingerprintHalProperties::next_enrollment({}); cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); } else { // progress and update props if last time LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left; if (left == 0) { auto enrollments = FingerprintHalProperties::enrollments(); enrollments.emplace_back(enrollmentId); FingerprintHalProperties::enrollments(enrollments); FingerprintHalProperties::next_enrollment({}); // change authenticatorId after new enrollment auto id = FingerprintHalProperties::authenticator_id().value_or(0); auto newId = id + 1; FingerprintHalProperties::authenticator_id(newId); LOG(INFO) << "Enrolled: " << enrollmentId; } cb->onEnrollmentProgress(enrollmentId, left); } } return true; } bool FakeFingerprintEngine::onAuthenticateFingerDown(ISessionCallback* cb, int64_t /* operationId */, const std::future& cancel) { BEGIN_OP(getLatency(FingerprintHalProperties::operation_authenticate_latency())); int64_t now = Util::getSystemNanoTime(); int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10); auto acquired = FingerprintHalProperties::operation_authenticate_acquired().value_or("1"); auto acquiredInfos = parseIntSequence(acquired); int N = acquiredInfos.size(); if (N == 0) { LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return true; } // got lockout? if (checkSensorLockout(cb)) { return FakeLockoutTracker::LockoutMode::kPermanent == mLockoutTracker.getMode(); } int i = 0; do { if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) { LOG(ERROR) << "Fail: operation_authenticate_fails"; mLockoutTracker.addFailedAttempt(); cb->onAuthenticationFailed(); return false; } auto err = FingerprintHalProperties::operation_authenticate_error().value_or(0); if (err != 0) { LOG(ERROR) << "Fail: operation_authenticate_error"; auto ec = convertError(err); cb->onError(ec.first, ec.second); return true; /* simply terminating current operation for any user inserted error, revisit if tests need*/ } if (FingerprintHalProperties::lockout().value_or(false)) { LOG(ERROR) << "Fail: lockout"; cb->onLockoutPermanent(); cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */); return true; } if (shouldCancel(cancel)) { LOG(ERROR) << "Fail: cancel"; cb->onError(Error::CANCELED, 0 /* vendorCode */); return true; } if (i < N) { auto ac = convertAcquiredInfo(acquiredInfos[i]); cb->onAcquired(ac.first, ac.second); i++; } SLEEP_MS(duration / N); } while (!Util::hasElapsed(now, duration)); auto id = FingerprintHalProperties::enrollment_hit().value_or(0); auto enrolls = FingerprintHalProperties::enrollments(); auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end(); if (id > 0 && isEnrolled) { cb->onAuthenticationSucceeded(id, {} /* hat */); mLockoutTracker.reset(); return true; } else { LOG(ERROR) << "Fail: fingerprint not enrolled"; cb->onAuthenticationFailed(); mLockoutTracker.addFailedAttempt(); checkSensorLockout(cb); return false; } } bool FakeFingerprintEngine::onDetectInteractFingerDown(ISessionCallback* cb, const std::future& cancel) { BEGIN_OP(getLatency(FingerprintHalProperties::operation_detect_interaction_latency())); int64_t duration = FingerprintHalProperties::operation_detect_interaction_duration().value_or(10); auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1"); auto acquiredInfos = parseIntSequence(acquired); int N = acquiredInfos.size(); int64_t now = Util::getSystemNanoTime(); if (N == 0) { LOG(ERROR) << "Fail to parse detect interaction acquired info: " + acquired; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return true; } int i = 0; do { auto err = FingerprintHalProperties::operation_detect_interaction_error().value_or(0); if (err != 0) { LOG(ERROR) << "Fail: operation_detect_interaction_error"; auto ec = convertError(err); cb->onError(ec.first, ec.second); return true; } if (shouldCancel(cancel)) { LOG(ERROR) << "Fail: cancel"; cb->onError(Error::CANCELED, 0 /* vendorCode */); return true; } if (i < N) { auto ac = convertAcquiredInfo(acquiredInfos[i]); cb->onAcquired(ac.first, ac.second); i++; } SLEEP_MS(duration / N); } while (!Util::hasElapsed(now, duration)); auto id = FingerprintHalProperties::enrollment_hit().value_or(0); auto enrolls = FingerprintHalProperties::enrollments(); auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end(); if (id <= 0 || !isEnrolled) { LOG(ERROR) << "Fail: not enrolled"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return true; } cb->onInteractionDetected(); return true; } void FakeFingerprintEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) { BEGIN_OP(0); std::vector ids; for (auto& enrollment : FingerprintHalProperties::enrollments()) { auto id = enrollment.value_or(0); if (id > 0) { ids.push_back(id); } } cb->onEnrollmentsEnumerated(ids); } void FakeFingerprintEngine::removeEnrollmentsImpl(ISessionCallback* cb, const std::vector& enrollmentIds) { BEGIN_OP(0); std::vector> newEnrollments; std::vector removed; for (auto& enrollment : FingerprintHalProperties::enrollments()) { auto id = enrollment.value_or(0); if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) != enrollmentIds.end()) { removed.push_back(id); } else if (id > 0) { newEnrollments.emplace_back(id); } } FingerprintHalProperties::enrollments(newEnrollments); cb->onEnrollmentsRemoved(enrollmentIds); } void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); int64_t authenticatorId; if (FingerprintHalProperties::enrollments().size() == 0) { authenticatorId = 0; } else { authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0); if (authenticatorId == 0) authenticatorId = 1; } cb->onAuthenticatorIdRetrieved(authenticatorId); } void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); int64_t newId; if (FingerprintHalProperties::enrollments().size() == 0) { newId = 0; } else { auto id = FingerprintHalProperties::authenticator_id().value_or(0); newId = id + 1; } FingerprintHalProperties::authenticator_id(newId); cb->onAuthenticatorIdInvalidated(newId); } void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat) { BEGIN_OP(0); if (hat.mac.empty()) { LOG(ERROR) << "Fail: hat in resetLockout()"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } clearLockout(cb); isLockoutTimerAborted = true; } void FakeFingerprintEngine::clearLockout(ISessionCallback* cb) { FingerprintHalProperties::lockout(false); cb->onLockoutCleared(); mLockoutTracker.reset(); } ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/, int32_t /*y*/, float /*minor*/, float /*major*/) { BEGIN_OP(0); fingerDownAction(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus FakeFingerprintEngine::onPointerUpImpl(int32_t /*pointerId*/) { BEGIN_OP(0); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus FakeFingerprintEngine::onUiReadyImpl() { BEGIN_OP(0); return ndk::ScopedAStatus::ok(); } bool FakeFingerprintEngine::getSensorLocationConfig(SensorLocation& out) { auto loc = FingerprintHalProperties::sensor_location().value_or(""); auto isValidStr = false; auto dim = Util::split(loc, ":"); if (dim.size() < 3 or dim.size() > 4) { if (!loc.empty()) LOG(WARNING) << "Invalid sensor location input (x:y:radius):" + loc; return false; } else { int32_t x, y, r; std::string d = ""; if (dim.size() >= 3) { isValidStr = ParseInt(dim[0], &x) && ParseInt(dim[1], &y) && ParseInt(dim[2], &r); } if (dim.size() >= 4) { d = dim[3]; } if (isValidStr) out = {.sensorLocationX = x, .sensorLocationY = y, .sensorRadius = r, .display = d}; return isValidStr; } } SensorLocation FakeFingerprintEngine::getSensorLocation() { SensorLocation location; if (getSensorLocationConfig(location)) { return location; } else { return defaultSensorLocation(); } } SensorLocation FakeFingerprintEngine::defaultSensorLocation() { return SensorLocation(); } std::vector FakeFingerprintEngine::parseIntSequence(const std::string& str, const std::string& sep) { std::vector seqs = Util::split(str, sep); std::vector res; for (const auto& seq : seqs) { int32_t val; if (ParseInt(seq, &val)) { res.push_back(val); } else { LOG(WARNING) << "Invalid int sequence:" + str; res.clear(); break; } } return res; } bool FakeFingerprintEngine::parseEnrollmentCaptureSingle(const std::string& str, std::vector>& res) { std::vector defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD}; bool aborted = true; do { std::smatch sms; // Parses strings like "1000-[5,1]" or "500" std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)"); if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break; int32_t duration; if (!ParseInt(sms.str(2), &duration)) break; res.push_back({duration}); if (!sms.str(4).empty()) { auto acqv = parseIntSequence(sms.str(4)); if (acqv.empty()) break; res.push_back(acqv); } else res.push_back(defaultAcquiredInfo); aborted = false; } while (0); return !aborted; } std::vector> FakeFingerprintEngine::parseEnrollmentCapture( const std::string& str) { std::vector> res; std::string s(str); s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); bool aborted = false; std::smatch sms; // Parses strings like "1000-[5,1],500,800-[6,5,1]" // ---------- --- ----------- // into parts: A B C while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) { if (!parseEnrollmentCaptureSingle(sms.str(2), res)) { aborted = true; break; } s = sms.suffix(); } if (aborted || s.length() != 0) { res.clear(); LOG(ERROR) << "Failed to parse enrollment captures:" + str; } return res; } std::pair FakeFingerprintEngine::convertAcquiredInfo(int32_t code) { std::pair res; if (code > FINGERPRINT_ACQUIRED_VENDOR_BASE) { res.first = AcquiredInfo::VENDOR; res.second = code - FINGERPRINT_ACQUIRED_VENDOR_BASE; } else { res.first = (AcquiredInfo)code; res.second = 0; } return res; } std::pair FakeFingerprintEngine::convertError(int32_t code) { std::pair res; if (code > FINGERPRINT_ERROR_VENDOR_BASE) { res.first = Error::VENDOR; res.second = code - FINGERPRINT_ERROR_VENDOR_BASE; } else { res.first = (Error)code; res.second = 0; } return res; } int32_t FakeFingerprintEngine::getLatency( const std::vector>& latencyIn) { int32_t res = DEFAULT_LATENCY; std::vector latency; for (auto x : latencyIn) if (x.has_value()) latency.push_back(*x); switch (latency.size()) { case 0: break; case 1: res = latency[0]; break; case 2: res = getRandomInRange(latency[0], latency[1]); break; default: LOG(ERROR) << "ERROR: unexpected input of size " << latency.size(); break; } return res; } int32_t FakeFingerprintEngine::getRandomInRange(int32_t bound1, int32_t bound2) { std::uniform_int_distribution dist(std::min(bound1, bound2), std::max(bound1, bound2)); return dist(mRandom); } bool FakeFingerprintEngine::checkSensorLockout(ISessionCallback* cb) { FakeLockoutTracker::LockoutMode lockoutMode = mLockoutTracker.getMode(); if (lockoutMode == FakeLockoutTracker::LockoutMode::kPermanent) { LOG(ERROR) << "Fail: lockout permanent"; cb->onLockoutPermanent(); isLockoutTimerAborted = true; return true; } else if (lockoutMode == FakeLockoutTracker::LockoutMode::kTimed) { int64_t timeLeft = mLockoutTracker.getLockoutTimeLeft(); LOG(ERROR) << "Fail: lockout timed " << timeLeft; cb->onLockoutTimed(timeLeft); if (isLockoutTimerSupported && !isLockoutTimerStarted) startLockoutTimer(timeLeft, cb); return true; } return false; } void FakeFingerprintEngine::startLockoutTimer(int64_t timeout, ISessionCallback* cb) { BEGIN_OP(0); std::function action = std::bind(&FakeFingerprintEngine::lockoutTimerExpired, this, std::placeholders::_1); std::thread([timeout, action, cb]() { std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); action(cb); }).detach(); isLockoutTimerStarted = true; } void FakeFingerprintEngine::lockoutTimerExpired(ISessionCallback* cb) { if (!isLockoutTimerAborted) { clearLockout(cb); } isLockoutTimerStarted = false; isLockoutTimerAborted = false; } } // namespace aidl::android::hardware::biometrics::fingerprint