upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
52
android/system/gatekeeper/Android.bp
Normal file
52
android/system/gatekeeper/Android.bp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// 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.
|
||||
|
||||
// libgatekeeper contains just the code necessary to communicate with a
|
||||
// GoogleGateKeeper implementation, e.g. one running in TrustZone.
|
||||
cc_library_shared {
|
||||
name: "libgatekeeper",
|
||||
vendor_available: true,
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
srcs: [
|
||||
"gatekeeper_messages.cpp",
|
||||
"gatekeeper.cpp",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-g",
|
||||
],
|
||||
header_libs: [
|
||||
"libhardware_headers",
|
||||
],
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
// TODO(krasin): reenable coverage flags, when the new Clang toolchain is released.
|
||||
// Currently, if enabled, these flags will cause an internal error in Clang.
|
||||
// Bug: 25119481
|
||||
clang_cflags: ["-fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp"],
|
||||
}
|
||||
|
||||
// libgatekeeper_static is an empty static library that exports
|
||||
// all of the files in gatekeeper as includes.
|
||||
cc_library_static {
|
||||
name: "libgatekeeper_static",
|
||||
export_include_dirs: [
|
||||
".",
|
||||
"include",
|
||||
],
|
||||
}
|
42
android/system/gatekeeper/CleanSpec.mk
Normal file
42
android/system/gatekeeper/CleanSpec.mk
Normal file
|
@ -0,0 +1,42 @@
|
|||
# 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.
|
||||
#
|
||||
|
||||
# If you don't need to do a full clean build but would like to touch
|
||||
# a file or delete some intermediate files, add a clean step to the end
|
||||
# of the list. These steps will only be run once, if they haven't been
|
||||
# run before.
|
||||
#
|
||||
# E.g.:
|
||||
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
|
||||
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
|
||||
#
|
||||
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
|
||||
# files that are missing or have been moved.
|
||||
#
|
||||
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
|
||||
# Use $(OUT_DIR) to refer to the "out" directory.
|
||||
#
|
||||
# If you need to re-do something that's already mentioned, just copy
|
||||
# the command and add it to the bottom of the list. E.g., if a change
|
||||
# that you made last week required touching a file and a change you
|
||||
# made today requires touching the same file, just copy the old
|
||||
# touch step and add it to the end of the list.
|
||||
#
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
||||
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.default.so)
|
||||
|
2
android/system/gatekeeper/OWNERS
Normal file
2
android/system/gatekeeper/OWNERS
Normal file
|
@ -0,0 +1,2 @@
|
|||
jdanis@google.com
|
||||
swillden@google.com
|
321
android/system/gatekeeper/gatekeeper.cpp
Normal file
321
android/system/gatekeeper/gatekeeper.cpp
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
#include <gatekeeper/UniquePtr.h>
|
||||
#include <gatekeeper/gatekeeper.h>
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#define DAY_IN_MS (1000 * 60 * 60 * 24)
|
||||
|
||||
namespace gatekeeper {
|
||||
|
||||
void GateKeeper::Enroll(const EnrollRequest &request, EnrollResponse *response) {
|
||||
if (response == NULL) return;
|
||||
|
||||
if (!request.provided_password.buffer.get()) {
|
||||
response->error = ERROR_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
secure_id_t user_id = 0;// todo: rename to policy
|
||||
uint32_t uid = request.user_id;
|
||||
|
||||
if (request.password_handle.buffer.get() == NULL) {
|
||||
// Password handle does not match what is stored, generate new SecureID
|
||||
GetRandom(&user_id, sizeof(secure_id_t));
|
||||
} else {
|
||||
password_handle_t *pw_handle =
|
||||
reinterpret_cast<password_handle_t *>(request.password_handle.buffer.get());
|
||||
|
||||
if (pw_handle->version > HANDLE_VERSION) {
|
||||
response->error = ERROR_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
user_id = pw_handle->user_id;
|
||||
|
||||
uint64_t timestamp = GetMillisecondsSinceBoot();
|
||||
|
||||
uint32_t timeout = 0;
|
||||
bool throttle = (pw_handle->version >= HANDLE_VERSION_THROTTLE);
|
||||
if (throttle) {
|
||||
bool throttle_secure = pw_handle->flags & HANDLE_FLAG_THROTTLE_SECURE;
|
||||
failure_record_t record;
|
||||
if (!GetFailureRecord(uid, user_id, &record, throttle_secure)) {
|
||||
response->error = ERROR_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ThrottleRequest(uid, timestamp, &record, throttle_secure, response)) return;
|
||||
|
||||
if (!IncrementFailureRecord(uid, user_id, timestamp, &record, throttle_secure)) {
|
||||
response->error = ERROR_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = ComputeRetryTimeout(&record);
|
||||
}
|
||||
|
||||
if (!DoVerify(pw_handle, request.enrolled_password)) {
|
||||
// incorrect old password
|
||||
if (throttle && timeout > 0) {
|
||||
response->SetRetryTimeout(timeout);
|
||||
} else {
|
||||
response->error = ERROR_INVALID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t flags = 0;
|
||||
if (ClearFailureRecord(uid, user_id, true)) {
|
||||
flags |= HANDLE_FLAG_THROTTLE_SECURE;
|
||||
} else {
|
||||
ClearFailureRecord(uid, user_id, false);
|
||||
}
|
||||
|
||||
salt_t salt;
|
||||
GetRandom(&salt, sizeof(salt));
|
||||
|
||||
SizedBuffer password_handle;
|
||||
if (!CreatePasswordHandle(&password_handle,
|
||||
salt, user_id, flags, HANDLE_VERSION, request.provided_password.buffer.get(),
|
||||
request.provided_password.length)) {
|
||||
response->error = ERROR_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
response->SetEnrolledPasswordHandle(&password_handle);
|
||||
}
|
||||
|
||||
void GateKeeper::Verify(const VerifyRequest &request, VerifyResponse *response) {
|
||||
if (response == NULL) return;
|
||||
|
||||
if (!request.provided_password.buffer.get() || !request.password_handle.buffer.get()) {
|
||||
response->error = ERROR_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
password_handle_t *password_handle = reinterpret_cast<password_handle_t *>(
|
||||
request.password_handle.buffer.get());
|
||||
|
||||
if (password_handle->version > HANDLE_VERSION) {
|
||||
response->error = ERROR_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
secure_id_t user_id = password_handle->user_id;
|
||||
secure_id_t authenticator_id = 0;
|
||||
uint32_t uid = request.user_id;
|
||||
|
||||
uint64_t timestamp = GetMillisecondsSinceBoot();
|
||||
|
||||
uint32_t timeout = 0;
|
||||
bool throttle = (password_handle->version >= HANDLE_VERSION_THROTTLE);
|
||||
bool throttle_secure = password_handle->flags & HANDLE_FLAG_THROTTLE_SECURE;
|
||||
if (throttle) {
|
||||
failure_record_t record;
|
||||
if (!GetFailureRecord(uid, user_id, &record, throttle_secure)) {
|
||||
response->error = ERROR_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ThrottleRequest(uid, timestamp, &record, throttle_secure, response)) return;
|
||||
|
||||
if (!IncrementFailureRecord(uid, user_id, timestamp, &record, throttle_secure)) {
|
||||
response->error = ERROR_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = ComputeRetryTimeout(&record);
|
||||
} else {
|
||||
response->request_reenroll = true;
|
||||
}
|
||||
|
||||
if (DoVerify(password_handle, request.provided_password)) {
|
||||
// Signature matches
|
||||
UniquePtr<uint8_t> auth_token_buffer;
|
||||
uint32_t auth_token_len;
|
||||
MintAuthToken(&auth_token_buffer, &auth_token_len, timestamp,
|
||||
user_id, authenticator_id, request.challenge);
|
||||
|
||||
SizedBuffer auth_token(auth_token_len);
|
||||
memcpy(auth_token.buffer.get(), auth_token_buffer.get(), auth_token_len);
|
||||
response->SetVerificationToken(&auth_token);
|
||||
if (throttle) ClearFailureRecord(uid, user_id, throttle_secure);
|
||||
} else {
|
||||
// compute the new timeout given the incremented record
|
||||
if (throttle && timeout > 0) {
|
||||
response->SetRetryTimeout(timeout);
|
||||
} else {
|
||||
response->error = ERROR_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GateKeeper::CreatePasswordHandle(SizedBuffer *password_handle_buffer, salt_t salt,
|
||||
secure_id_t user_id, uint64_t flags, uint8_t handle_version, const uint8_t *password,
|
||||
uint32_t password_length) {
|
||||
password_handle_buffer->buffer.reset(new uint8_t[sizeof(password_handle_t)]);
|
||||
password_handle_buffer->length = sizeof(password_handle_t);
|
||||
|
||||
password_handle_t *password_handle = reinterpret_cast<password_handle_t *>(
|
||||
password_handle_buffer->buffer.get());
|
||||
password_handle->version = handle_version;
|
||||
password_handle->salt = salt;
|
||||
password_handle->user_id = user_id;
|
||||
password_handle->flags = flags;
|
||||
password_handle->hardware_backed = IsHardwareBacked();
|
||||
|
||||
uint32_t metadata_length = sizeof(user_id) + sizeof(flags) + sizeof(HANDLE_VERSION);
|
||||
const size_t to_sign_size = password_length + metadata_length;
|
||||
UniquePtr<uint8_t> to_sign(new uint8_t[to_sign_size]);
|
||||
|
||||
if (to_sign.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(to_sign.get(), password_handle, metadata_length);
|
||||
memcpy(to_sign.get() + metadata_length, password, password_length);
|
||||
|
||||
const uint8_t *password_key = NULL;
|
||||
uint32_t password_key_length = 0;
|
||||
GetPasswordKey(&password_key, &password_key_length);
|
||||
|
||||
if (!password_key || password_key_length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputePasswordSignature(password_handle->signature, sizeof(password_handle->signature),
|
||||
password_key, password_key_length, to_sign.get(), to_sign_size, salt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GateKeeper::DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
|
||||
if (!password.buffer.get()) return false;
|
||||
|
||||
SizedBuffer provided_handle;
|
||||
if (!CreatePasswordHandle(&provided_handle, expected_handle->salt, expected_handle->user_id,
|
||||
expected_handle->flags, expected_handle->version,
|
||||
password.buffer.get(), password.length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
password_handle_t *generated_handle =
|
||||
reinterpret_cast<password_handle_t *>(provided_handle.buffer.get());
|
||||
return memcmp_s(generated_handle->signature, expected_handle->signature,
|
||||
sizeof(expected_handle->signature)) == 0;
|
||||
}
|
||||
|
||||
void GateKeeper::MintAuthToken(UniquePtr<uint8_t> *auth_token, uint32_t *length,
|
||||
uint64_t timestamp, secure_id_t user_id, secure_id_t authenticator_id,
|
||||
uint64_t challenge) {
|
||||
if (auth_token == NULL) return;
|
||||
|
||||
hw_auth_token_t *token = new hw_auth_token_t;
|
||||
SizedBuffer serialized_auth_token;
|
||||
|
||||
token->version = HW_AUTH_TOKEN_VERSION;
|
||||
token->challenge = challenge;
|
||||
token->user_id = user_id;
|
||||
token->authenticator_id = authenticator_id;
|
||||
token->authenticator_type = htonl(HW_AUTH_PASSWORD);
|
||||
token->timestamp = htobe64(timestamp);
|
||||
|
||||
const uint8_t *auth_token_key = NULL;
|
||||
uint32_t key_len = 0;
|
||||
if (GetAuthTokenKey(&auth_token_key, &key_len)) {
|
||||
uint32_t hash_len = (uint32_t)((uint8_t *)&token->hmac - (uint8_t *)token);
|
||||
ComputeSignature(token->hmac, sizeof(token->hmac), auth_token_key, key_len,
|
||||
reinterpret_cast<uint8_t *>(token), hash_len);
|
||||
delete[] auth_token_key;
|
||||
} else {
|
||||
memset(token->hmac, 0, sizeof(token->hmac));
|
||||
}
|
||||
|
||||
if (length != NULL) *length = sizeof(*token);
|
||||
auth_token->reset(reinterpret_cast<uint8_t *>(token));
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the timeout in milliseconds as a function of the failure
|
||||
* counter 'x' as follows:
|
||||
*
|
||||
* [0. 5) -> 0
|
||||
* 5 -> 30
|
||||
* [6, 10) -> 0
|
||||
* [11, 30) -> 30
|
||||
* [30, 140) -> 30 * (2^((x - 30)/10))
|
||||
* [140, inf) -> 1 day
|
||||
*
|
||||
*/
|
||||
uint32_t GateKeeper::ComputeRetryTimeout(const failure_record_t *record) {
|
||||
static const int failure_timeout_ms = 30000;
|
||||
if (record->failure_counter == 0) return 0;
|
||||
|
||||
if (record->failure_counter > 0 && record->failure_counter <= 10) {
|
||||
if (record->failure_counter % 5 == 0) {
|
||||
return failure_timeout_ms;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (record->failure_counter < 30) {
|
||||
return failure_timeout_ms;
|
||||
} else if (record->failure_counter < 140) {
|
||||
return failure_timeout_ms << ((record->failure_counter - 30) / 10);
|
||||
}
|
||||
|
||||
return DAY_IN_MS;
|
||||
}
|
||||
|
||||
bool GateKeeper::ThrottleRequest(uint32_t uid, uint64_t timestamp,
|
||||
failure_record_t *record, bool secure, GateKeeperMessage *response) {
|
||||
|
||||
uint64_t last_checked = record->last_checked_timestamp;
|
||||
uint32_t timeout = ComputeRetryTimeout(record);
|
||||
|
||||
if (timeout > 0) {
|
||||
// we have a pending timeout
|
||||
if (timestamp < last_checked + timeout && timestamp > last_checked) {
|
||||
// attempt before timeout expired, return remaining time
|
||||
response->SetRetryTimeout(timeout - (timestamp - last_checked));
|
||||
return true;
|
||||
} else if (timestamp <= last_checked) {
|
||||
// device was rebooted or timer reset, don't count as new failure but
|
||||
// reset timeout
|
||||
record->last_checked_timestamp = timestamp;
|
||||
if (!WriteFailureRecord(uid, record, secure)) {
|
||||
response->error = ERROR_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
response->SetRetryTimeout(timeout);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GateKeeper::IncrementFailureRecord(uint32_t uid, secure_id_t user_id, uint64_t timestamp,
|
||||
failure_record_t *record, bool secure) {
|
||||
record->secure_user_id = user_id;
|
||||
record->failure_counter++;
|
||||
record->last_checked_timestamp = timestamp;
|
||||
|
||||
return WriteFailureRecord(uid, record, secure);
|
||||
}
|
||||
} // namespace gatekeeper
|
||||
|
365
android/system/gatekeeper/gatekeeper_messages.cpp
Normal file
365
android/system/gatekeeper/gatekeeper_messages.cpp
Normal file
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gatekeeper/gatekeeper_messages.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gatekeeper {
|
||||
|
||||
/**
|
||||
* Methods for serializing/deserializing SizedBuffers
|
||||
*/
|
||||
|
||||
struct __attribute__((__packed__)) serial_header_t {
|
||||
uint32_t error;
|
||||
uint32_t user_id;
|
||||
};
|
||||
|
||||
static inline uint32_t serialized_buffer_size(const SizedBuffer &buf) {
|
||||
return sizeof(buf.length) + buf.length;
|
||||
}
|
||||
|
||||
static inline void append_to_buffer(uint8_t **buffer, const SizedBuffer *to_append) {
|
||||
memcpy(*buffer, &to_append->length, sizeof(to_append->length));
|
||||
*buffer += sizeof(to_append->length);
|
||||
if (to_append->length != 0) {
|
||||
memcpy(*buffer, to_append->buffer.get(), to_append->length);
|
||||
*buffer += to_append->length;
|
||||
}
|
||||
}
|
||||
|
||||
static inline gatekeeper_error_t read_from_buffer(const uint8_t **buffer, const uint8_t *end,
|
||||
SizedBuffer *target) {
|
||||
if (*buffer + sizeof(target->length) > end) return ERROR_INVALID;
|
||||
|
||||
memcpy(&target->length, *buffer, sizeof(target->length));
|
||||
*buffer += sizeof(target->length);
|
||||
if (target->length != 0) {
|
||||
const size_t buffer_size = end - *buffer;
|
||||
if (buffer_size < target->length) return ERROR_INVALID;
|
||||
|
||||
target->buffer.reset(new uint8_t[target->length]);
|
||||
memcpy(target->buffer.get(), *buffer, target->length);
|
||||
*buffer += target->length;
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
uint32_t GateKeeperMessage::GetSerializedSize() const {
|
||||
if (error == ERROR_NONE) {
|
||||
uint32_t size = sizeof(serial_header_t) + nonErrorSerializedSize();
|
||||
return size;
|
||||
} else {
|
||||
uint32_t size = sizeof(serial_header_t);
|
||||
if (error == ERROR_RETRY) {
|
||||
size += sizeof(retry_timeout);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GateKeeperMessage::Serialize(uint8_t *buffer, const uint8_t *end) const {
|
||||
uint32_t bytes_written = 0;
|
||||
if (buffer + GetSerializedSize() > end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
serial_header_t *header = reinterpret_cast<serial_header_t *>(buffer);
|
||||
if (error != ERROR_NONE) {
|
||||
if (buffer + sizeof(serial_header_t) > end) return 0;
|
||||
header->error = error;
|
||||
header->user_id = user_id;
|
||||
bytes_written += sizeof(*header);
|
||||
if (error == ERROR_RETRY) {
|
||||
memcpy(buffer + sizeof(serial_header_t), &retry_timeout, sizeof(retry_timeout));
|
||||
bytes_written += sizeof(retry_timeout);
|
||||
}
|
||||
} else {
|
||||
if (buffer + sizeof(serial_header_t) + nonErrorSerializedSize() > end)
|
||||
return 0;
|
||||
header->error = error;
|
||||
header->user_id = user_id;
|
||||
nonErrorSerialize(buffer + sizeof(*header));
|
||||
bytes_written += sizeof(*header) + nonErrorSerializedSize();
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
gatekeeper_error_t GateKeeperMessage::Deserialize(const uint8_t *payload, const uint8_t *end) {
|
||||
if (payload + sizeof(uint32_t) > end) return ERROR_INVALID;
|
||||
const serial_header_t *header = reinterpret_cast<const serial_header_t *>(payload);
|
||||
if (header->error == ERROR_NONE) {
|
||||
if (payload == end) return ERROR_INVALID;
|
||||
user_id = header->user_id;
|
||||
error = nonErrorDeserialize(payload + sizeof(*header), end);
|
||||
} else {
|
||||
error = static_cast<gatekeeper_error_t>(header->error);
|
||||
user_id = header->user_id;
|
||||
if (error == ERROR_RETRY) {
|
||||
if (payload + sizeof(serial_header_t) < end) {
|
||||
memcpy(&retry_timeout, payload + sizeof(serial_header_t), sizeof(retry_timeout));
|
||||
} else {
|
||||
retry_timeout = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void GateKeeperMessage::SetRetryTimeout(uint32_t retry_timeout) {
|
||||
this->retry_timeout = retry_timeout;
|
||||
this->error = ERROR_RETRY;
|
||||
}
|
||||
|
||||
VerifyRequest::VerifyRequest(uint32_t user_id, uint64_t challenge,
|
||||
SizedBuffer *enrolled_password_handle, SizedBuffer *provided_password_payload) {
|
||||
this->user_id = user_id;
|
||||
this->challenge = challenge;
|
||||
this->password_handle.buffer.reset(enrolled_password_handle->buffer.release());
|
||||
this->password_handle.length = enrolled_password_handle->length;
|
||||
this->provided_password.buffer.reset(provided_password_payload->buffer.release());
|
||||
this->provided_password.length = provided_password_payload->length;
|
||||
}
|
||||
|
||||
VerifyRequest::VerifyRequest() {
|
||||
memset_s(&password_handle, 0, sizeof(password_handle));
|
||||
memset_s(&provided_password, 0, sizeof(provided_password));
|
||||
}
|
||||
|
||||
VerifyRequest::~VerifyRequest() {
|
||||
if (password_handle.buffer.get()) {
|
||||
password_handle.buffer.reset();
|
||||
}
|
||||
|
||||
if (provided_password.buffer.get()) {
|
||||
memset_s(provided_password.buffer.get(), 0, provided_password.length);
|
||||
provided_password.buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VerifyRequest::nonErrorSerializedSize() const {
|
||||
return sizeof(challenge) + serialized_buffer_size(password_handle)
|
||||
+ serialized_buffer_size(provided_password);
|
||||
}
|
||||
|
||||
void VerifyRequest::nonErrorSerialize(uint8_t *buffer) const {
|
||||
memcpy(buffer, &challenge, sizeof(challenge));
|
||||
buffer += sizeof(challenge);
|
||||
append_to_buffer(&buffer, &password_handle);
|
||||
append_to_buffer(&buffer, &provided_password);
|
||||
}
|
||||
|
||||
gatekeeper_error_t VerifyRequest::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
|
||||
gatekeeper_error_t error = ERROR_NONE;
|
||||
|
||||
if (password_handle.buffer.get()) {
|
||||
password_handle.buffer.reset();
|
||||
}
|
||||
|
||||
if (provided_password.buffer.get()) {
|
||||
memset_s(provided_password.buffer.get(), 0, provided_password.length);
|
||||
provided_password.buffer.reset();
|
||||
}
|
||||
|
||||
memcpy(&challenge, payload, sizeof(challenge));
|
||||
payload += sizeof(challenge);
|
||||
|
||||
error = read_from_buffer(&payload, end, &password_handle);
|
||||
if (error != ERROR_NONE) return error;
|
||||
|
||||
return read_from_buffer(&payload, end, &provided_password);
|
||||
|
||||
}
|
||||
|
||||
VerifyResponse::VerifyResponse(uint32_t user_id, SizedBuffer *auth_token) {
|
||||
this->user_id = user_id;
|
||||
this->auth_token.buffer.reset(auth_token->buffer.release());
|
||||
this->auth_token.length = auth_token->length;
|
||||
this->request_reenroll = false;
|
||||
}
|
||||
|
||||
VerifyResponse::VerifyResponse() {
|
||||
request_reenroll = false;
|
||||
memset_s(&auth_token, 0, sizeof(auth_token));
|
||||
};
|
||||
|
||||
VerifyResponse::~VerifyResponse() {
|
||||
if (auth_token.length > 0) {
|
||||
auth_token.buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyResponse::SetVerificationToken(SizedBuffer *auth_token) {
|
||||
this->auth_token.buffer.reset(auth_token->buffer.release());
|
||||
this->auth_token.length = auth_token->length;
|
||||
}
|
||||
|
||||
uint32_t VerifyResponse::nonErrorSerializedSize() const {
|
||||
return serialized_buffer_size(auth_token) + sizeof(request_reenroll);
|
||||
}
|
||||
|
||||
void VerifyResponse::nonErrorSerialize(uint8_t *buffer) const {
|
||||
append_to_buffer(&buffer, &auth_token);
|
||||
memcpy(buffer, &request_reenroll, sizeof(request_reenroll));
|
||||
}
|
||||
|
||||
gatekeeper_error_t VerifyResponse::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
|
||||
if (auth_token.buffer.get()) {
|
||||
auth_token.buffer.reset();
|
||||
}
|
||||
|
||||
gatekeeper_error_t err = read_from_buffer(&payload, end, &auth_token);
|
||||
if (err != ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
|
||||
memcpy(&request_reenroll, payload, sizeof(request_reenroll));
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
EnrollRequest::EnrollRequest(uint32_t user_id, SizedBuffer *password_handle,
|
||||
SizedBuffer *provided_password, SizedBuffer *enrolled_password) {
|
||||
this->user_id = user_id;
|
||||
this->provided_password.buffer.reset(provided_password->buffer.release());
|
||||
this->provided_password.length = provided_password->length;
|
||||
|
||||
if (enrolled_password == NULL) {
|
||||
this->enrolled_password.buffer.reset();
|
||||
this->enrolled_password.length = 0;
|
||||
} else {
|
||||
this->enrolled_password.buffer.reset(enrolled_password->buffer.release());
|
||||
this->enrolled_password.length = enrolled_password->length;
|
||||
}
|
||||
|
||||
if (password_handle == NULL) {
|
||||
this->password_handle.buffer.reset();
|
||||
this->password_handle.length = 0;
|
||||
} else {
|
||||
this->password_handle.buffer.reset(password_handle->buffer.release());
|
||||
this->password_handle.length = password_handle->length;
|
||||
}
|
||||
}
|
||||
|
||||
EnrollRequest::EnrollRequest() {
|
||||
memset_s(&provided_password, 0, sizeof(provided_password));
|
||||
memset_s(&enrolled_password, 0, sizeof(enrolled_password));
|
||||
memset_s(&password_handle, 0, sizeof(password_handle));
|
||||
}
|
||||
|
||||
EnrollRequest::~EnrollRequest() {
|
||||
if (provided_password.buffer.get()) {
|
||||
memset_s(provided_password.buffer.get(), 0, provided_password.length);
|
||||
provided_password.buffer.reset();
|
||||
}
|
||||
|
||||
if (enrolled_password.buffer.get()) {
|
||||
memset_s(enrolled_password.buffer.get(), 0, enrolled_password.length);
|
||||
enrolled_password.buffer.reset();
|
||||
}
|
||||
|
||||
if (password_handle.buffer.get()) {
|
||||
memset_s(password_handle.buffer.get(), 0, password_handle.length);
|
||||
password_handle.buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EnrollRequest::nonErrorSerializedSize() const {
|
||||
return serialized_buffer_size(provided_password) + serialized_buffer_size(enrolled_password)
|
||||
+ serialized_buffer_size(password_handle);
|
||||
}
|
||||
|
||||
void EnrollRequest::nonErrorSerialize(uint8_t *buffer) const {
|
||||
append_to_buffer(&buffer, &provided_password);
|
||||
append_to_buffer(&buffer, &enrolled_password);
|
||||
append_to_buffer(&buffer, &password_handle);
|
||||
}
|
||||
|
||||
gatekeeper_error_t EnrollRequest::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
|
||||
gatekeeper_error_t ret;
|
||||
if (provided_password.buffer.get()) {
|
||||
memset_s(provided_password.buffer.get(), 0, provided_password.length);
|
||||
provided_password.buffer.reset();
|
||||
}
|
||||
|
||||
if (enrolled_password.buffer.get()) {
|
||||
memset_s(enrolled_password.buffer.get(), 0, enrolled_password.length);
|
||||
enrolled_password.buffer.reset();
|
||||
}
|
||||
|
||||
if (password_handle.buffer.get()) {
|
||||
memset_s(password_handle.buffer.get(), 0, password_handle.length);
|
||||
password_handle.buffer.reset();
|
||||
}
|
||||
|
||||
ret = read_from_buffer(&payload, end, &provided_password);
|
||||
if (ret != ERROR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = read_from_buffer(&payload, end, &enrolled_password);
|
||||
if (ret != ERROR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return read_from_buffer(&payload, end, &password_handle);
|
||||
}
|
||||
|
||||
EnrollResponse::EnrollResponse(uint32_t user_id, SizedBuffer *enrolled_password_handle) {
|
||||
this->user_id = user_id;
|
||||
this->enrolled_password_handle.buffer.reset(enrolled_password_handle->buffer.release());
|
||||
this->enrolled_password_handle.length = enrolled_password_handle->length;
|
||||
}
|
||||
|
||||
EnrollResponse::EnrollResponse() {
|
||||
memset_s(&enrolled_password_handle, 0, sizeof(enrolled_password_handle));
|
||||
}
|
||||
|
||||
EnrollResponse::~EnrollResponse() {
|
||||
if (enrolled_password_handle.buffer.get()) {
|
||||
enrolled_password_handle.buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void EnrollResponse::SetEnrolledPasswordHandle(SizedBuffer *enrolled_password_handle) {
|
||||
this->enrolled_password_handle.buffer.reset(enrolled_password_handle->buffer.release());
|
||||
this->enrolled_password_handle.length = enrolled_password_handle->length;
|
||||
}
|
||||
|
||||
uint32_t EnrollResponse::nonErrorSerializedSize() const {
|
||||
return serialized_buffer_size(enrolled_password_handle);
|
||||
}
|
||||
|
||||
void EnrollResponse::nonErrorSerialize(uint8_t *buffer) const {
|
||||
append_to_buffer(&buffer, &enrolled_password_handle);
|
||||
}
|
||||
|
||||
gatekeeper_error_t EnrollResponse::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
|
||||
if (enrolled_password_handle.buffer.get()) {
|
||||
enrolled_password_handle.buffer.reset();
|
||||
}
|
||||
|
||||
return read_from_buffer(&payload, end, &enrolled_password_handle);
|
||||
}
|
||||
|
||||
};
|
||||
|
137
android/system/gatekeeper/include/gatekeeper/UniquePtr.h
Normal file
137
android/system/gatekeeper/include/gatekeeper/UniquePtr.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
#ifndef GATEKEEPER_UNIQUE_PTR_H_included
|
||||
#define GATEKEEPER_UNIQUE_PTR_H_included
|
||||
|
||||
#include <stdlib.h> // For NULL.
|
||||
|
||||
namespace gatekeeper {
|
||||
|
||||
// Default deleter for pointer types.
|
||||
template <typename T>
|
||||
struct DefaultDelete {
|
||||
enum { type_must_be_complete = sizeof(T) };
|
||||
DefaultDelete() {}
|
||||
void operator()(T* p) const {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
|
||||
// Default deleter for array types.
|
||||
template <typename T>
|
||||
struct DefaultDelete<T[]> {
|
||||
enum { type_must_be_complete = sizeof(T) };
|
||||
void operator()(T* p) const {
|
||||
delete[] p;
|
||||
}
|
||||
};
|
||||
|
||||
// A smart pointer that deletes the given pointer on destruction.
|
||||
// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
|
||||
// and boost::scoped_array).
|
||||
// Named to be in keeping with Android style but also to avoid
|
||||
// collision with any other implementation, until we can switch over
|
||||
// to unique_ptr.
|
||||
// Use thus:
|
||||
// UniquePtr<C> c(new C);
|
||||
template <typename T, typename D = DefaultDelete<T> >
|
||||
class UniquePtr {
|
||||
public:
|
||||
// Construct a new UniquePtr, taking ownership of the given raw pointer.
|
||||
explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
|
||||
}
|
||||
|
||||
~UniquePtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Accessors.
|
||||
T& operator*() const { return *mPtr; }
|
||||
T* operator->() const { return mPtr; }
|
||||
T* get() const { return mPtr; }
|
||||
|
||||
// Returns the raw pointer and hands over ownership to the caller.
|
||||
// The pointer will not be deleted by UniquePtr.
|
||||
T* release() __attribute__((warn_unused_result)) {
|
||||
T* result = mPtr;
|
||||
mPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Takes ownership of the given raw pointer.
|
||||
// If this smart pointer previously owned a different raw pointer, that
|
||||
// raw pointer will be freed.
|
||||
void reset(T* ptr = NULL) {
|
||||
if (ptr != mPtr) {
|
||||
D()(mPtr);
|
||||
mPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// The raw pointer.
|
||||
T* mPtr;
|
||||
|
||||
// Comparing unique pointers is probably a mistake, since they're unique.
|
||||
template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
|
||||
template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
UniquePtr(const UniquePtr&);
|
||||
void operator=(const UniquePtr&);
|
||||
};
|
||||
|
||||
// Partial specialization for array types. Like std::unique_ptr, this removes
|
||||
// operator* and operator-> but adds operator[].
|
||||
template <typename T, typename D>
|
||||
class UniquePtr<T[], D> {
|
||||
public:
|
||||
explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
|
||||
}
|
||||
|
||||
~UniquePtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
T& operator[](size_t i) const {
|
||||
return mPtr[i];
|
||||
}
|
||||
T* get() const { return mPtr; }
|
||||
|
||||
T* release() __attribute__((warn_unused_result)) {
|
||||
T* result = mPtr;
|
||||
mPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset(T* ptr = NULL) {
|
||||
if (ptr != mPtr) {
|
||||
D()(mPtr);
|
||||
mPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T* mPtr;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
UniquePtr(const UniquePtr&);
|
||||
void operator=(const UniquePtr&);
|
||||
};
|
||||
|
||||
} //namespace gatekeeper
|
||||
#endif // GATEKEEPER_UNIQUE_PTR_H_included
|
213
android/system/gatekeeper/include/gatekeeper/gatekeeper.h
Normal file
213
android/system/gatekeeper/include/gatekeeper/gatekeeper.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#ifndef GATEKEEPER_H_
|
||||
#define GATEKEEPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gatekeeper/UniquePtr.h>
|
||||
#include <hardware/hw_auth_token.h>
|
||||
|
||||
#include "gatekeeper_messages.h"
|
||||
#include "password_handle.h"
|
||||
|
||||
namespace gatekeeper {
|
||||
|
||||
struct __attribute__((packed)) failure_record_t {
|
||||
uint64_t secure_user_id;
|
||||
uint64_t last_checked_timestamp;
|
||||
uint32_t failure_counter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for gatekeeper implementations. Provides all functionality except
|
||||
* the ability to create/access keys and compute signatures. These are left up
|
||||
* to the platform-specific implementation.
|
||||
*/
|
||||
class GateKeeper {
|
||||
public:
|
||||
GateKeeper() {}
|
||||
virtual ~GateKeeper() {}
|
||||
|
||||
void Enroll(const EnrollRequest &request, EnrollResponse *response);
|
||||
void Verify(const VerifyRequest &request, VerifyResponse *response);
|
||||
|
||||
protected:
|
||||
|
||||
// The following methods are intended to be implemented by concrete subclasses
|
||||
|
||||
/**
|
||||
* Retrieves the key used by GateKeeper::MintAuthToken to sign the payload
|
||||
* of the AuthToken. This is not cached as is may have changed due to an event such
|
||||
* as a password change.
|
||||
*
|
||||
* Writes the length in bytes of the returned key to length if it is not null.
|
||||
*
|
||||
* Ownership of the auth_token_key pointer is maintained by the implementor.
|
||||
*
|
||||
* Returns true if the key was successfully fetched.
|
||||
*
|
||||
*/
|
||||
virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, uint32_t *length)
|
||||
const = 0;
|
||||
|
||||
/**
|
||||
* The key used to sign and verify password data.
|
||||
*
|
||||
* MUST be different from the AuthTokenKey.
|
||||
*
|
||||
* GetPasswordKey is not const because unlike AuthTokenKey,
|
||||
* this value can be cached.
|
||||
*
|
||||
* Ownership of the password_key pointer is maintained by the implementor.
|
||||
*
|
||||
*/
|
||||
virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) = 0;
|
||||
|
||||
/**
|
||||
* Uses platform-specific routines to compute a signature on the provided password.
|
||||
*
|
||||
* This can be implemented as a simple pass-through to ComputeSignature, but is
|
||||
* available in case handling for password signatures is different from general
|
||||
* purpose signatures.
|
||||
*
|
||||
* Writes the signature_length size signature to the 'signature' pointer.
|
||||
*/
|
||||
virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length,
|
||||
const uint8_t *key, uint32_t key_length, const uint8_t *password,
|
||||
uint32_t password_length, salt_t salt) const = 0;
|
||||
|
||||
/**
|
||||
* Retrieves a unique, cryptographically randomly generated buffer for use in password
|
||||
* hashing, etc.
|
||||
*
|
||||
* Assings the random to the random UniquePtr, relinquishing ownership to the caller
|
||||
*/
|
||||
virtual void GetRandom(void *random, uint32_t requested_size) const = 0;
|
||||
|
||||
/**
|
||||
* Uses platform-specific routines to compute a signature on the provided message.
|
||||
*
|
||||
* Writes the signature_length size signature to the 'signature' pointer.
|
||||
*/
|
||||
virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length,
|
||||
const uint8_t *key, uint32_t key_length, const uint8_t *message,
|
||||
const uint32_t length) const = 0;
|
||||
|
||||
/**
|
||||
* Get the time since boot in milliseconds.
|
||||
*
|
||||
* Should return 0 on error.
|
||||
*/
|
||||
virtual uint64_t GetMillisecondsSinceBoot() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the value of the current failure record for the user.
|
||||
*
|
||||
* The failure record should be written to hardware-backed secure storage, such as
|
||||
* RPMB, if the target device supports it.
|
||||
*
|
||||
* If 'secure' is false, password is operating in a fallback mode. Implementations
|
||||
* may store the failure record in memory or in non-secure storage if this value is false.
|
||||
*
|
||||
* Returns true on success, false if failure record cannot be retrieved.
|
||||
*/
|
||||
virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
|
||||
bool secure) = 0;
|
||||
|
||||
/**
|
||||
* Initializes or reinitializes the failure record for the current user.
|
||||
*
|
||||
* Must be persisted in secure storage if the target device supports it.
|
||||
*
|
||||
* If 'secure' is false, password is operating in a fallback mode. Implementations
|
||||
* may store the failure record in memory or in non-secure storage if this value is false.
|
||||
*
|
||||
* Returns true if the failure record was successfully persisted.
|
||||
*/
|
||||
virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool secure) = 0;
|
||||
|
||||
/*
|
||||
* Writes the provided failure record to persistent storage.
|
||||
*
|
||||
* Must be persisted in secure storage if the target device supports it.
|
||||
*
|
||||
* If 'secure' is false, password is operating in a fallback mode. Implementations
|
||||
* may store the failure record in memory or in non-secure storage if this value is false.
|
||||
*
|
||||
* Returns true if record was successfully written.
|
||||
*/
|
||||
virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool secure) = 0;
|
||||
|
||||
/**
|
||||
* Computes the amount of time to throttle the user due to the current failure_record
|
||||
* counter. An implementation is provided by the generic GateKeeper, but may be
|
||||
* overriden.
|
||||
*/
|
||||
virtual uint32_t ComputeRetryTimeout(const failure_record_t *record);
|
||||
|
||||
/**
|
||||
* Returns whether the GateKeeper implementation is backed by hardware.
|
||||
*/
|
||||
virtual bool IsHardwareBacked() const = 0;
|
||||
|
||||
/**
|
||||
* Verifies that handle matches password HMAC'ed with the password_key
|
||||
*/
|
||||
virtual bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generates a signed attestation of an authentication event and assings
|
||||
* to auth_token UniquePtr.
|
||||
* The format is consistent with that of hw_auth_token_t.
|
||||
* Also returns the length in length if it is not null.
|
||||
*/
|
||||
void MintAuthToken(UniquePtr<uint8_t> *auth_token, uint32_t *length, uint64_t timestamp,
|
||||
secure_id_t user_id, secure_id_t authenticator_id, uint64_t challenge);
|
||||
|
||||
/**
|
||||
* Populates password_handle with the data provided and computes HMAC.
|
||||
*/
|
||||
bool CreatePasswordHandle(SizedBuffer *password_handle, salt_t salt,
|
||||
secure_id_t secure_id, secure_id_t authenticator_id, uint8_t handle_version,
|
||||
const uint8_t *password, uint32_t password_length);
|
||||
|
||||
/**
|
||||
* Increments the counter on the current failure record for the provided user id.
|
||||
* Sets the last_checked_timestamp to timestamp. Writes the updated record
|
||||
* to *record if not null.
|
||||
*
|
||||
* Returns true if failure record was successfully incremented.
|
||||
*/
|
||||
bool IncrementFailureRecord(uint32_t uid, secure_id_t user_id, uint64_t timestamp,
|
||||
failure_record_t *record, bool secure);
|
||||
|
||||
/**
|
||||
* Determines whether the request is within the current throttle window.
|
||||
*
|
||||
* If the system timer has been reset due to a reboot or otherwise, resets
|
||||
* the throttle window with a base at the current time.
|
||||
*
|
||||
* Returns true if the request is in the throttle window.
|
||||
*/
|
||||
bool ThrottleRequest(uint32_t uid, uint64_t timestamp,
|
||||
failure_record_t *record, bool secure, GateKeeperMessage *response);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // GATEKEEPER_H_
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
#ifndef GATEKEEPER_MESSAGES_H_
|
||||
#define GATEKEEPER_MESSAGES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gatekeeper/UniquePtr.h>
|
||||
|
||||
|
||||
#include "gatekeeper_utils.h"
|
||||
/**
|
||||
* Message serialization objects for communicating with the hardware gatekeeper.
|
||||
*/
|
||||
namespace gatekeeper {
|
||||
|
||||
const uint32_t ENROLL = 0;
|
||||
const uint32_t VERIFY = 1;
|
||||
|
||||
typedef enum {
|
||||
ERROR_NONE = 0,
|
||||
ERROR_INVALID = 1,
|
||||
ERROR_RETRY = 2,
|
||||
ERROR_UNKNOWN = 3,
|
||||
} gatekeeper_error_t;
|
||||
|
||||
struct SizedBuffer {
|
||||
SizedBuffer() {
|
||||
length = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a SizedBuffer of a provided
|
||||
* length.
|
||||
*/
|
||||
explicit SizedBuffer(uint32_t length) {
|
||||
if (length != 0) {
|
||||
buffer.reset(new uint8_t[length]);
|
||||
} else {
|
||||
buffer.reset();
|
||||
}
|
||||
this->length = length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a SizedBuffer out of a pointer and a length
|
||||
* Takes ownership of the buf pointer, and deallocates it
|
||||
* when destructed.
|
||||
*/
|
||||
SizedBuffer(uint8_t buf[], uint32_t len) {
|
||||
buffer.reset(buf);
|
||||
length = len;
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[]> buffer;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Abstract base class of all message objects. Handles serialization of common
|
||||
* elements like the error and user ID. Delegates specialized serialization
|
||||
* to protected pure virtual functions implemented by subclasses.
|
||||
*/
|
||||
struct GateKeeperMessage {
|
||||
GateKeeperMessage() : error(ERROR_NONE) {}
|
||||
explicit GateKeeperMessage(gatekeeper_error_t error) : error(error) {}
|
||||
virtual ~GateKeeperMessage() {}
|
||||
|
||||
/**
|
||||
* Returns serialized size in bytes of the current state of the
|
||||
* object.
|
||||
*/
|
||||
uint32_t GetSerializedSize() const;
|
||||
/**
|
||||
* Converts the object into its serialized representation.
|
||||
*
|
||||
* Expects payload to be allocated with GetSerializedSize bytes.
|
||||
*
|
||||
* Returns the number of bytes written or 0 on error.
|
||||
*/
|
||||
uint32_t Serialize(uint8_t *payload, const uint8_t *end) const;
|
||||
|
||||
/**
|
||||
* Inflates the object from its serial representation.
|
||||
*/
|
||||
gatekeeper_error_t Deserialize(const uint8_t *payload, const uint8_t *end);
|
||||
|
||||
/**
|
||||
* Calls may fail due to throttling. If so, this sets a timeout in milliseconds
|
||||
* for when the caller should attempt the call again. Additionally, sets the
|
||||
* error to ERROR_RETRY.
|
||||
*/
|
||||
void SetRetryTimeout(uint32_t retry_timeout);
|
||||
|
||||
/**
|
||||
* The following methods are intended to be implemented by subclasses.
|
||||
* They are hooks to serialize the elements specific to each particular
|
||||
* specialization.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the size of serializing only the elements specific to the
|
||||
* current sublclass.
|
||||
*/
|
||||
virtual uint32_t nonErrorSerializedSize() const { return 0; } ;
|
||||
/**
|
||||
* Takes a pointer to a buffer prepared by Serialize and writes
|
||||
* the subclass specific data into it. The size of the buffer must be exactly
|
||||
* that returned by nonErrorSerializedSize() in bytes.
|
||||
*/
|
||||
virtual void nonErrorSerialize(uint8_t *) const { }
|
||||
|
||||
/**
|
||||
* Deserializes subclass specific data from payload without reading past end.
|
||||
*/
|
||||
virtual gatekeeper_error_t nonErrorDeserialize(const uint8_t *, const uint8_t *) {
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
gatekeeper_error_t error;
|
||||
uint32_t user_id;
|
||||
uint32_t retry_timeout;
|
||||
};
|
||||
|
||||
struct VerifyRequest : public GateKeeperMessage {
|
||||
VerifyRequest(
|
||||
uint32_t user_id,
|
||||
uint64_t challenge,
|
||||
SizedBuffer *enrolled_password_handle,
|
||||
SizedBuffer *provided_password_payload);
|
||||
VerifyRequest();
|
||||
~VerifyRequest();
|
||||
|
||||
virtual uint32_t nonErrorSerializedSize() const;
|
||||
virtual void nonErrorSerialize(uint8_t *buffer) const;
|
||||
virtual gatekeeper_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
|
||||
|
||||
uint64_t challenge;
|
||||
SizedBuffer password_handle;
|
||||
SizedBuffer provided_password;
|
||||
};
|
||||
|
||||
struct VerifyResponse : public GateKeeperMessage {
|
||||
VerifyResponse(uint32_t user_id, SizedBuffer *auth_token);
|
||||
VerifyResponse();
|
||||
~VerifyResponse();
|
||||
|
||||
void SetVerificationToken(SizedBuffer *auth_token);
|
||||
|
||||
virtual uint32_t nonErrorSerializedSize() const;
|
||||
virtual void nonErrorSerialize(uint8_t *buffer) const;
|
||||
virtual gatekeeper_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
|
||||
|
||||
SizedBuffer auth_token;
|
||||
bool request_reenroll;
|
||||
};
|
||||
|
||||
struct EnrollRequest : public GateKeeperMessage {
|
||||
EnrollRequest(uint32_t user_id, SizedBuffer *password_handle,
|
||||
SizedBuffer *provided_password, SizedBuffer *enrolled_password);
|
||||
EnrollRequest();
|
||||
~EnrollRequest();
|
||||
|
||||
virtual uint32_t nonErrorSerializedSize() const;
|
||||
virtual void nonErrorSerialize(uint8_t *buffer) const;
|
||||
virtual gatekeeper_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
|
||||
|
||||
/**
|
||||
* The password handle returned from the previous call to enroll or NULL
|
||||
* if none
|
||||
*/
|
||||
SizedBuffer password_handle;
|
||||
/**
|
||||
* The currently enrolled password as entered by the user
|
||||
*/
|
||||
SizedBuffer enrolled_password;
|
||||
/**
|
||||
* The password desired by the user
|
||||
*/
|
||||
SizedBuffer provided_password;
|
||||
};
|
||||
|
||||
struct EnrollResponse : public GateKeeperMessage {
|
||||
public:
|
||||
EnrollResponse(uint32_t user_id, SizedBuffer *enrolled_password_handle);
|
||||
EnrollResponse();
|
||||
~EnrollResponse();
|
||||
|
||||
void SetEnrolledPasswordHandle(SizedBuffer *enrolled_password_handle);
|
||||
|
||||
virtual uint32_t nonErrorSerializedSize() const;
|
||||
virtual void nonErrorSerialize(uint8_t *buffer) const;
|
||||
virtual gatekeeper_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
|
||||
|
||||
SizedBuffer enrolled_password_handle;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GATEKEEPER_MESSAGES_H_
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
*/
|
||||
#ifndef GOOGLE_GATEKEEPER_UTILS_H_
|
||||
#define GOOGLE_GATEKEEPER_UTILS_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace gatekeeper {
|
||||
/**
|
||||
* Variant of memset() that uses GCC-specific pragmas to disable optimizations, so effect is not
|
||||
* optimized away. This is important because we often need to wipe blocks of sensitive data from
|
||||
* memory. As an additional convenience, this implementation avoids writing to NULL pointers.
|
||||
*/
|
||||
#ifdef __clang__
|
||||
#define OPTNONE __attribute__((optnone))
|
||||
#else // not __clang__
|
||||
#define OPTNONE __attribute__((optimize("O0")))
|
||||
#endif // not __clang__
|
||||
inline OPTNONE void* memset_s(void* s, int c, size_t n) {
|
||||
if (!s)
|
||||
return s;
|
||||
return memset(s, c, n);
|
||||
}
|
||||
#undef OPTNONE
|
||||
|
||||
/**
|
||||
* Return the number of elements in array \p a.
|
||||
*/
|
||||
template <typename T, size_t N> inline size_t array_length(const T (&)[N]) {
|
||||
return N;
|
||||
}
|
||||
|
||||
static inline int memcmp_s(const void* p1, const void* p2, size_t length) {
|
||||
const uint8_t* s1 = static_cast<const uint8_t*>(p1);
|
||||
const uint8_t* s2 = static_cast<const uint8_t*>(p2);
|
||||
uint8_t result = 0;
|
||||
while (length-- > 0)
|
||||
result |= *s1++ ^ *s2++;
|
||||
return result == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
};
|
||||
#endif //GOOGLE_GATEKEEPER_UTILS_H_
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#ifndef GATEKEEPER_PASSWORD_HANDLE_H_
|
||||
#define GATEKEEPER_PASSWORD_HANDLE_H_
|
||||
|
||||
#define HANDLE_FLAG_THROTTLE_SECURE 1
|
||||
|
||||
#define HANDLE_VERSION_THROTTLE 2
|
||||
|
||||
namespace gatekeeper {
|
||||
|
||||
typedef uint64_t secure_id_t;
|
||||
typedef uint64_t salt_t;
|
||||
/**
|
||||
* structure for easy serialization
|
||||
* and deserialization of password handles.
|
||||
*/
|
||||
static const uint8_t HANDLE_VERSION = 2;
|
||||
struct __attribute__ ((__packed__)) password_handle_t {
|
||||
// fields included in signature
|
||||
uint8_t version;
|
||||
secure_id_t user_id;
|
||||
uint64_t flags;
|
||||
|
||||
// fields not included in signature
|
||||
salt_t salt;
|
||||
uint8_t signature[32];
|
||||
|
||||
bool hardware_backed;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GATEKEEPER_PASSWORD_HANDLE_H_
|
15
android/system/gatekeeper/rules.mk
Normal file
15
android/system/gatekeeper/rules.mk
Normal file
|
@ -0,0 +1,15 @@
|
|||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS := \
|
||||
$(LOCAL_DIR)/gatekeeper_messages.cpp \
|
||||
$(LOCAL_DIR)/gatekeeper.cpp
|
||||
|
||||
GLOBAL_INCLUDES += $(LOCAL_DIR)/include/
|
||||
|
||||
MODULE_DEPS := \
|
||||
lib/libc \
|
||||
lib/libc-trusty \
|
||||
|
||||
include make/module.mk
|
29
android/system/gatekeeper/tests/Android.mk
Normal file
29
android/system/gatekeeper/tests/Android.mk
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := gatekeeper-unit-tests
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
|
||||
LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
|
||||
LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libhardware
|
||||
LOCAL_STATIC_LIBRARIES := libscrypt_static
|
||||
LOCAL_C_INCLUDES := external/scrypt/lib/crypto
|
||||
LOCAL_SRC_FILES := \
|
||||
gatekeeper_messages_test.cpp \
|
||||
gatekeeper_device_test.cpp
|
||||
include $(BUILD_NATIVE_TEST)
|
251
android/system/gatekeeper/tests/gatekeeper_device_test.cpp
Executable file
251
android/system/gatekeeper/tests/gatekeeper_device_test.cpp
Executable file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <endian.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hardware/gatekeeper.h>
|
||||
#include <gatekeeper/gatekeeper.h> // For password_handle_t
|
||||
#include <unistd.h>
|
||||
|
||||
using ::testing::Test;
|
||||
using ::gatekeeper::password_handle_t;
|
||||
using ::gatekeeper::secure_id_t;
|
||||
|
||||
class GateKeeperDeviceTest : public virtual Test {
|
||||
public:
|
||||
GateKeeperDeviceTest() {}
|
||||
virtual ~GateKeeperDeviceTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
gatekeeper_device_initialize(&device);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
gatekeeper_close(device);
|
||||
}
|
||||
|
||||
static void gatekeeper_device_initialize(gatekeeper_device_t **dev) {
|
||||
int ret;
|
||||
const hw_module_t *mod;
|
||||
ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
ret = gatekeeper_open(mod, dev);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
|
||||
gatekeeper_device_t *device;
|
||||
};
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, EnrollAndVerifyStress) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
uint8_t *auth_token;
|
||||
uint32_t auth_token_len;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
bool should_reenroll;
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len, &should_reenroll);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, EnrollAndVerify) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
uint8_t *auth_token;
|
||||
uint32_t auth_token_len;
|
||||
hw_auth_token_t *hat;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
bool should_reenroll;
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len, &should_reenroll);
|
||||
ASSERT_EQ(0, should_reenroll);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
hat = reinterpret_cast<hw_auth_token_t *>(auth_token);
|
||||
|
||||
ASSERT_EQ(HW_AUTH_TOKEN_VERSION, hat->version);
|
||||
ASSERT_EQ(htonl(HW_AUTH_PASSWORD), hat->authenticator_type);
|
||||
}
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, EnrollAndVerifyTimeout) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
uint8_t *auth_token = NULL;
|
||||
uint32_t auth_token_len;
|
||||
bool should_reenroll;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
int payload_val = password_payload[0];
|
||||
password_payload[0] = 4;
|
||||
|
||||
int timeout = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
bool should_reenroll;
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len,
|
||||
&should_reenroll);
|
||||
ASSERT_NE(0, ret);
|
||||
ASSERT_EQ(NULL, auth_token);
|
||||
|
||||
if (ret > 0) {
|
||||
timeout = ret;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NE(0, timeout);
|
||||
|
||||
sleep((timeout + 999)/ 1000);
|
||||
|
||||
password_payload[0] = payload_val;
|
||||
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len,
|
||||
&should_reenroll);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, EnrollAndVerifyBadPassword) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
uint8_t *auth_token = NULL;
|
||||
uint32_t auth_token_len;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
password_payload[0] = 4;
|
||||
|
||||
bool should_reenroll;
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len,
|
||||
&should_reenroll);
|
||||
|
||||
ASSERT_NE(0, ret);
|
||||
ASSERT_EQ(NULL, auth_token);
|
||||
}
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, MinFailedAttemptsBeforeLockout) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
uint8_t *auth_token = NULL;
|
||||
uint32_t auth_token_len;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
password_payload[0] = 4;
|
||||
|
||||
// User should have at least 4 attempts before being locked out
|
||||
static const int MIN_FAILED_ATTEMPTS = 4;
|
||||
|
||||
bool should_reenroll;
|
||||
for (int i = 0; i < MIN_FAILED_ATTEMPTS; i++) {
|
||||
ret = device->verify(device, 400, 0, password_handle, password_handle_length,
|
||||
password_payload, password_len, &auth_token, &auth_token_len,
|
||||
&should_reenroll);
|
||||
// shoudln't be a timeout
|
||||
ASSERT_LT(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, UntrustedReEnroll) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
password_handle_t *handle = reinterpret_cast<password_handle_t *>(password_handle);
|
||||
secure_id_t sid = handle->user_id;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
handle = reinterpret_cast<password_handle_t *>(password_handle);
|
||||
secure_id_t sid_tmp = handle->user_id;
|
||||
ASSERT_NE(sid, sid_tmp);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GateKeeperDeviceTest, TrustedReEnroll) {
|
||||
uint32_t password_len = 50;
|
||||
uint8_t password_payload[password_len];
|
||||
uint8_t *password_handle;
|
||||
uint32_t password_handle_length;
|
||||
int ret;
|
||||
|
||||
ret = device->enroll(device, 400, NULL, 0, NULL, 0, password_payload, password_len,
|
||||
&password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
password_handle_t *handle = reinterpret_cast<password_handle_t *>(password_handle);
|
||||
secure_id_t sid = handle->user_id;
|
||||
|
||||
ret = device->enroll(device, 400, password_handle, password_handle_length, password_payload,
|
||||
password_len, password_payload, password_len, &password_handle, &password_handle_length);
|
||||
|
||||
ASSERT_EQ(0, ret);
|
||||
handle = reinterpret_cast<password_handle_t *>(password_handle);
|
||||
secure_id_t sid_tmp = handle->user_id;
|
||||
ASSERT_EQ(sid, sid_tmp);
|
||||
}
|
||||
|
312
android/system/gatekeeper/tests/gatekeeper_messages_test.cpp
Normal file
312
android/system/gatekeeper/tests/gatekeeper_messages_test.cpp
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gatekeeper/gatekeeper_messages.h>
|
||||
|
||||
using ::gatekeeper::SizedBuffer;
|
||||
using ::testing::Test;
|
||||
using ::gatekeeper::EnrollRequest;
|
||||
using ::gatekeeper::EnrollResponse;
|
||||
using ::gatekeeper::VerifyRequest;
|
||||
using ::gatekeeper::VerifyResponse;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
static const uint32_t USER_ID = 3857;
|
||||
|
||||
static SizedBuffer *make_buffer(uint32_t size) {
|
||||
SizedBuffer *result = new SizedBuffer;
|
||||
result->length = size;
|
||||
uint8_t *buffer = new uint8_t[size];
|
||||
srand(size);
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
buffer[i] = rand();
|
||||
}
|
||||
|
||||
result->buffer.reset(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, EnrollRequestNullEnrolledNullHandle) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *provided_password = make_buffer(password_size);
|
||||
const SizedBuffer *deserialized_password;
|
||||
// create request, serialize, deserialize, and validate
|
||||
EnrollRequest msg(USER_ID, NULL, provided_password, NULL);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
EnrollRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
deserialized_password = &deserialized_msg.provided_password;
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.provided_password.buffer.get(), deserialized_password->buffer.get(), password_size));
|
||||
ASSERT_EQ((uint32_t) 0, deserialized_msg.enrolled_password.length);
|
||||
ASSERT_EQ(NULL, deserialized_msg.enrolled_password.buffer.get());
|
||||
ASSERT_EQ((uint32_t) 0, deserialized_msg.password_handle.length);
|
||||
ASSERT_EQ(NULL, deserialized_msg.password_handle.buffer.get());
|
||||
delete provided_password;
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, EnrollRequestEmptyEnrolledEmptyHandle) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *provided_password = make_buffer(password_size);
|
||||
SizedBuffer enrolled;
|
||||
SizedBuffer handle;
|
||||
const SizedBuffer *deserialized_password;
|
||||
// create request, serialize, deserialize, and validate
|
||||
EnrollRequest msg(USER_ID, &handle, provided_password, &enrolled);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
EnrollRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
deserialized_password = &deserialized_msg.provided_password;
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.provided_password.buffer.get(), deserialized_password->buffer.get(), password_size));
|
||||
ASSERT_EQ((uint32_t) 0, deserialized_msg.enrolled_password.length);
|
||||
ASSERT_EQ(NULL, deserialized_msg.enrolled_password.buffer.get());
|
||||
ASSERT_EQ((uint32_t) 0, deserialized_msg.password_handle.length);
|
||||
ASSERT_EQ(NULL, deserialized_msg.password_handle.buffer.get());
|
||||
delete provided_password;
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, EnrollRequestNonNullEnrolledOrHandle) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *provided_password = make_buffer(password_size);
|
||||
SizedBuffer *enrolled_password = make_buffer(password_size);
|
||||
SizedBuffer *password_handle = make_buffer(password_size);
|
||||
const SizedBuffer *deserialized_password;
|
||||
const SizedBuffer *deserialized_enrolled;
|
||||
const SizedBuffer *deserialized_handle;
|
||||
// create request, serialize, deserialize, and validate
|
||||
EnrollRequest msg(USER_ID, password_handle, provided_password, enrolled_password);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
EnrollRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
deserialized_password = &deserialized_msg.provided_password;
|
||||
deserialized_enrolled = &deserialized_msg.enrolled_password;
|
||||
deserialized_handle = &deserialized_msg.password_handle;
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.provided_password.buffer.get(), deserialized_password->buffer.get(), password_size));
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_enrolled->length);
|
||||
ASSERT_EQ(0, memcmp(msg.enrolled_password.buffer.get(), deserialized_enrolled->buffer.get(), password_size));
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_handle->length);
|
||||
ASSERT_EQ(0, memcmp(msg.password_handle.buffer.get(), deserialized_handle->buffer.get(), password_size));
|
||||
delete provided_password;
|
||||
delete enrolled_password;
|
||||
delete password_handle;
|
||||
}
|
||||
|
||||
|
||||
TEST(RoundTripTest, EnrollResponse) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *enrolled_password = make_buffer(password_size);
|
||||
const SizedBuffer *deserialized_password;
|
||||
// create request, serialize, deserialize, and validate
|
||||
EnrollResponse msg(USER_ID, enrolled_password);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
EnrollResponse deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
deserialized_password = &deserialized_msg.enrolled_password_handle;
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.enrolled_password_handle.buffer.get(),
|
||||
deserialized_password->buffer.get(), password_size));
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, VerifyRequest) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *provided_password = make_buffer(password_size),
|
||||
*password_handle = make_buffer(password_size);
|
||||
const SizedBuffer *deserialized_password;
|
||||
// create request, serialize, deserialize, and validate
|
||||
VerifyRequest msg(USER_ID, 1, password_handle, provided_password);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
VerifyRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
ASSERT_EQ((uint64_t) 1, deserialized_msg.challenge);
|
||||
deserialized_password = &deserialized_msg.password_handle;
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.provided_password.buffer.get(), deserialized_password->buffer.get(),
|
||||
password_size));
|
||||
|
||||
deserialized_password = &deserialized_msg.password_handle;
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.password_handle.buffer.get(), deserialized_password->buffer.get(),
|
||||
password_size));
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, VerifyResponse) {
|
||||
const uint32_t password_size = 512;
|
||||
SizedBuffer *auth_token = make_buffer(password_size);
|
||||
const SizedBuffer *deserialized_password;
|
||||
// create request, serialize, deserialize, and validate
|
||||
VerifyResponse msg(USER_ID, auth_token);
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
|
||||
VerifyResponse deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get()
|
||||
+ serialized_msg.length);
|
||||
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_NONE,
|
||||
deserialized_msg.error);
|
||||
|
||||
ASSERT_EQ(USER_ID, deserialized_msg.user_id);
|
||||
deserialized_password = &deserialized_msg.auth_token;
|
||||
ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
|
||||
ASSERT_EQ(0, memcmp(msg.auth_token.buffer.get(), deserialized_password->buffer.get(),
|
||||
password_size));
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, VerifyResponseError) {
|
||||
VerifyResponse msg;
|
||||
msg.error = gatekeeper::gatekeeper_error_t::ERROR_INVALID;
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
VerifyResponse deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_INVALID,
|
||||
deserialized_msg.error);
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, VerifyRequestError) {
|
||||
VerifyRequest msg;
|
||||
msg.error = gatekeeper::gatekeeper_error_t::ERROR_INVALID;
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
VerifyRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_INVALID,
|
||||
deserialized_msg.error);
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, EnrollResponseError) {
|
||||
EnrollResponse msg;
|
||||
msg.error = gatekeeper::gatekeeper_error_t::ERROR_INVALID;
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
EnrollResponse deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_INVALID,
|
||||
deserialized_msg.error);
|
||||
}
|
||||
|
||||
TEST(RoundTripTest, EnrollRequestError) {
|
||||
EnrollRequest msg;
|
||||
msg.error = gatekeeper::gatekeeper_error_t::ERROR_INVALID;
|
||||
SizedBuffer serialized_msg(msg.GetSerializedSize());
|
||||
msg.Serialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
EnrollRequest deserialized_msg;
|
||||
deserialized_msg.Deserialize(serialized_msg.buffer.get(), serialized_msg.buffer.get() + serialized_msg.length);
|
||||
ASSERT_EQ(gatekeeper::gatekeeper_error_t::ERROR_INVALID,
|
||||
deserialized_msg.error);
|
||||
}
|
||||
|
||||
uint8_t msgbuf[] = {
|
||||
220, 88, 183, 255, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 173, 0, 0, 0, 228, 174, 98, 187, 191, 135, 253, 200, 51, 230, 114, 247, 151, 109,
|
||||
237, 79, 87, 32, 94, 5, 204, 46, 154, 30, 91, 6, 103, 148, 254, 129, 65, 171, 228,
|
||||
167, 224, 163, 9, 15, 206, 90, 58, 11, 205, 55, 211, 33, 87, 178, 149, 91, 28, 236,
|
||||
218, 112, 231, 34, 82, 82, 134, 103, 137, 115, 27, 156, 102, 159, 220, 226, 89, 42, 25,
|
||||
37, 9, 84, 239, 76, 161, 198, 72, 167, 163, 39, 91, 148, 191, 17, 191, 87, 169, 179,
|
||||
136, 10, 194, 154, 4, 40, 107, 109, 61, 161, 20, 176, 247, 13, 214, 106, 229, 45, 17,
|
||||
5, 60, 189, 64, 39, 166, 208, 14, 57, 25, 140, 148, 25, 177, 246, 189, 43, 181, 88,
|
||||
204, 29, 126, 224, 100, 143, 93, 60, 57, 249, 55, 0, 87, 83, 227, 224, 166, 59, 214,
|
||||
81, 144, 129, 58, 6, 57, 46, 254, 232, 41, 220, 209, 230, 167, 138, 158, 94, 180, 125,
|
||||
247, 26, 162, 116, 238, 202, 187, 100, 65, 13, 180, 44, 245, 159, 83, 161, 176, 58, 72,
|
||||
236, 109, 105, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 11, 0, 0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0,
|
||||
0, 32, 3, 0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0,
|
||||
1, 0, 0, 200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112,
|
||||
1, 246, 1, 0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145,
|
||||
1, 0, 96, 144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 190, 2, 0, 16, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 11, 0,
|
||||
0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0, 0, 32, 3,
|
||||
0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0, 1, 0, 0,
|
||||
200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112, 1, 246, 1,
|
||||
0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145, 1, 0, 96,
|
||||
144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 190, 2, 0, 16, 1, 0, 0, 0,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* These tests don't have any assertions or expectations. They just try to parse garbage, to see if
|
||||
* the result will be a crash. This is especially informative when run under Valgrind memcheck.
|
||||
*/
|
||||
|
||||
template <typename Message> void parse_garbage() {
|
||||
Message msg;
|
||||
uint32_t array_length = sizeof(msgbuf) / sizeof(msgbuf[0]);
|
||||
const uint8_t* end = msgbuf + array_length;
|
||||
for (uint32_t i = 0; i < array_length; ++i) {
|
||||
const uint8_t* begin = msgbuf + i;
|
||||
const uint8_t* p = begin;
|
||||
msg.Deserialize(p, end);
|
||||
}
|
||||
}
|
||||
|
||||
#define GARBAGE_TEST(Message) \
|
||||
TEST(GarbageTest, Message) { parse_garbage<Message>(); }
|
||||
|
||||
GARBAGE_TEST(VerifyRequest);
|
||||
GARBAGE_TEST(VerifyResponse);
|
||||
GARBAGE_TEST(EnrollRequest);
|
||||
GARBAGE_TEST(EnrollResponse);
|
Loading…
Add table
Add a link
Reference in a new issue