Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
initializer.cc
1 #include "initializer.h"
2 
3 #include <string>
4 
5 #include <openssl/bio.h>
6 #include <boost/scoped_array.hpp>
7 
8 #include "bootstrap/bootstrap.h"
9 #include "crypto/keymanager.h"
10 #include "logging/logging.h"
11 #include "storage/invstorage.h"
12 
13 // Postcondition: device_id is in the storage
14 bool Initializer::initDeviceId() {
15  // if device_id is already stored, just return
16  std::string device_id;
17  if (storage_->loadDeviceId(&device_id)) {
18  return true;
19  }
20 
21  // if device_id is specified in config, just use it, otherwise generate a random one
22  device_id = config_.device_id;
23  if (device_id.empty()) {
24  if (config_.mode == ProvisionMode::kSharedCred) {
25  device_id = Utils::genPrettyName();
26  } else if (config_.mode == ProvisionMode::kDeviceCred) {
27  device_id = keys_.getCN();
28  } else {
29  LOG_ERROR << "Unknown provisioning method";
30  return false;
31  }
32  }
33 
34  storage_->storeDeviceId(device_id);
35  return true;
36 }
37 void Initializer::resetDeviceId() { storage_->clearDeviceId(); }
38 
39 // Postcondition [(serial, hw_id)] is in the storage
40 bool Initializer::initEcuSerials() {
41  EcuSerials ecu_serials;
42 
43  // TODO: the assumption now is that the set of connected ECUs doesn't change, but it might obviously
44  // not be the case. ECU discovery seems to be a big story and should be worked on accordingly.
45  if (storage_->loadEcuSerials(&ecu_serials)) {
46  return true;
47  }
48 
49  std::string primary_ecu_serial_local = config_.primary_ecu_serial;
50  if (primary_ecu_serial_local.empty()) {
51  primary_ecu_serial_local = keys_.UptanePublicKey().KeyId();
52  }
53 
54  std::string primary_ecu_hardware_id = config_.primary_ecu_hardware_id;
55  if (primary_ecu_hardware_id.empty()) {
56  primary_ecu_hardware_id = Utils::getHostname();
57  if (primary_ecu_hardware_id == "") {
58  return false;
59  }
60  }
61 
62  ecu_serials.emplace_back(Uptane::EcuSerial(primary_ecu_serial_local),
63  Uptane::HardwareIdentifier(primary_ecu_hardware_id));
64 
65  for (const auto& s : secondaries_) {
66  ecu_serials.emplace_back(s.first, s.second->getHwId());
67  }
68 
69  storage_->storeEcuSerials(ecu_serials);
70  return true;
71 }
72 
73 void Initializer::resetEcuSerials() { storage_->clearEcuSerials(); }
74 
75 // Postcondition: (public, private) is in the storage. It should not be stored until secondaries are provisioned
76 bool Initializer::initPrimaryEcuKeys() { return keys_.generateUptaneKeyPair().size() != 0u; }
77 
78 void Initializer::resetEcuKeys() { storage_->clearPrimaryKeys(); }
79 
80 bool Initializer::loadSetTlsCreds() {
81  keys_.copyCertsToCurl(*http_client_);
82  return keys_.isOk();
83 }
84 
85 // Postcondition: TLS credentials are in the storage
86 InitRetCode Initializer::initTlsCreds() {
87  if (loadSetTlsCreds()) {
88  return InitRetCode::kOk;
89  }
90 
91  if (config_.mode != ProvisionMode::kSharedCred) {
92  LOG_ERROR << "Credentials not found";
93  return InitRetCode::kStorageFailure;
94  }
95 
96  // Shared credential provision is required and possible => (automatically)
97  // provision with shared credentials.
98 
99  // set bootstrap credentials
100  Bootstrap boot(config_.provision_path, config_.p12_password);
101  http_client_->setCerts(boot.getCa(), CryptoSource::kFile, boot.getCert(), CryptoSource::kFile, boot.getPkey(),
102  CryptoSource::kFile);
103 
104  Json::Value data;
105  std::string device_id;
106  if (!storage_->loadDeviceId(&device_id)) {
107  LOG_ERROR << "Unknown device_id during shared credential provisioning.";
108  return InitRetCode::kStorageFailure;
109  }
110  data["deviceId"] = device_id;
111  data["ttl"] = config_.expiry_days;
112  HttpResponse response = http_client_->post(config_.server + "/devices", data);
113  if (!response.isOk()) {
114  Json::Value resp_code = response.getJson()["code"];
115  if (resp_code.isString() && resp_code.asString() == "device_already_registered") {
116  LOG_ERROR << "Device id" << device_id << "is occupied";
117  return InitRetCode::kOccupied;
118  }
119  LOG_ERROR << "Shared credential provisioning failed, response: " << response.body;
120  return InitRetCode::kServerFailure;
121  }
122 
123  std::string pkey;
124  std::string cert;
125  std::string ca;
126  StructGuard<BIO> device_p12(BIO_new_mem_buf(response.body.c_str(), static_cast<int>(response.body.size())),
127  BIO_vfree);
128  if (!Crypto::parseP12(device_p12.get(), "", &pkey, &cert, &ca)) {
129  LOG_ERROR << "Received a malformed P12 package from the server";
130  return InitRetCode::kBadP12;
131  }
132  storage_->storeTlsCreds(ca, cert, pkey);
133 
134  // set provisioned credentials
135  if (!loadSetTlsCreds()) {
136  LOG_ERROR << "Failed to set provisioned credentials";
137  return InitRetCode::kStorageFailure;
138  }
139 
140  LOG_INFO << "Provisioned successfully on Device Gateway";
141  return InitRetCode::kOk;
142 }
143 
144 void Initializer::resetTlsCreds() {
145  if (config_.mode != ProvisionMode::kDeviceCred) {
146  storage_->clearTlsCreds();
147  }
148 }
149 
150 // Postcondition: "ECUs registered" flag set in the storage
151 InitRetCode Initializer::initEcuRegister() {
152  if (storage_->loadEcuRegistered()) {
153  return InitRetCode::kOk;
154  }
155 
156  PublicKey uptane_public_key = keys_.UptanePublicKey();
157 
158  if (uptane_public_key.Type() == KeyType::kUnknown) {
159  return InitRetCode::kStorageFailure;
160  }
161 
162  EcuSerials ecu_serials;
163  // initEcuSerials should have been called by this point
164  if (!storage_->loadEcuSerials(&ecu_serials) || ecu_serials.size() < 1) {
165  return InitRetCode::kStorageFailure;
166  }
167 
168  Json::Value all_ecus;
169  all_ecus["primary_ecu_serial"] = ecu_serials[0].first.ToString();
170  all_ecus["ecus"] = Json::arrayValue;
171  {
172  Json::Value primary_ecu;
173  primary_ecu["hardware_identifier"] = ecu_serials[0].second.ToString();
174  primary_ecu["ecu_serial"] = ecu_serials[0].first.ToString();
175  primary_ecu["clientKey"] = keys_.UptanePublicKey().ToUptane();
176  all_ecus["ecus"].append(primary_ecu);
177  }
178 
179  for (const auto& sec : secondaries_) {
180  const Uptane::EcuSerial ecu_serial = sec.first;
181  Uptane::SecondaryInterface& itf = *sec.second;
182 
183  const Uptane::HardwareIdentifier hw_id = itf.getHwId();
184  const PublicKey pub_key = itf.getPublicKey();
185  storage_->saveSecondaryInfo(ecu_serial, itf.Type(), pub_key);
186 
187  Json::Value ecu;
188  ecu["hardware_identifier"] = hw_id.ToString();
189  ecu["ecu_serial"] = ecu_serial.ToString();
190  ecu["clientKey"] = pub_key.ToUptane();
191  all_ecus["ecus"].append(ecu);
192  }
193 
194  HttpResponse response = http_client_->post(config_.ecu_registration_endpoint, all_ecus);
195  if (!response.isOk()) {
196  Json::Value resp_code = response.getJson()["code"];
197  if (resp_code.isString() &&
198  (resp_code.asString() == "ecu_already_registered" || resp_code.asString() == "device_already_registered")) {
199  LOG_ERROR << "One or more ECUs are unexpectedly already registered.";
200  return InitRetCode::kOccupied;
201  }
202  LOG_ERROR << "Error registering device on Uptane, response: " << response.body;
203  return InitRetCode::kServerFailure;
204  }
205  // do not call storage_->storeEcuRegistered(), it will be called from the top-level Init function after the
206  // acknowledgement
207  LOG_INFO << "ECUs have been successfully registered to the server.";
208  return InitRetCode::kOk;
209 }
210 
211 bool Initializer::initEcuReportCounter() {
212  std::vector<std::pair<Uptane::EcuSerial, int64_t>> ecu_cnt;
213 
214  if (storage_->loadEcuReportCounter(&ecu_cnt)) {
215  return true;
216  }
217 
218  EcuSerials ecu_serials;
219 
220  if (!storage_->loadEcuSerials(&ecu_serials) || (ecu_serials.size() == 0)) {
221  return false;
222  }
223 
224  storage_->saveEcuReportCounter(Uptane::EcuSerial(ecu_serials[0].first.ToString()), 0);
225 
226  return true;
227 }
228 // Postcondition: "ECUs registered" flag set in the storage
229 Initializer::Initializer(const ProvisionConfig& config_in, std::shared_ptr<INvStorage> storage_in,
230  std::shared_ptr<HttpInterface> http_client_in, KeyManager& keys_in,
231  const std::map<Uptane::EcuSerial, std::shared_ptr<Uptane::SecondaryInterface>>& secondaries_in)
232  : config_(config_in),
233  storage_(std::move(storage_in)),
234  http_client_(std::move(http_client_in)),
235  keys_(keys_in),
236  secondaries_(secondaries_in) {
237  success_ = false;
238  for (int i = 0; i < MaxInitializationAttempts; i++) {
239  if (!initDeviceId()) {
240  LOG_ERROR << "Device ID generation failed. Aborting initialization.";
241  return;
242  }
243 
244  InitRetCode ret_code = initTlsCreds();
245  // if a device with the same ID has already been registered to the server,
246  // generate a new one
247  if (ret_code == InitRetCode::kOccupied) {
248  resetDeviceId();
249  LOG_INFO << "Device name is already registered. Restarting.";
250  continue;
251  } else if (ret_code == InitRetCode::kStorageFailure) {
252  LOG_ERROR << "Error reading existing provisioning data from storage.";
253  return;
254  } else if (ret_code != InitRetCode::kOk) {
255  LOG_ERROR << "Shared credential provisioning failed. Aborting initialization.";
256  return;
257  }
258 
259  if (!initPrimaryEcuKeys()) {
260  LOG_ERROR << "ECU key generation failed. Aborting initialization.";
261  return;
262  }
263  if (!initEcuSerials()) {
264  LOG_ERROR << "ECU serial generation failed. Aborting initialization.";
265  return;
266  }
267  if (!initEcuReportCounter()) {
268  LOG_ERROR << "ECU report counter init failed. Aborting initialization.";
269  return;
270  }
271 
272  ret_code = initEcuRegister();
273  // if ECUs with same ID have been registered to the server, we don't have a
274  // clear remediation path right now, just ignore the error
275  if (ret_code == InitRetCode::kOccupied) {
276  LOG_INFO << "ECU serial is already registered.";
277  } else if (ret_code != InitRetCode::kOk) {
278  LOG_ERROR << "ECU registration failed. Aborting initialization.";
279  return;
280  }
281 
282  // TODO: acknowledge on server _before_ setting the flag
283  storage_->storeEcuRegistered();
284  success_ = true;
285  return;
286  }
287  LOG_ERROR << "Initialization failed after " << MaxInitializationAttempts << " attempts.";
288 }
ProvisionConfig
Definition: config.h:38
KeyManager
Definition: keymanager.h:13
PublicKey::ToUptane
Json::Value ToUptane() const
Uptane Json representation of this public key.
Definition: crypto.cc:74
data
General data structures.
Definition: types.cc:54
Uptane::HardwareIdentifier
Definition: tuf.h:143
HttpResponse
Definition: httpinterface.h:17
Uptane::EcuSerial
Definition: tuf.h:174
PublicKey
Definition: crypto.h:26
Bootstrap
Definition: bootstrap.h:7
Uptane::SecondaryInterface
Definition: secondaryinterface.h:12