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 */
CalculatePasswordDigest(const nugget_app_password & 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 */
DeriveCitadelUpdatePassword(const hidl_vec<uint8_t> & secret)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 */
TryEnrollCitadelUpdatePassword(NuggetClientInterface & client,const nugget_app_password & password)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. */
RebootCitadel(NuggetClientInterface & client)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 */
TryEnablingCitadelUpdate(NuggetClientInterface & client,const nugget_app_password & password)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.
primaryUserCredential(const hidl_vec<uint8_t> & secret)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