blob: f3ace0e79277823d709992888f74fce1c1cf5ffe [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "biod/fake_biometrics_manager.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <uuid/uuid.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/values.h>
namespace biod {
const std::string& FakeBiometricsManager::Record::GetId() const {
return id_;
}
const std::string& FakeBiometricsManager::Record::GetUserId() const {
InternalRecord* internal = GetInternal();
CHECK(internal)
<< "Attempted to get user ID for invalid BiometricsManager Record";
return internal->user_id;
}
const std::string& FakeBiometricsManager::Record::GetLabel() const {
InternalRecord* internal = GetInternal();
CHECK(internal)
<< "Attempted to get label for invalid BiometricsManager Record";
return internal->label;
}
bool FakeBiometricsManager::Record::SetLabel(std::string label) {
InternalRecord* internal = GetInternal();
if (internal) {
internal->label = std::move(label);
// Set label by overwriting records in file.
return biometrics_manager_->biod_storage_.WriteRecord(
*this,
std::unique_ptr<base::Value>(new base::StringValue("Hello, world!")));
}
LOG(ERROR) << "Attempt to set label for invalid BiometricsManager Record";
return false;
}
bool FakeBiometricsManager::Record::Remove() {
// Delete one record.
if (biometrics_manager_->biod_storage_.DeleteRecord(
biometrics_manager_->records_[id_].user_id, id_)) {
return biometrics_manager_->records_.erase(id_) > 0;
} else {
return false;
}
}
FakeBiometricsManager::InternalRecord*
FakeBiometricsManager::Record::GetInternal() const {
if (!biometrics_manager_)
return nullptr;
auto internal_record = biometrics_manager_->records_.find(id_);
if (internal_record == biometrics_manager_->records_.end())
return nullptr;
return &internal_record->second;
}
FakeBiometricsManager::FakeBiometricsManager()
: session_weak_factory_(this),
weak_factory_(this),
biod_storage_(kFakeBiometricsManagerName,
base::Bind(&FakeBiometricsManager::LoadRecord,
base::Unretained(this))) {
const char kFakeInputPath[] = "/tmp/fake_biometric";
base::DeleteFile(base::FilePath(kFakeInputPath), false);
CHECK_EQ(mkfifo(kFakeInputPath, 0600), 0)
<< "Failed to create FakeBiometricsManager input";
// The pipe gets opened read/write to avoid triggering a constant stream of
// POLLHUP after the pipe is opened writable and closed. The pipe is never
// actually written to here.
fake_input_ = base::ScopedFD(open(kFakeInputPath, O_RDWR | O_NONBLOCK));
CHECK_GE(fake_input_.get(), 0)
<< "Failed to open FakeBiometricsManager input";
fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
CHECK(base::MessageLoopForIO::current()->WatchFileDescriptor(
fake_input_.get(),
true,
base::MessageLoopForIO::WATCH_READ,
fd_watcher_.get(),
this))
<< "Failed to watch FakeBiometricsManager input";
}
BiometricType FakeBiometricsManager::GetType() {
return BIOMETRIC_TYPE_FINGERPRINT;
}
BiometricsManager::EnrollSession FakeBiometricsManager::StartEnrollSession(
std::string user_id, std::string label) {
if (mode_ == Mode::kNone) {
mode_ = Mode::kEnrollSession;
next_internal_record_ = {std::move(user_id), std::move(label)};
return BiometricsManager::EnrollSession(session_weak_factory_.GetWeakPtr());
}
return BiometricsManager::EnrollSession();
}
BiometricsManager::AuthSession FakeBiometricsManager::StartAuthSession() {
if (mode_ == Mode::kNone) {
mode_ = Mode::kAuthSession;
return BiometricsManager::AuthSession(session_weak_factory_.GetWeakPtr());
}
return BiometricsManager::AuthSession();
}
std::vector<std::unique_ptr<BiometricsManager::Record>>
FakeBiometricsManager::GetRecords() {
std::vector<std::unique_ptr<BiometricsManager::Record>> records(
records_.size());
std::transform(records_.begin(),
records_.end(),
records.begin(),
[this](decltype(records_)::value_type& record) {
return std::unique_ptr<BiometricsManager::Record>(
new Record(weak_factory_.GetWeakPtr(), record.first));
});
return records;
}
bool FakeBiometricsManager::DestroyAllRecords() {
// Enumerate through records_ and delete each record.
bool delete_all_records = true;
for (auto& record_pair : records_) {
delete_all_records &= biod_storage_.DeleteRecord(record_pair.second.user_id,
record_pair.first);
}
records_.clear();
return delete_all_records;
}
void FakeBiometricsManager::RemoveRecordsFromMemory() {
records_.clear();
}
bool FakeBiometricsManager::ReadRecords(
const std::unordered_set<std::string>& user_ids) {
return biod_storage_.ReadRecords(user_ids);
}
void FakeBiometricsManager::SetEnrollScanDoneHandler(
const BiometricsManager::EnrollScanDoneCallback& on_enroll_scan_done) {
on_enroll_scan_done_ = on_enroll_scan_done;
}
void FakeBiometricsManager::SetAuthScanDoneHandler(
const BiometricsManager::AuthScanDoneCallback& on_auth_scan_done) {
on_auth_scan_done_ = on_auth_scan_done;
}
void FakeBiometricsManager::SetSessionFailedHandler(
const BiometricsManager::SessionFailedCallback& on_session_failed) {
on_session_failed_ = on_session_failed;
}
void FakeBiometricsManager::EndEnrollSession() {
CHECK(mode_ == Mode::kEnrollSession);
session_weak_factory_.InvalidateWeakPtrs();
mode_ = Mode::kNone;
}
void FakeBiometricsManager::EndAuthSession() {
CHECK(mode_ == Mode::kAuthSession);
session_weak_factory_.InvalidateWeakPtrs();
mode_ = Mode::kNone;
}
void FakeBiometricsManager::OnFileCanWriteWithoutBlocking(int fd) {
NOTREACHED();
}
void FakeBiometricsManager::OnFileCanReadWithoutBlocking(int fd) {
// We scan the stream for the magic bytes in case the previous input command
// was not the correct length or malformed for whatever reason. This must be
// done a single byte at a time because the input stream of bytes is totally
// unaligned. Reading the length of magic bytes at once might consume some
// garbage data and the start of the magic bytes, but that would fail to
// validate, and subsequent reads would never see that correct instance of
// magic bytes.
size_t magic_index = 0;
const uint8_t magic_start[] = {FAKE_BIOMETRIC_MAGIC_BYTES};
while (magic_index < sizeof(magic_start)) {
uint8_t magic;
if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
return;
if (magic == magic_start[magic_index])
magic_index++;
else
magic_index = 0;
}
uint8_t cmd;
if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))
return;
switch (cmd) {
case 'A': {
uint8_t res_code;
if (read(fd, &res_code, sizeof(res_code)) != sizeof(res_code))
return;
ScanResult res = static_cast<ScanResult>(res_code);
uint8_t match_user_count;
if (read(fd, &match_user_count, sizeof(match_user_count)) !=
sizeof(match_user_count))
return;
BiometricsManager::AttemptMatches matches;
for (uint8_t match_index = 0; match_index < match_user_count;
match_index++) {
uint8_t user_id_size;
if (read(fd, &user_id_size, sizeof(user_id_size)) !=
sizeof(user_id_size))
return;
std::string user_id(user_id_size, '\0');
if (read(fd, &user_id.front(), user_id.size()) != user_id.size())
return;
// These record IDs are interpreted as record identifiers by biod and
// its clients.
std::vector<std::string>& record_ids = matches[user_id];
uint8_t record_id_count;
if (read(fd, &record_id_count, sizeof(record_id_count)) !=
sizeof(record_id_count))
return;
for (uint8_t record_id_index = 0; record_id_index < record_id_count;
record_id_index++) {
uint8_t record_id_size;
if (read(fd, &record_id_size, sizeof(record_id_size)) !=
sizeof(record_id_size))
return;
std::string record_id(record_id_size, '\0');
int ret = read(fd, &record_id.front(), record_id.size());
if (ret != record_id.size()) {
LOG(ERROR) << "failed to read record id " << errno;
return;
}
record_ids.emplace_back(std::move(record_id));
}
std::string record_ids_joined;
for (const auto& record_id : record_ids) {
record_ids_joined += " \"" + record_id + "\"";
}
LOG(INFO) << "Recognized User " << user_id
<< " with record ids:" << record_ids_joined;
}
if (!on_auth_scan_done_.is_null() && mode_ == Mode::kAuthSession)
on_auth_scan_done_.Run(res, matches);
return;
}
case 'S': {
uint8_t res_code;
if (read(fd, &res_code, sizeof(res_code)) != sizeof(res_code))
return;
ScanResult res = static_cast<ScanResult>(res_code);
uint8_t done;
if (read(fd, &done, sizeof(done)) != sizeof(done))
return;
LOG(INFO) << "Scan result " << static_cast<int>(res_code) << " done "
<< static_cast<bool>(done);
if (mode_ == Mode::kEnrollSession) {
if (done) {
std::string record_id(biod_storage_.GenerateNewRecordId());
records_[record_id] = std::move(next_internal_record_);
Record current_record(weak_factory_.GetWeakPtr(), record_id);
if (!biod_storage_.WriteRecord(
current_record,
std::unique_ptr<base::Value>(
new base::StringValue("Hello, world!")))) {
records_.erase(record_id);
}
mode_ = Mode::kNone;
session_weak_factory_.InvalidateWeakPtrs();
}
if (!on_enroll_scan_done_.is_null()) {
BiometricsManager::EnrollStatus enroll_status = {done > 0, -1};
on_enroll_scan_done_.Run(res, enroll_status);
}
}
return;
}
case 'F':
LOG(INFO) << "Fake failure";
if (!on_session_failed_.is_null())
on_session_failed_.Run();
}
}
bool FakeBiometricsManager::LoadRecord(const std::string& user_id,
const std::string& label,
const std::string& record_id,
const base::Value& data) {
InternalRecord internal_record = {user_id, label};
records_[record_id] = std::move(internal_record);
LOG(INFO) << "Load record " << record_id << " from disk.";
return true;
}
} // namespace biod