300 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
| **
 | |
| ** 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,  //
 | |
|     UniquePtr<Key> attest_key,                //
 | |
|     const KeymasterBlob& issuer_subject,      //
 | |
|     KeymasterKeyBlob* key_blob,               //
 | |
|     AuthorizationSet* hw_enforced,            //
 | |
|     AuthorizationSet* sw_enforced,            //
 | |
|     CertificateChain* cert_chain) const {
 | |
|     if (legacy_support_.RequiresSoftwareDigesting(key_description)) {
 | |
|         return software_digest_factory_.GenerateKey(key_description, move(attest_key),
 | |
|                                                     issuer_subject, key_blob, hw_enforced,
 | |
|                                                     sw_enforced, cert_chain);
 | |
|     } 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, move(attest_key),
 | |
|                                                 issuer_subject, key_blob, hw_enforced, sw_enforced,
 | |
|                                                 cert_chain);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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
 |