Home | History | Annotate | Download | only in authsecret
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "AuthSecret.h"
     18 
     19 #include <android-base/logging.h>
     20 
     21 #include <openssl/sha.h>
     22 
     23 #include <application.h>
     24 #include <app_nugget.h>
     25 #include <nos/debug.h>
     26 
     27 #include <AuthSecret.h>
     28 
     29 namespace android {
     30 namespace hardware {
     31 namespace authsecret {
     32 
     33 // libhidl
     34 using ::android::hardware::Void;
     35 
     36 namespace {
     37 
     38 /** The digest is the first 4 bytes of the sha1 of the password */
     39 uint32_t CalculatePasswordDigest(const nugget_app_password& password) {
     40     uint8_t passwordSha1[SHA_DIGEST_LENGTH];
     41     SHA_CTX c;
     42     SHA1_Init(&c);
     43     SHA1_Update(&c, &password.password, NUGGET_UPDATE_PASSWORD_LEN);
     44     SHA1_Final(passwordSha1, &c);
     45 
     46     uint32_t digest;
     47     memcpy(&digest, passwordSha1, sizeof(digest));
     48     return digest;
     49 }
     50 
     51 /*
     52  * Derive the update password from the secret:
     53  *     update_password = sha256( CITADEL_UPDATE_KEY | 0 | secret )
     54  *
     55  * Password must point to NUGGET_UPDATE_PASSWORD_LEN bytes.
     56  *
     57  * The password is derived in case the secret needs to be used for another
     58  * purpose. Deriving different values for each subsystem adds a layer of
     59  * security.
     60  */
     61 nugget_app_password DeriveCitadelUpdatePassword(const hidl_vec<uint8_t>& secret) {
     62     nugget_app_password password;
     63 
     64     static_assert(SHA256_DIGEST_LENGTH == NUGGET_UPDATE_PASSWORD_LEN,
     65                   "Hash output does not match update password length");
     66     const uint8_t CITADEL_UPDATE_KEY[] = "citadel_update";
     67     SHA256_CTX c;
     68     SHA256_Init(&c);
     69     SHA256_Update(&c, CITADEL_UPDATE_KEY, sizeof(CITADEL_UPDATE_KEY));
     70     SHA256_Update(&c, secret.data(), secret.size());
     71     SHA256_Final(password.password, &c);
     72     password.digest = CalculatePasswordDigest(password);
     73     return password;
     74 }
     75 
     76 /**
     77  * The first time this is called, Citadel won't have its update password set so
     78  * always try and enroll it. If the update password is already set, this will
     79  * faily gracefully.
     80  */
     81 void TryEnrollCitadelUpdatePassword(NuggetClientInterface& client,
     82                                     const nugget_app_password& password) {
     83     std::vector<uint8_t> buffer(sizeof(nugget_app_change_update_password));
     84     nugget_app_change_update_password* const msg
     85             = reinterpret_cast<nugget_app_change_update_password*>(buffer.data());
     86 
     87     msg->new_password = password;
     88     // Default password is uninitialized flash i.e. all 0xff
     89     memset(&msg->old_password.password, 0xff, NUGGET_UPDATE_PASSWORD_LEN);
     90     msg->old_password.digest = 0xffffffff;
     91 
     92     const uint32_t appStatus = client.CallApp(APP_ID_NUGGET,
     93                                               NUGGET_PARAM_CHANGE_UPDATE_PASSWORD,
     94                                               buffer, nullptr);
     95     if (appStatus == APP_ERROR_BOGUS_ARGS) {
     96         LOG(VERBOSE) << "Citadel update password already installed";
     97         return;
     98     }
     99     if (appStatus != APP_SUCCESS) {
    100         LOG(ERROR) << "Citadel change update password failed: "
    101                    << ::nos::StatusCodeString(appStatus) << " (" << appStatus << ")";
    102         return;
    103     }
    104 
    105     LOG(INFO) << "Citadel update password installed";
    106 }
    107 
    108 /** Request a hard reboot of Citadel. */
    109 void RebootCitadel(NuggetClientInterface& client) {
    110     std::vector<uint8_t> ignored = {1};         // older images require this
    111     const uint32_t appStatus = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_REBOOT, ignored, nullptr);
    112     if (appStatus != APP_SUCCESS) {
    113         LOG(ERROR) << "Citadel failed to reboot: " << ::nos::StatusCodeString(appStatus)
    114                    << " (" << appStatus << ")";
    115     }
    116 }
    117 
    118 /**
    119  * Try to enable a Citadel update with the update password. If this fails,
    120  * something has gone wrong somewhere...
    121  */
    122 void TryEnablingCitadelUpdate(NuggetClientInterface& client, const nugget_app_password& password) {
    123     std::vector<uint8_t> buffer(sizeof(nugget_app_enable_update));
    124     nugget_app_enable_update* const msg
    125             = reinterpret_cast<nugget_app_enable_update*>(buffer.data());
    126 
    127     msg->password = password;
    128     msg->which_headers = NUGGET_ENABLE_HEADER_RW | NUGGET_ENABLE_HEADER_RO;
    129     const uint32_t appStatus = client.CallApp(APP_ID_NUGGET,
    130                                               NUGGET_PARAM_ENABLE_UPDATE,
    131                                               buffer, &buffer);
    132     if (appStatus == APP_ERROR_BOGUS_ARGS) {
    133         LOG(ERROR) << "Incorrect Citadel update password";
    134         return;
    135     }
    136     if (appStatus != APP_SUCCESS) {
    137         LOG(ERROR) << "Citadel enable update failed: " << ::nos::StatusCodeString(appStatus)
    138                    << " (" << appStatus << ")";
    139         return;
    140     }
    141 
    142     // If the header[s] changed, reboot for the update to take effect
    143     // Old firmware doesn't have a reply but still needs to be updated
    144     if (buffer.empty() || buffer[0]) {
    145       LOG(INFO) << "Update password enabled a new image; rebooting Citadel";
    146       RebootCitadel(client);
    147     }
    148 }
    149 
    150 } // namespace
    151 
    152 // Methods from ::android::hardware::authsecret::V1_0::IAuthSecret follow.
    153 Return<void> AuthSecret::primaryUserCredential(const hidl_vec<uint8_t>& secret) {
    154     LOG(VERBOSE) << "Running AuthSecret::primaryUserCredential";
    155 
    156     if (secret.size() < 16) {
    157         LOG(WARNING) << "The secret is shorter than 16 bytes";
    158     }
    159 
    160     const nugget_app_password password = DeriveCitadelUpdatePassword(secret);
    161     TryEnrollCitadelUpdatePassword(_client, password);
    162     TryEnablingCitadelUpdate(_client, password);
    163 
    164     return Void();
    165 }
    166 
    167 } // namespace authsecret
    168 } // namespace hardware
    169 } // namespace android
    170