Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/policy/core/common/cloud/cloud_policy_validator.h"
      6 
      7 #include "base/bind_helpers.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/sequenced_task_runner.h"
     11 #include "base/stl_util.h"
     12 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
     13 #include "crypto/signature_verifier.h"
     14 #include "google_apis/gaia/gaia_auth_util.h"
     15 #include "policy/proto/device_management_backend.pb.h"
     16 
     17 namespace em = enterprise_management;
     18 
     19 namespace policy {
     20 
     21 namespace {
     22 
     23 // Grace interval for policy timestamp checks, in seconds.
     24 const int kTimestampGraceIntervalSeconds = 60;
     25 
     26 // DER-encoded ASN.1 object identifier for the SHA1-RSA signature algorithm.
     27 const uint8 kSHA1SignatureAlgorithm[] = {
     28     0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
     29     0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
     30 };
     31 
     32 // DER-encoded ASN.1 object identifier for the SHA256-RSA signature algorithm
     33 // (source: http://tools.ietf.org/html/rfc5754 section 3.2).
     34 const uint8 kSHA256SignatureAlgorithm[] = {
     35     0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
     36     0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00
     37 };
     38 
     39 COMPILE_ASSERT(sizeof(kSHA256SignatureAlgorithm) ==
     40                sizeof(kSHA1SignatureAlgorithm), invalid_algorithm_size);
     41 
     42 const int kSignatureAlgorithmSize = sizeof(kSHA1SignatureAlgorithm);
     43 
     44 const char kMetricPolicyKeyVerification[] = "Enterprise.PolicyKeyVerification";
     45 
     46 enum MetricPolicyKeyVerification {
     47   // UMA metric recorded when the client has no verification key.
     48   METRIC_POLICY_KEY_VERIFICATION_KEY_MISSING,
     49   // Recorded when the policy being verified has no key signature (e.g. policy
     50   // fetched before the server supported the verification key).
     51   METRIC_POLICY_KEY_VERIFICATION_SIGNATURE_MISSING,
     52   // Recorded when the key signature did not match the expected value (in
     53   // theory, this should only happen after key rotation or if the policy cached
     54   // on disk has been modified).
     55   METRIC_POLICY_KEY_VERIFICATION_FAILED,
     56   // Recorded when key verification succeeded.
     57   METRIC_POLICY_KEY_VERIFICATION_SUCCEEDED,
     58   METRIC_POLICY_KEY_VERIFICATION_SIZE  // Must be the last.
     59 };
     60 
     61 }  // namespace
     62 
     63 CloudPolicyValidatorBase::~CloudPolicyValidatorBase() {}
     64 
     65 void CloudPolicyValidatorBase::ValidateTimestamp(
     66     base::Time not_before,
     67     base::Time now,
     68     ValidateTimestampOption timestamp_option) {
     69   // Timestamp should be from the past. We allow for a 1-minute grace interval
     70   // to cover clock drift.
     71   validation_flags_ |= VALIDATE_TIMESTAMP;
     72   timestamp_not_before_ =
     73       (not_before - base::Time::UnixEpoch()).InMilliseconds();
     74   timestamp_not_after_ =
     75       ((now + base::TimeDelta::FromSeconds(kTimestampGraceIntervalSeconds)) -
     76           base::Time::UnixEpoch()).InMillisecondsRoundedUp();
     77   timestamp_option_ = timestamp_option;
     78 }
     79 
     80 void CloudPolicyValidatorBase::ValidateUsername(
     81     const std::string& expected_user,
     82     bool canonicalize) {
     83   validation_flags_ |= VALIDATE_USERNAME;
     84   user_ = expected_user;
     85   canonicalize_user_ = canonicalize;
     86 }
     87 
     88 void CloudPolicyValidatorBase::ValidateDomain(
     89     const std::string& expected_domain) {
     90   validation_flags_ |= VALIDATE_DOMAIN;
     91   domain_ = gaia::CanonicalizeDomain(expected_domain);
     92 }
     93 
     94 void CloudPolicyValidatorBase::ValidateDMToken(
     95     const std::string& token,
     96     ValidateDMTokenOption dm_token_option) {
     97   validation_flags_ |= VALIDATE_TOKEN;
     98   token_ = token;
     99   dm_token_option_ = dm_token_option;
    100 }
    101 
    102 void CloudPolicyValidatorBase::ValidatePolicyType(
    103     const std::string& policy_type) {
    104   validation_flags_ |= VALIDATE_POLICY_TYPE;
    105   policy_type_ = policy_type;
    106 }
    107 
    108 void CloudPolicyValidatorBase::ValidateSettingsEntityId(
    109     const std::string& settings_entity_id) {
    110   validation_flags_ |= VALIDATE_ENTITY_ID;
    111   settings_entity_id_ = settings_entity_id;
    112 }
    113 
    114 void CloudPolicyValidatorBase::ValidatePayload() {
    115   validation_flags_ |= VALIDATE_PAYLOAD;
    116 }
    117 
    118 
    119 void CloudPolicyValidatorBase::ValidateCachedKey(
    120     const std::string& cached_key,
    121     const std::string& cached_key_signature,
    122     const std::string& verification_key,
    123     const std::string& owning_domain) {
    124   validation_flags_ |= VALIDATE_CACHED_KEY;
    125   set_verification_key_and_domain(verification_key, owning_domain);
    126   cached_key_ = cached_key;
    127   cached_key_signature_ = cached_key_signature;
    128 }
    129 
    130 void CloudPolicyValidatorBase::ValidateSignature(
    131     const std::string& key,
    132     const std::string& verification_key,
    133     const std::string& owning_domain,
    134     bool allow_key_rotation) {
    135   validation_flags_ |= VALIDATE_SIGNATURE;
    136   set_verification_key_and_domain(verification_key, owning_domain);
    137   key_ = key;
    138   allow_key_rotation_ = allow_key_rotation;
    139 }
    140 
    141 void CloudPolicyValidatorBase::ValidateInitialKey(
    142     const std::string& verification_key,
    143     const std::string& owning_domain) {
    144   validation_flags_ |= VALIDATE_INITIAL_KEY;
    145   set_verification_key_and_domain(verification_key, owning_domain);
    146 }
    147 
    148 void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
    149     const em::PolicyData* policy_data,
    150     ValidateTimestampOption timestamp_option,
    151     ValidateDMTokenOption dm_token_option) {
    152   base::Time last_policy_timestamp;
    153   std::string expected_dm_token;
    154   if (policy_data) {
    155     last_policy_timestamp =
    156         base::Time::UnixEpoch() +
    157         base::TimeDelta::FromMilliseconds(policy_data->timestamp());
    158     expected_dm_token = policy_data->request_token();
    159   }
    160   ValidateTimestamp(last_policy_timestamp, base::Time::NowFromSystemTime(),
    161                     timestamp_option);
    162   ValidateDMToken(expected_dm_token, dm_token_option);
    163 }
    164 
    165 CloudPolicyValidatorBase::CloudPolicyValidatorBase(
    166     scoped_ptr<em::PolicyFetchResponse> policy_response,
    167     google::protobuf::MessageLite* payload,
    168     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
    169     : status_(VALIDATION_OK),
    170       policy_(policy_response.Pass()),
    171       payload_(payload),
    172       validation_flags_(0),
    173       timestamp_not_before_(0),
    174       timestamp_not_after_(0),
    175       timestamp_option_(TIMESTAMP_REQUIRED),
    176       dm_token_option_(DM_TOKEN_REQUIRED),
    177       canonicalize_user_(false),
    178       allow_key_rotation_(false),
    179       background_task_runner_(background_task_runner) {}
    180 
    181 void CloudPolicyValidatorBase::PostValidationTask(
    182     const base::Closure& completion_callback) {
    183   background_task_runner_->PostTask(
    184       FROM_HERE,
    185       base::Bind(&CloudPolicyValidatorBase::PerformValidation,
    186                  base::Passed(scoped_ptr<CloudPolicyValidatorBase>(this)),
    187                  base::MessageLoop::current()->message_loop_proxy(),
    188                  completion_callback));
    189 }
    190 
    191 // static
    192 void CloudPolicyValidatorBase::PerformValidation(
    193     scoped_ptr<CloudPolicyValidatorBase> self,
    194     scoped_refptr<base::MessageLoopProxy> message_loop,
    195     const base::Closure& completion_callback) {
    196   // Run the validation activities on this thread.
    197   self->RunValidation();
    198 
    199   // Report completion on |message_loop|.
    200   message_loop->PostTask(
    201       FROM_HERE,
    202       base::Bind(&CloudPolicyValidatorBase::ReportCompletion,
    203                  base::Passed(&self),
    204                  completion_callback));
    205 }
    206 
    207 // static
    208 void CloudPolicyValidatorBase::ReportCompletion(
    209     scoped_ptr<CloudPolicyValidatorBase> self,
    210     const base::Closure& completion_callback) {
    211   completion_callback.Run();
    212 }
    213 
    214 void CloudPolicyValidatorBase::RunValidation() {
    215   policy_data_.reset(new em::PolicyData());
    216   RunChecks();
    217 }
    218 
    219 void CloudPolicyValidatorBase::RunChecks() {
    220   status_ = VALIDATION_OK;
    221   if ((policy_->has_error_code() && policy_->error_code() != 200) ||
    222       (policy_->has_error_message() && !policy_->error_message().empty())) {
    223     LOG(ERROR) << "Error in policy blob."
    224                << " code: " << policy_->error_code()
    225                << " message: " << policy_->error_message();
    226     status_ = VALIDATION_ERROR_CODE_PRESENT;
    227     return;
    228   }
    229 
    230   // Parse policy data.
    231   if (!policy_data_->ParseFromString(policy_->policy_data()) ||
    232       !policy_data_->IsInitialized()) {
    233     LOG(ERROR) << "Failed to parse policy response";
    234     status_ = VALIDATION_PAYLOAD_PARSE_ERROR;
    235     return;
    236   }
    237 
    238   // Table of checks we run. These are sorted by descending severity of the
    239   // error, s.t. the most severe check will determine the validation status.
    240   static const struct {
    241     int flag;
    242     Status (CloudPolicyValidatorBase::* checkFunction)();
    243   } kCheckFunctions[] = {
    244     { VALIDATE_SIGNATURE,   &CloudPolicyValidatorBase::CheckSignature },
    245     { VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
    246     { VALIDATE_CACHED_KEY,  &CloudPolicyValidatorBase::CheckCachedKey },
    247     { VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
    248     { VALIDATE_ENTITY_ID,   &CloudPolicyValidatorBase::CheckEntityId },
    249     { VALIDATE_TOKEN,       &CloudPolicyValidatorBase::CheckToken },
    250     { VALIDATE_USERNAME,    &CloudPolicyValidatorBase::CheckUsername },
    251     { VALIDATE_DOMAIN,      &CloudPolicyValidatorBase::CheckDomain },
    252     { VALIDATE_TIMESTAMP,   &CloudPolicyValidatorBase::CheckTimestamp },
    253     { VALIDATE_PAYLOAD,     &CloudPolicyValidatorBase::CheckPayload },
    254   };
    255 
    256   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCheckFunctions); ++i) {
    257     if (validation_flags_ & kCheckFunctions[i].flag) {
    258       status_ = (this->*(kCheckFunctions[i].checkFunction))();
    259       if (status_ != VALIDATION_OK)
    260         break;
    261     }
    262   }
    263 }
    264 
    265 // Verifies the |new_public_key_verification_signature| for the |new_public_key|
    266 // in the policy blob.
    267 bool CloudPolicyValidatorBase::CheckNewPublicKeyVerificationSignature() {
    268   // If there's no local verification key, then just return true (no
    269   // validation possible).
    270   if (verification_key_.empty()) {
    271     UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
    272                               METRIC_POLICY_KEY_VERIFICATION_KEY_MISSING,
    273                               METRIC_POLICY_KEY_VERIFICATION_SIZE);
    274     return true;
    275   }
    276 
    277   if (!policy_->has_new_public_key_verification_signature()) {
    278     // Policy does not contain a verification signature, so log an error.
    279     LOG(ERROR) << "Policy is missing public_key_verification_signature";
    280     UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
    281                               METRIC_POLICY_KEY_VERIFICATION_SIGNATURE_MISSING,
    282                               METRIC_POLICY_KEY_VERIFICATION_SIZE);
    283     return false;
    284   }
    285 
    286   if (!CheckVerificationKeySignature(
    287           policy_->new_public_key(),
    288           verification_key_,
    289           policy_->new_public_key_verification_signature())) {
    290     LOG(ERROR) << "Signature verification failed";
    291     UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
    292                               METRIC_POLICY_KEY_VERIFICATION_FAILED,
    293                               METRIC_POLICY_KEY_VERIFICATION_SIZE);
    294     return false;
    295   }
    296   // Signature verification succeeded - return success to the caller.
    297   DVLOG(1) << "Signature verification succeeded";
    298   UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
    299                             METRIC_POLICY_KEY_VERIFICATION_SUCCEEDED,
    300                             METRIC_POLICY_KEY_VERIFICATION_SIZE);
    301   return true;
    302 }
    303 
    304 bool CloudPolicyValidatorBase::CheckVerificationKeySignature(
    305     const std::string& key,
    306     const std::string& verification_key,
    307     const std::string& signature) {
    308   DCHECK(!verification_key.empty());
    309   em::PolicyPublicKeyAndDomain signed_data;
    310   signed_data.set_new_public_key(key);
    311 
    312   // If no owning_domain_ supplied, try extracting the domain from the policy
    313   // itself (this happens on certain platforms during startup, when we validate
    314   // cached policy before prefs are loaded).
    315   std::string domain = owning_domain_.empty() ?
    316       ExtractDomainFromPolicy() : owning_domain_;
    317   if (domain.empty()) {
    318     LOG(ERROR) << "Policy does not contain a domain";
    319     return false;
    320   }
    321   signed_data.set_domain(domain);
    322   std::string signed_data_as_string;
    323   if (!signed_data.SerializeToString(&signed_data_as_string)) {
    324     DLOG(ERROR) << "Could not serialize verification key to string";
    325     return false;
    326   }
    327   return VerifySignature(signed_data_as_string, verification_key, signature,
    328                          SHA256);
    329 }
    330 
    331 std::string CloudPolicyValidatorBase::ExtractDomainFromPolicy() {
    332   std::string domain;
    333   if (policy_data_->has_username()) {
    334     domain = gaia::ExtractDomainName(
    335         gaia::CanonicalizeEmail(
    336             gaia::SanitizeEmail(policy_data_->username())));
    337   }
    338   return domain;
    339 }
    340 
    341 void CloudPolicyValidatorBase::set_verification_key_and_domain(
    342     const std::string& verification_key, const std::string& owning_domain) {
    343   // Make sure we aren't overwriting the verification key with a different key.
    344   DCHECK(verification_key_.empty() || verification_key_ == verification_key);
    345   DCHECK(owning_domain_.empty() || owning_domain_ == owning_domain);
    346   verification_key_ = verification_key;
    347   owning_domain_ = owning_domain;
    348 }
    349 
    350 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
    351   const std::string* signature_key = &key_;
    352   if (policy_->has_new_public_key() && allow_key_rotation_) {
    353     signature_key = &policy_->new_public_key();
    354     if (!policy_->has_new_public_key_signature() ||
    355         !VerifySignature(policy_->new_public_key(), key_,
    356                          policy_->new_public_key_signature(), SHA1)) {
    357       LOG(ERROR) << "New public key rotation signature verification failed";
    358       return VALIDATION_BAD_SIGNATURE;
    359     }
    360 
    361     if (!CheckNewPublicKeyVerificationSignature()) {
    362       LOG(ERROR) << "New public key root verification failed";
    363       return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
    364     }
    365   }
    366 
    367   if (!policy_->has_policy_data_signature() ||
    368       !VerifySignature(policy_->policy_data(), *signature_key,
    369                        policy_->policy_data_signature(), SHA1)) {
    370     LOG(ERROR) << "Policy signature validation failed";
    371     return VALIDATION_BAD_SIGNATURE;
    372   }
    373 
    374   return VALIDATION_OK;
    375 }
    376 
    377 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
    378   if (!policy_->has_new_public_key() ||
    379       !policy_->has_policy_data_signature() ||
    380       !VerifySignature(policy_->policy_data(), policy_->new_public_key(),
    381                        policy_->policy_data_signature(), SHA1)) {
    382     LOG(ERROR) << "Initial policy signature validation failed";
    383     return VALIDATION_BAD_INITIAL_SIGNATURE;
    384   }
    385 
    386   if (!CheckNewPublicKeyVerificationSignature()) {
    387     LOG(ERROR) << "Initial policy root signature validation failed";
    388     return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
    389   }
    390   return VALIDATION_OK;
    391 }
    392 
    393 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckCachedKey() {
    394   if (!verification_key_.empty() &&
    395       !CheckVerificationKeySignature(cached_key_, verification_key_,
    396                                      cached_key_signature_)) {
    397     LOG(ERROR) << "Cached key signature verification failed";
    398     return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
    399   } else {
    400     DVLOG(1) << "Cached key signature verification succeeded";
    401   }
    402   return VALIDATION_OK;
    403 }
    404 
    405 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() {
    406   if (!policy_data_->has_policy_type() ||
    407        policy_data_->policy_type() != policy_type_) {
    408     LOG(ERROR) << "Wrong policy type " << policy_data_->policy_type();
    409     return VALIDATION_WRONG_POLICY_TYPE;
    410   }
    411 
    412   return VALIDATION_OK;
    413 }
    414 
    415 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckEntityId() {
    416   if (!policy_data_->has_settings_entity_id() ||
    417       policy_data_->settings_entity_id() != settings_entity_id_) {
    418     LOG(ERROR) << "Wrong settings_entity_id "
    419                << policy_data_->settings_entity_id() << ", expected "
    420                << settings_entity_id_;
    421     return VALIDATION_WRONG_SETTINGS_ENTITY_ID;
    422   }
    423 
    424   return VALIDATION_OK;
    425 }
    426 
    427 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckTimestamp() {
    428   if (!policy_data_->has_timestamp()) {
    429     if (timestamp_option_ == TIMESTAMP_NOT_REQUIRED) {
    430       return VALIDATION_OK;
    431     } else {
    432       LOG(ERROR) << "Policy timestamp missing";
    433       return VALIDATION_BAD_TIMESTAMP;
    434     }
    435   }
    436 
    437   if (timestamp_option_ != TIMESTAMP_NOT_REQUIRED &&
    438       policy_data_->timestamp() < timestamp_not_before_) {
    439     // If |timestamp_option_| is TIMESTAMP_REQUIRED or TIMESTAMP_NOT_BEFORE
    440     // then this is a failure.
    441     LOG(ERROR) << "Policy too old: " << policy_data_->timestamp();
    442     return VALIDATION_BAD_TIMESTAMP;
    443   }
    444   if (timestamp_option_ == TIMESTAMP_REQUIRED &&
    445       policy_data_->timestamp() > timestamp_not_after_) {
    446     LOG(ERROR) << "Policy from the future: " << policy_data_->timestamp();
    447     return VALIDATION_BAD_TIMESTAMP;
    448   }
    449 
    450   return VALIDATION_OK;
    451 }
    452 
    453 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckToken() {
    454   // Make sure the token matches the expected token (if any) and also
    455   // make sure the token itself is valid (non-empty if DM_TOKEN_REQUIRED).
    456   if (dm_token_option_ == DM_TOKEN_REQUIRED &&
    457       (!policy_data_->has_request_token() ||
    458        policy_data_->request_token().empty())) {
    459     LOG(ERROR) << "Empty DM token encountered - expected: " << token_;
    460     return VALIDATION_WRONG_TOKEN;
    461   }
    462   if (!token_.empty() && policy_data_->request_token() != token_) {
    463     LOG(ERROR) << "Invalid DM token: " << policy_data_->request_token()
    464                << " - expected: " << token_;
    465     return VALIDATION_WRONG_TOKEN;
    466   }
    467 
    468   return VALIDATION_OK;
    469 }
    470 
    471 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
    472   if (!policy_data_->has_username()) {
    473     LOG(ERROR) << "Policy is missing user name";
    474     return VALIDATION_BAD_USERNAME;
    475   }
    476 
    477   std::string expected = user_;
    478   std::string actual = policy_data_->username();
    479   if (canonicalize_user_) {
    480     expected = gaia::CanonicalizeEmail(gaia::SanitizeEmail(expected));
    481     actual = gaia::CanonicalizeEmail(gaia::SanitizeEmail(actual));
    482   }
    483 
    484   if (expected != actual) {
    485     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    486     return VALIDATION_BAD_USERNAME;
    487   }
    488 
    489   return VALIDATION_OK;
    490 }
    491 
    492 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() {
    493   std::string policy_domain = ExtractDomainFromPolicy();
    494   if (policy_domain.empty()) {
    495     LOG(ERROR) << "Policy is missing user name";
    496     return VALIDATION_BAD_USERNAME;
    497   }
    498 
    499   if (domain_ != policy_domain) {
    500     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    501     return VALIDATION_BAD_USERNAME;
    502   }
    503 
    504   return VALIDATION_OK;
    505 }
    506 
    507 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPayload() {
    508   if (!policy_data_->has_policy_value() ||
    509       !payload_->ParseFromString(policy_data_->policy_value()) ||
    510       !payload_->IsInitialized()) {
    511     LOG(ERROR) << "Failed to decode policy payload protobuf";
    512     return VALIDATION_POLICY_PARSE_ERROR;
    513   }
    514 
    515   return VALIDATION_OK;
    516 }
    517 
    518 // static
    519 bool CloudPolicyValidatorBase::VerifySignature(const std::string& data,
    520                                                const std::string& key,
    521                                                const std::string& signature,
    522                                                SignatureType signature_type) {
    523   crypto::SignatureVerifier verifier;
    524   const uint8* algorithm = NULL;
    525   switch (signature_type) {
    526     case SHA1:
    527       algorithm = kSHA1SignatureAlgorithm;
    528       break;
    529     case SHA256:
    530       algorithm = kSHA256SignatureAlgorithm;
    531       break;
    532     default:
    533       NOTREACHED() << "Invalid signature type: " << signature_type;
    534       return false;
    535   }
    536 
    537   if (!verifier.VerifyInit(algorithm, kSignatureAlgorithmSize,
    538                            reinterpret_cast<const uint8*>(signature.c_str()),
    539                            signature.size(),
    540                            reinterpret_cast<const uint8*>(key.c_str()),
    541                            key.size())) {
    542     DLOG(ERROR) << "Invalid verification signature/key format";
    543     return false;
    544   }
    545   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
    546                         data.size());
    547   return verifier.VerifyFinal();
    548 }
    549 
    550 template class CloudPolicyValidator<em::CloudPolicySettings>;
    551 
    552 #if !defined(OS_ANDROID) && !defined(OS_IOS)
    553 template class CloudPolicyValidator<em::ExternalPolicyData>;
    554 #endif
    555 
    556 }  // namespace policy
    557