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