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