From f0f05d4052347dabe063f034956df3b6ed2ad5e1 Mon Sep 17 00:00:00 2001 From: Shawn Willden Date: Tue, 1 May 2018 17:08:39 -0600 Subject: [PATCH] Add utility method to perform HMAC agreement To make it easier for clients (vold & keystore) to perform key agreement, this CL adds a service method that does it. To make key agreement consistent, this method sorts the HMAC sharing parameters lexicographically. The requirement for sorting is documented in the HAL. Test: Boot device Bug: 79307225 Bug: 78766190 Change-Id: Idb224f27f8e4426281d9a0105605ba22bf7c7e95 --- keymaster/4.0/support/Keymaster.cpp | 111 ++++++++++++++++-- .../support/include/keymasterV4_0/Keymaster.h | 22 +++- .../include/keymasterV4_0/Keymaster3.h | 4 +- .../include/keymasterV4_0/Keymaster4.h | 4 +- .../include/keymasterV4_0/keymaster_utils.h | 8 ++ keymaster/4.0/support/keymaster_utils.cpp | 16 +++ 6 files changed, 148 insertions(+), 17 deletions(-) diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.0/support/Keymaster.cpp index fac0017474..066bca4100 100644 --- a/keymaster/4.0/support/Keymaster.cpp +++ b/keymaster/4.0/support/Keymaster.cpp @@ -16,24 +16,73 @@ #include +#include + #include #include #include #include +#include +#include namespace android { namespace hardware { + +template +std::ostream& operator<<(std::ostream& os, const hidl_vec& vec) { + os << "{ "; + if (vec.size()) { + for (size_t i = 0; i < vec.size() - 1; ++i) os << vec[i] << ", "; + os << vec[vec.size() - 1]; + } + os << " }"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const hidl_vec& vec) { + std::ios_base::fmtflags flags(os.flags()); + os << std::setw(2) << std::setfill('0') << std::hex; + for (uint8_t c : vec) os << static_cast(c); + os.flags(flags); + return os; +} + +template +std::ostream& operator<<(std::ostream& os, const hidl_array& vec) { + std::ios_base::fmtflags flags(os.flags()); + os << std::setw(2) << std::setfill('0') << std::hex; + for (size_t i = 0; i < N; ++i) os << static_cast(vec[i]); + os.flags(flags); + return os; +} + namespace keymaster { namespace V4_0 { + +std::ostream& operator<<(std::ostream& os, const HmacSharingParameters& params) { + // Note that by design, although seed and nonce are used to compute a secret, they are + // not secrets and it's just fine to log them. + os << "(seed: " << params.seed << ", nonce: " << params.nonce << ')'; + return os; +} + namespace support { using ::android::sp; using ::android::hidl::manager::V1_0::IServiceManager; +std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster) { + auto& version = keymaster.halVersion(); + os << version.keymasterName << " from " << version.authorName + << " SecurityLevel: " << toString(version.securityLevel) + << " HAL: " << keymaster.descriptor() << "/" << keymaster.instanceName(); + return os; +} + template std::vector> enumerateDevices( const sp& serviceManager) { - std::vector> result; + Keymaster::KeymasterSet result; bool foundDefault = false; auto& descriptor = Wrapper::WrappedIKeymasterDevice::descriptor; @@ -57,7 +106,7 @@ std::vector> enumerateDevices( return result; } -std::vector> Keymaster::enumerateAvailableDevices() { +Keymaster::KeymasterSet Keymaster::enumerateAvailableDevices() { auto serviceManager = IServiceManager::getService(); CHECK(serviceManager) << "Could not retrieve ServiceManager"; @@ -73,18 +122,62 @@ std::vector> Keymaster::enumerateAvailableDevices() { size_t i = 1; LOG(INFO) << "List of Keymaster HALs found:"; - for (auto& hal : result) { - auto& version = hal->halVersion(); - LOG(INFO) << "Keymaster HAL #" << i << ": " << version.keymasterName << " from " - << version.authorName << " SecurityLevel: " << toString(version.securityLevel) - << " HAL : " << hal->descriptor() << " instance " << hal->instanceName(); - } + for (auto& hal : result) LOG(INFO) << "Keymaster HAL #" << i++ << ": " << *hal; return result; } +static hidl_vec getHmacParameters( + const Keymaster::KeymasterSet& keymasters) { + std::vector params_vec; + params_vec.reserve(keymasters.size()); + for (auto& keymaster : keymasters) { + if (keymaster->halVersion().majorVersion < 4) continue; + auto rc = keymaster->getHmacSharingParameters([&](auto error, auto& params) { + CHECK(error == ErrorCode::OK) + << "Failed to get HMAC parameters from " << *keymaster << " error " << error; + params_vec.push_back(params); + }); + CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster + << " error: " << rc.description(); + } + std::sort(params_vec.begin(), params_vec.end()); + + return params_vec; +} + +static void computeHmac(const Keymaster::KeymasterSet& keymasters, + const hidl_vec& params) { + if (!params.size()) return; + + hidl_vec sharingCheck; + bool firstKeymaster = true; + LOG(DEBUG) << "Computing HMAC with params " << params; + for (auto& keymaster : keymasters) { + if (keymaster->halVersion().majorVersion < 4) continue; + LOG(DEBUG) << "Computing HMAC for " << *keymaster; + auto rc = keymaster->computeSharedHmac(params, [&](auto error, auto& curSharingCheck) { + CHECK(error == ErrorCode::OK) + << "Failed to get HMAC parameters from " << *keymaster << " error " << error; + if (firstKeymaster) { + sharingCheck = curSharingCheck; + firstKeymaster = false; + } + // TODO: Validate that curSharingCheck == sharingCheck. b/77588764 + // CHECK(curSharingCheck == sharingCheck) << "HMAC computation failed for " << + // *keymaster; + }); + CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster + << " error: " << rc.description(); + } +} + +void Keymaster::performHmacKeyAgreement(const KeymasterSet& keymasters) { + computeHmac(keymasters, getHmacParameters(keymasters)); +} + } // namespace support } // namespace V4_0 } // namespace keymaster } // namespace hardware -}; // namespace android +} // namespace android diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h index f9efd5160b..83b1d69990 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h +++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h @@ -37,6 +37,8 @@ namespace support { */ class Keymaster : public IKeymasterDevice { public: + using KeymasterSet = std::vector>; + Keymaster(const hidl_string& descriptor, const hidl_string& instanceName) : descriptor_(descriptor), instanceName_(instanceName) {} virtual ~Keymaster() {} @@ -55,21 +57,33 @@ class Keymaster : public IKeymasterDevice { } }; - virtual const VersionResult& halVersion() = 0; - const hidl_string& descriptor() { return descriptor_; } - const hidl_string& instanceName() { return instanceName_; } + virtual const VersionResult& halVersion() const = 0; + const hidl_string& descriptor() const { return descriptor_; } + const hidl_string& instanceName() const { return instanceName_; } /** * Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least * secure (as defined by VersionResult::operator<). */ - static std::vector> enumerateAvailableDevices(); + static KeymasterSet enumerateAvailableDevices(); + + /** + * Ask provided Keymaster instances to compute a shared HMAC key using + * getHmacSharingParameters() and computeSharedHmac(). This computation is idempotent as long + * as the same set of Keymaster instances is used each time (and if all of the instances work + * correctly). It must be performed once per boot, but should do no harm to be repeated. + * + * If key agreement fails, this method will crash the process (with CHECK). + */ + static void performHmacKeyAgreement(const KeymasterSet& keymasters); private: hidl_string descriptor_; hidl_string instanceName_; }; +std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster); + } // namespace support } // namespace V4_0 } // namespace keymaster diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h index 2bb77ca7b4..c40be7c757 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h +++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h @@ -45,8 +45,8 @@ class Keymaster3 : public Keymaster { km3_dev_(km3_dev), haveVersion_(false) {} - const VersionResult& halVersion() override { - getVersionIfNeeded(); + const VersionResult& halVersion() const override { + const_cast(this)->getVersionIfNeeded(); return version_; } diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h index 96afb13ac6..dfd03ef69f 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h +++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h @@ -37,8 +37,8 @@ class Keymaster4 : public Keymaster { haveVersion_(false), dev_(km4_dev) {} - const VersionResult& halVersion() override { - getVersionIfNeeded(); + const VersionResult& halVersion() const override { + const_cast(this)->getVersionIfNeeded(); return version_; } diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h index 1c1b000b79..90a0f1b29f 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h +++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h @@ -23,6 +23,14 @@ namespace android { namespace hardware { namespace keymaster { namespace V4_0 { + +/** + * Define a lexicographical ordering on HmacSharingParameters. The parameters to + * IKeymasterDevice::computeSharedHmac are required to be delivered in the order specified by this + * comparison operator. + */ +bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b); + namespace support { inline static hidl_vec blob2hidlVec(const uint8_t* data, const size_t length, diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp index bc610aa3bd..729e1c1dbf 100644 --- a/keymaster/4.0/support/keymaster_utils.cpp +++ b/keymaster/4.0/support/keymaster_utils.cpp @@ -19,8 +19,24 @@ namespace android { namespace hardware { + +inline static bool operator<(const hidl_vec& a, const hidl_vec& b) { + return memcmp(a.data(), b.data(), std::min(a.size(), b.size())) == -1; +} + +template +inline static bool operator<(const hidl_array& a, + const hidl_array& b) { + return memcmp(a.data(), b.data(), SIZE) == -1; +} + namespace keymaster { namespace V4_0 { + +bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b) { + return std::tie(a.seed, a.nonce) < std::tie(b.seed, b.nonce); +} + namespace support { template