Created libattestation

Move attestation logic from input into separate library. This way Input
and SurfaceFlinger can leverage the same logic

Test: inputflinger_test
Test: attestation_tests
Bug: 155825630
Change-Id: Ia4f65166da8a1c53a9570db59eab602190438696
This commit is contained in:
chaviw 2020-08-24 15:48:26 -07:00
parent 36ca632e6f
commit 09c8d2d004
20 changed files with 299 additions and 134 deletions

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2020 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 <array>
namespace android {
/**
* Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
*/
constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
class HmacKeyManager {
public:
HmacKeyManager();
std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
private:
const std::array<uint8_t, 128> mHmacKey;
};
} // namespace android

View file

@ -312,11 +312,6 @@ private:
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
/**
* Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
*/
constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
/*
* Pointer coordinate data.
*/

View file

@ -0,0 +1,31 @@
// Copyright (C) 2020 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.
cc_library_static {
name: "libattestation",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
srcs: [
"HmacKeyManager.cpp"
],
clang: true,
shared_libs: [
"liblog",
"libcrypto",
],
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 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 <attestation/HmacKeyManager.h>
#include <log/log.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
namespace android {
static std::array<uint8_t, 128> getRandomKey() {
std::array<uint8_t, 128> key;
if (RAND_bytes(key.data(), key.size()) != 1) {
LOG_ALWAYS_FATAL("Can't generate HMAC key");
}
return key;
}
HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
// SHA256 always generates 32-bytes result
std::array<uint8_t, 32> hash;
unsigned int hashLen = 0;
uint8_t* result =
HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
if (result == nullptr) {
ALOGE("Could not sign the data using HMAC");
return INVALID_HMAC;
}
if (hashLen != hash.size()) {
ALOGE("HMAC-SHA256 has unexpected length");
return INVALID_HMAC;
}
return hash;
}
} // namespace android

2
libs/attestation/OWNERS Normal file
View file

@ -0,0 +1,2 @@
chaviw@google.com
svv@google.com

View file

@ -0,0 +1,7 @@
{
"presubmit": [
{
"name": "libattestation_tests"
}
]
}

View file

@ -0,0 +1,28 @@
// Copyright (C) 2020 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.
cc_test {
name: "libattestation_tests",
test_suites: ["device-tests"],
srcs: [
"HmacKeyManager_test.cpp",
],
static_libs: [
"libattestation",
],
shared_libs: [
"liblog",
"libcrypto",
],
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 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 <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
namespace android {
class HmacKeyManagerTest : public testing::Test {
protected:
HmacKeyManager mHmacKeyManager;
};
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
* that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
* tests.
*/
TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data));
std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data));
ASSERT_EQ(hmac1, hmac2);
}
/**
* Ensure that changes in the hmac verification data produce a different hmac.
*/
TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data));
data[2] = 2;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data)));
}
} // namespace android

View file

@ -17,6 +17,7 @@
#define LOG_TAG "Input"
//#define LOG_NDEBUG 0
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <limits.h>
#include <string.h>

View file

@ -24,9 +24,10 @@
#endif
#include <android/keycodes.h>
#include <attestation/HmacKeyManager.h>
#include <input/InputEventLabels.h>
#include <input/Keyboard.h>
#include <input/KeyCharacterMap.h>
#include <input/Keyboard.h>
#include <utils/Log.h>
#include <utils/Errors.h>

View file

@ -17,6 +17,7 @@
#include <array>
#include <math.h>
#include <attestation/HmacKeyManager.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/Input.h>

View file

@ -20,11 +20,12 @@
#include <sys/mman.h>
#include <time.h>
#include <attestation/HmacKeyManager.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
#include <utils/Timers.h>
#include <utils/StopWatch.h>
#include <utils/Timers.h>
namespace android {

View file

@ -21,6 +21,7 @@
#include <math.h>
#include <android-base/stringprintf.h>
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/VelocityTracker.h>

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/Input.h>

View file

@ -59,6 +59,9 @@ cc_defaults {
"libutils",
"libui",
],
static_libs: [
"libattestation",
],
}
cc_library_shared {

View file

@ -18,6 +18,7 @@ cc_benchmark {
"libutils",
],
static_libs: [
"libattestation",
"libinputdispatcher",
],
}

View file

@ -48,6 +48,9 @@ cc_defaults {
"libui",
"libutils",
],
static_libs: [
"libattestation",
],
header_libs: [
"libinputdispatcher_headers",
],

View file

