/* ** ** Copyright 2017, 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 <keymaster/legacy_support/keymaster1_legacy_support.h> #include <android-base/logging.h> #include <assert.h> #include <algorithm> #include <vector> namespace keymaster { template <typename T> std::vector<T> make_vector(const T* array, size_t len) { return std::vector<T>(array, array + len); } // This helper class implements just enough of the C++ standard collection interface to be able to // accept push_back calls, and it does nothing but count them. It's useful when you want to count // insertions but not actually store anything. It's used in digest_set_is_full below to count the // size of a set intersection. struct PushbackCounter { struct value_type { // NOLINTNEXTLINE(google-explicit-constructor) template <typename T> value_type(const T&) {} }; void push_back(const value_type&) { ++count; } size_t count = 0; }; static std::vector<keymaster_digest_t> full_digest_list = { KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; template <typename Iter> static bool digest_set_is_full(Iter begin, Iter end) { PushbackCounter counter; std::set_intersection(begin, end, full_digest_list.begin(), full_digest_list.end(), std::back_inserter(counter)); return counter.count == full_digest_list.size(); } static keymaster_error_t add_digests(const keymaster1_device_t* dev, keymaster_algorithm_t algorithm, keymaster_purpose_t purpose, Keymaster1LegacySupport::DigestMap* map, bool* supports_all) { auto key = std::make_pair(algorithm, purpose); keymaster_digest_t* digests; size_t digests_length; keymaster_error_t error = dev->get_supported_digests(dev, algorithm, purpose, &digests, &digests_length); if (error != KM_ERROR_OK) { LOG(ERROR) << "Error " << error << " getting supported digests from keymaster1 device"; return error; } std::unique_ptr<keymaster_digest_t, Malloc_Delete> digests_deleter(digests); auto digest_vec = make_vector(digests, digests_length); *supports_all = digest_set_is_full(digest_vec.begin(), digest_vec.end()); (*map)[key] = std::move(digest_vec); return error; } static keymaster_error_t map_digests(const keymaster1_device_t* dev, Keymaster1LegacySupport::DigestMap* map, bool* supports_all) { map->clear(); *supports_all = true; keymaster_algorithm_t sig_algorithms[] = {KM_ALGORITHM_RSA, KM_ALGORITHM_EC, KM_ALGORITHM_HMAC}; keymaster_purpose_t sig_purposes[] = {KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY}; for (auto algorithm : sig_algorithms) for (auto purpose : sig_purposes) { bool alg_purpose_supports_all; keymaster_error_t error = add_digests(dev, algorithm, purpose, map, &alg_purpose_supports_all); if (error != KM_ERROR_OK) return error; *supports_all &= alg_purpose_supports_all; } keymaster_algorithm_t crypt_algorithms[] = {KM_ALGORITHM_RSA}; keymaster_purpose_t crypt_purposes[] = {KM_PURPOSE_ENCRYPT, KM_PURPOSE_DECRYPT}; for (auto algorithm : crypt_algorithms) for (auto purpose : crypt_purposes) { bool alg_purpose_supports_all; keymaster_error_t error = add_digests(dev, algorithm, purpose, map, &alg_purpose_supports_all); if (error != KM_ERROR_OK) return error; *supports_all &= alg_purpose_supports_all; } return KM_ERROR_OK; } Keymaster1LegacySupport::Keymaster1LegacySupport(const keymaster1_device_t* dev) { map_digests(dev, &device_digests_, &supports_all_); } template <typename Collection, typename Value> bool contains(const Collection& c, const Value& v) { return std::find(c.begin(), c.end(), v) != c.end(); } template <typename T> static bool findUnsupportedDigest(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose, keymaster_digest_t digest, const T& params, const Keymaster1LegacySupport::DigestMap& digest_map) { auto supported_digests = digest_map.find(std::make_pair(algorithm, purpose)); if (supported_digests == digest_map.end()) // Invalid algorith/purpose pair (e.g. EC encrypt). Let the error be handled by HW module. return false; if (digest != KM_DIGEST_NONE && !contains(supported_digests->second, digest)) { LOG(WARNING) << "Digest " << digest << " requested but not supported by KM1 hal"; return true; } for (auto& entry : params) if (entry.tag == TAG_DIGEST) if (!contains(supported_digests->second, entry.enumerated)) { LOG(WARNING) << "Digest " << entry.enumerated << " requested but not supported by KM1 hal"; return true; } return false; } template <typename T> bool requiresSoftwareDigesting(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose, keymaster_digest_t digest, const T& params, const Keymaster1LegacySupport::DigestMap& digest_map) { switch (algorithm) { case KM_ALGORITHM_AES: case KM_ALGORITHM_TRIPLE_DES: LOG(WARNING) << "Not performing software digesting for symmetric cipher keys"; return false; case KM_ALGORITHM_HMAC: case KM_ALGORITHM_RSA: case KM_ALGORITHM_EC: break; } if (!findUnsupportedDigest(algorithm, purpose, digest, params, digest_map)) { LOG(DEBUG) << "Requested digest(s) supported for algorithm " << algorithm << " and purpose " << purpose; return false; } return true; } bool Keymaster1LegacySupport::RequiresSoftwareDigesting( const AuthorizationSet& key_description) const { keymaster_algorithm_t algorithm; if (!key_description.GetTagValue(TAG_ALGORITHM, &algorithm)) { // The hardware module will return an error during keygen. return false; } if (supports_all_) return false; bool has_purpose = false; for (auto& entry : key_description) if (entry.tag == TAG_PURPOSE) { has_purpose = true; keymaster_purpose_t purpose = static_cast<keymaster_purpose_t>(entry.enumerated); if (requiresSoftwareDigesting(algorithm, purpose, KM_DIGEST_NONE, key_description, device_digests_)) return true; } return !has_purpose; } bool Keymaster1LegacySupport::RequiresSoftwareDigesting(const keymaster_digest_t digest, const AuthProxy& key_description) const { keymaster_algorithm_t algorithm; if (!key_description.GetTagValue(TAG_ALGORITHM, &algorithm)) { // The hardware module will return an error during keygen. return false; } if (supports_all_) return false; bool has_purpose = false; for (auto& entry : key_description) { if (entry.tag == TAG_PURPOSE) { has_purpose = true; keymaster_purpose_t purpose = static_cast<keymaster_purpose_t>(entry.enumerated); if (requiresSoftwareDigesting(algorithm, purpose, digest, key_description, device_digests_)) return true; } } /* * If the key does not have a purpose it is unusable, i.e., for private key operations. * The public key operations which don't need purpose authorization may as well be done * in software. This also addresses a bug by which begin operation on keys without purpose and * unauthorized digest which is also not supported by the wrapped KM1 device fail with * KM_UNSUPPORTED_DIGEST although they should not fail during the begin operation. * If it has a purpose and we reach this point we did not find unsupported digests, and * therefore do not required software digesting. */ return !has_purpose; } template<> keymaster_error_t Keymaster1ArbitrationFactory<EcdsaKeymaster1KeyFactory>::GenerateKey( const AuthorizationSet& key_description, KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) const { if (legacy_support_.RequiresSoftwareDigesting(key_description)) { return software_digest_factory_.GenerateKey(key_description, key_blob, hw_enforced, sw_enforced); } else { AuthorizationSet mutable_key_description = key_description; keymaster_ec_curve_t curve; if (key_description.GetTagValue(TAG_EC_CURVE, &curve)) { // Keymaster1 doesn't know about EC curves. We need to translate to key size. uint32_t key_size_from_curve; keymaster_error_t error = EcCurveToKeySize(curve, &key_size_from_curve); if (error != KM_ERROR_OK) { return error; } uint32_t key_size_from_desc; if (key_description.GetTagValue(TAG_KEY_SIZE, &key_size_from_desc)) { if (key_size_from_desc != key_size_from_curve) { return KM_ERROR_INVALID_ARGUMENT; } } else { mutable_key_description.push_back(TAG_KEY_SIZE, key_size_from_curve); } } return passthrough_factory_.GenerateKey(mutable_key_description, key_blob, hw_enforced, sw_enforced); } } template<> keymaster_error_t Keymaster1ArbitrationFactory<EcdsaKeymaster1KeyFactory>::LoadKey(KeymasterKeyBlob&& key_material, const AuthorizationSet& additional_params, AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced, UniquePtr<Key>* key) const { keymaster_digest_t digest; if (!additional_params.GetTagValue(TAG_DIGEST, &digest)) { digest = KM_DIGEST_NONE; } bool requires_software_digesting = legacy_support_.RequiresSoftwareDigesting(digest, AuthProxy(hw_enforced, sw_enforced)); auto rc = software_digest_factory_.LoadKey(move(key_material), additional_params, move(hw_enforced), move(sw_enforced), key); if (rc != KM_ERROR_OK) return rc; if (!requires_software_digesting) { (*key)->key_factory() = & passthrough_factory_; } return KM_ERROR_OK; } template<> keymaster_error_t Keymaster1ArbitrationFactory<RsaKeymaster1KeyFactory>::LoadKey(KeymasterKeyBlob&& key_material, const AuthorizationSet& additional_params, AuthorizationSet&& hw_enforced, AuthorizationSet&& sw_enforced, UniquePtr<Key>* key) const { keymaster_digest_t digest; if (!additional_params.GetTagValue(TAG_DIGEST, &digest)) { digest = KM_DIGEST_NONE; } bool requires_software_digesting = legacy_support_.RequiresSoftwareDigesting(digest, AuthProxy(hw_enforced, sw_enforced)); auto rc = software_digest_factory_.LoadKey(move(key_material), additional_params, move(hw_enforced), move(sw_enforced), key); if (rc != KM_ERROR_OK) return rc; if (!requires_software_digesting) { (*key)->key_factory() = & passthrough_factory_; } return KM_ERROR_OK; } } // namespace keymaster