1aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//
2aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Copyright (C) 2012 The Android Open Source Project
3aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//
4aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Licensed under the Apache License, Version 2.0 (the "License");
5aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// you may not use this file except in compliance with the License.
6aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// You may obtain a copy of the License at
7aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//
8aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//      http://www.apache.org/licenses/LICENSE-2.0
9aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//
10aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Unless required by applicable law or agreed to in writing, software
11aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// distributed under the License is distributed on an "AS IS" BASIS,
12aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// See the License for the specific language governing permissions and
14aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// limitations under the License.
15aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo//
167f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1714c0da88a93aa7b1aa71d5e7e923b537f0d419f3Alex Deymo#include "update_engine/certificate_checker.h"
187f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
197f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <string>
207f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
2139910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include <base/logging.h>
2275039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/string_number_conversions.h>
2375039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/string_util.h>
2475039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/stringprintf.h>
257f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <curl/curl.h>
267f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <openssl/evp.h>
277f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <openssl/ssl.h>
287f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
2939910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/constants.h"
3039910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/prefs_interface.h"
3139910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/utils.h"
327f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
337f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochausing std::string;
347f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
357f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochanamespace chromeos_update_engine {
367f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
377f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochabool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx,
387f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                          int* out_depth,
397f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                          unsigned int* out_digest_length,
40f68bbbc952aa9a71898e4939b5f36187fa564a50Alex Vakulenko                                          uint8_t* out_digest) const {
417f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  TEST_AND_RETURN_FALSE(out_digest);
427f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx);
437f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  TEST_AND_RETURN_FALSE(certificate);
447f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
457f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  if (out_depth)
467f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    *out_depth = depth;
477f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
487f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  unsigned int len;
497f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  const EVP_MD* digest_function = EVP_sha256();
507f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  bool success = X509_digest(certificate, digest_function, out_digest, &len);
517f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
527f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  if (success && out_digest_length)
537f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    *out_digest_length = len;
547f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  return success;
557f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}
567f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
5733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static
5833e91e78bfe98c063b0c3b6d590976e275685686Alex DeymoCertificateChecker* CertificateChecker::cert_checker_singleton_ = nullptr;
5933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
60c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex DeymoCertificateChecker::CertificateChecker(PrefsInterface* prefs,
6133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                       OpenSSLWrapper* openssl_wrapper)
6233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    : prefs_(prefs), openssl_wrapper_(openssl_wrapper) {
6333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo}
6433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
6533e91e78bfe98c063b0c3b6d590976e275685686Alex DeymoCertificateChecker::~CertificateChecker() {
6633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  if (cert_checker_singleton_ == this)
6733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    cert_checker_singleton_ = nullptr;
6833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo}
6933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
7033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymovoid CertificateChecker::Init() {
7133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  CHECK(cert_checker_singleton_ == nullptr);
7233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  cert_checker_singleton_ = this;
73c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo}
747f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
757f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha// static
767f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno RochaCURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle,
777f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                               SSL_CTX* ssl_ctx,
787f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                               void* ptr) {
7933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr);
8033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
8133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  if (!cert_checker_singleton_) {
8233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    DLOG(WARNING) << "No CertificateChecker singleton initialized.";
8333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    return CURLE_FAILED_INIT;
8433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  }
85c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo
867f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // From here we set the SSL_CTX to another callback, from the openssl library,
877f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // which will be called after each server certificate is validated. However,
887f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // since openssl does not allow us to pass our own data pointer to the
8933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // callback, the certificate check will have to be done statically. Since we
9033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // need to know which update server we are using in order to check the
9133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // certificate, we hardcode Chrome OS's two known update servers here, and
9233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // define a different static callback for each. Since this code should only
9333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // run in official builds, this should not be a problem. However, if an update
9433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // server different from the ones listed here is used, the check will not
9533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  // take place.
9633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  int (*verify_callback)(int, X509_STORE_CTX*);
9733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  switch (*server_to_check) {
9833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    case ServerToCheck::kDownload:
9933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      verify_callback = &CertificateChecker::VerifySSLCallbackDownload;
10033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      break;
10133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    case ServerToCheck::kUpdate:
10233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      verify_callback = &CertificateChecker::VerifySSLCallbackUpdate;
10333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      break;
10433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    case ServerToCheck::kNone:
10533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      verify_callback = nullptr;
10633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      break;
10733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  }
1087f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
10933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
1107f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  return CURLE_OK;
1117f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}
1127f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1137f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha// static
11433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymoint CertificateChecker::VerifySSLCallbackDownload(int preverify_ok,
11533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                                  X509_STORE_CTX* x509_ctx) {
11633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload);
11733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo}
11833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
11933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static
12033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymoint CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok,
12133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                                X509_STORE_CTX* x509_ctx) {
12233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate);
12333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo}
12433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo
12533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static
126c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymoint CertificateChecker::VerifySSLCallback(int preverify_ok,
12733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                          X509_STORE_CTX* x509_ctx,
12833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                          ServerToCheck server_to_check) {
12933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  CHECK(cert_checker_singleton_ != nullptr);
13033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  return cert_checker_singleton_->CheckCertificateChange(
13133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo      preverify_ok, x509_ctx, server_to_check) ? 1 : 0;
1327f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}
1337f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
134c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymobool CertificateChecker::CheckCertificateChange(int preverify_ok,
13533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                                X509_STORE_CTX* x509_ctx,
13633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                                                ServerToCheck server_to_check) {
137c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo  TEST_AND_RETURN_FALSE(prefs_ != nullptr);
1387f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1397f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // If pre-verification failed, we are not interested in the current
1407f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // certificate. We store a report to UMA and just propagate the fail result.
1417f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  if (!preverify_ok) {
14233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed);
1437f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    return false;
1447f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  }
1457f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1467f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  int depth;
1477f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  unsigned int digest_length;
148f68bbbc952aa9a71898e4939b5f36187fa564a50Alex Vakulenko  uint8_t digest[EVP_MAX_MD_SIZE];
1497f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1507f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  if (!openssl_wrapper_->GetCertificateDigest(x509_ctx,
1517f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                              &depth,
1527f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                              &digest_length,
1537f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                                              digest)) {
1547f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    LOG(WARNING) << "Failed to generate digest of X509 certificate "
1557f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha                 << "from update server.";
15633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
1577f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    return true;
1587f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  }
1597f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1607f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // We convert the raw bytes of the digest to an hex string, for storage in
1617f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // prefs.
1627f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  string digest_string = base::HexEncode(digest, digest_length);
1637f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
164c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo  string storage_key =
165c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo      base::StringPrintf("%s-%d-%d", kPrefsUpdateServerCertificate,
16633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                         static_cast<int>(server_to_check), depth);
1677f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  string stored_digest;
1687f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // If there's no stored certificate, we just store the current one and return.
169c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo  if (!prefs_->GetString(storage_key, &stored_digest)) {
170c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    if (!prefs_->SetString(storage_key, digest_string)) {
171c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo      LOG(WARNING) << "Failed to store server certificate on storage key "
172c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo                   << storage_key;
173c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    }
17433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
1757f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha    return true;
1767f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  }
1777f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
1787f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // Certificate changed, we store a report to UMA and store the most recent
1797f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // certificate.
1807f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  if (stored_digest != digest_string) {
181c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    if (!prefs_->SetString(storage_key, digest_string)) {
182c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo      LOG(WARNING) << "Failed to store server certificate on storage key "
183c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo                   << storage_key;
184c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    }
18533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    LOG(INFO) << "Certificate changed from " << stored_digest << " to "
18633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo              << digest_string << ".";
18733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    NotifyCertificateChecked(server_to_check,
18833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo                             CertificateCheckResult::kValidChanged);
189c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    return true;
1907f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  }
1917f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
19233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo  NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
1937f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  // Since we don't perform actual SSL verification, we return success.
1947f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha  return true;
1957f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}
1967f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
197c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymovoid CertificateChecker::NotifyCertificateChecked(
19833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    ServerToCheck server_to_check,
199c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo    CertificateCheckResult result) {
200c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo  if (observer_)
20133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo    observer_->CertificateChecked(server_to_check, result);
2027f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}
2037f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha
2047f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha}  // namespace chromeos_update_engine
205