@ -53,8 +53,6 @@ static constexpr bool DEBUG_FOCUS = false;
#include <input/InputWindow.h>
#include <log/log.h>
#include <log/log_event_list.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <powermanager/PowerManager.h>
#include <statslog.h>
#include <unistd.h>
@ -344,53 +342,6 @@ static void addGestureMonitors(const std::vector<Monitor>& monitors,
}
}
static std::array<uint8_t, 128> getRandomKey() {
std::array<uint8_t, 128> key;
if (RAND_bytes(key.data(), key.size()) != 1) {
LOG_ALWAYS_FATAL("Can't generate HMAC key");
}
return key;
}
// --- HmacKeyManager ---
HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
size_t size;
switch (event.type) {
case VerifiedInputEvent::Type::KEY: {
size = sizeof(VerifiedKeyEvent);
break;
}
case VerifiedInputEvent::Type::MOTION: {
size = sizeof(VerifiedMotionEvent);
break;
}
}
const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
return sign(start, size);
}
std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
// SHA256 always generates 32-bytes result
std::array<uint8_t, 32> hash;
unsigned int hashLen = 0;
uint8_t* result =
HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
if (result == nullptr) {
ALOGE("Could not sign the data using HMAC");
return INVALID_HMAC;
}
if (hashLen != hash.size()) {
ALOGE("HMAC-SHA256 has unexpected length");
return INVALID_HMAC;
}
return hash;
}
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@ -2692,6 +2643,22 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
}
}
std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) const {
size_t size;
switch (event.type) {
case VerifiedInputEvent::Type::KEY: {
size = sizeof(VerifiedKeyEvent);
break;
}
case VerifiedInputEvent::Type::MOTION: {
size = sizeof(VerifiedMotionEvent);
break;
}
}
const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
return mHmacKeyManager.sign(start, size);
}
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
@ -2701,7 +2668,7 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature(
VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
verifiedEvent.actionMasked = actionMasked;
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
return mHmacKeyManager.sign(verifiedEvent);
return sign(verifiedEvent);
}
return INVALID_HMAC;
}
@ -2711,7 +2678,7 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature(
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
verifiedEvent.action = dispatchEntry.resolvedAction;
return mHmacKeyManager.sign(verifiedEvent);
return sign(verifiedEvent);
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
@ -3559,7 +3526,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
calculatedHmac = sign(verifiedKeyEvent);
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
@ -3567,7 +3534,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu
VerifiedMotionEvent verifiedMotionEvent =
verifiedMotionEventFromMotionEvent(motionEvent);
result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
calculatedHmac = sign(verifiedMotionEvent);
break;
}
default: {

View file

@ -31,6 +31,7 @@
#include "TouchState.h"
#include "TouchedWindow.h"
#include <attestation/HmacKeyManager.h>
#include <input/Input.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
@ -58,16 +59,6 @@ namespace android::inputdispatcher {
class Connection;
class HmacKeyManager {
public:
HmacKeyManager();
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
private:
std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
const std::array<uint8_t, 128> mHmacKey;
};
/* Dispatches events to input targets. Some functions of the input dispatcher, such as
* identifying input targets, are controlled by a separate policy object.
*
@ -133,6 +124,8 @@ public:
virtual status_t unregisterInputChannel(const InputChannel& inputChannel) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
private:
enum class DropReason {
NOT_DROPPED,

View file

@ -294,70 +294,6 @@ private:
}
};
// --- HmacKeyManagerTest ---
class HmacKeyManagerTest : public testing::Test {
protected:
HmacKeyManager mHmacKeyManager;
};
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
* that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
* tests.
*/
TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
KeyEvent event = getTestKeyEvent();
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
ASSERT_EQ(hmac1, hmac2);
}
/**
* Ensure that changes in VerifiedKeyEvent produce a different hmac.
*/
TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
KeyEvent event = getTestKeyEvent();
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
verifiedEvent.deviceId += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.source += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.eventTimeNanos += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.displayId += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.action += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.downTimeNanos += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.flags += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.keyCode += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.scanCode += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.metaState += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
verifiedEvent.repeatCount += 1;
ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
}
// --- InputDispatcherTest ---
class InputDispatcherTest : public testing::Test {
@ -2027,6 +1963,63 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
* that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
* tests.
*/
TEST_F(InputDispatcherTest, GeneratedHmac_IsConsistent) {
KeyEvent event = getTestKeyEvent();
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
std::array<uint8_t, 32> hmac1 = mDispatcher->sign(verifiedEvent);
std::array<uint8_t, 32> hmac2 = mDispatcher->sign(verifiedEvent);
ASSERT_EQ(hmac1, hmac2);
}
/**
* Ensure that changes in VerifiedKeyEvent produce a different hmac.
*/
TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) {
KeyEvent event = getTestKeyEvent();
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
std::array<uint8_t, 32> initialHmac = mDispatcher->sign(verifiedEvent);
verifiedEvent.deviceId += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.source += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.eventTimeNanos += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.displayId += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.action += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.downTimeNanos += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.flags += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.keyCode += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.scanCode += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.metaState += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.repeatCount += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
}
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms