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 void Initializer::initDeviceId() {
15  // if device_id is already stored, just return
16  std::string device_id;
17  if (storage_->loadDeviceId(&device_id)) {
18  return;
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  throw Error("Unknown provisioning method");
30  }
31  }
32 
33  storage_->storeDeviceId(device_id);
34 }
35 
36 void Initializer::resetDeviceId() { storage_->clearDeviceId(); }
37 
38 // Postcondition [(serial, hw_id)] is in the storage
39 void Initializer::initEcuSerials() {
40  EcuSerials ecu_serials;
41 
42  // TODO: the assumption now is that the set of connected ECUs doesn't change, but it might obviously
43  // not be the case. ECU discovery seems to be a big story and should be worked on accordingly.
44  if (storage_->loadEcuSerials(&ecu_serials)) {
45  return;
46  }
47 
48  std::string primary_ecu_serial_local = config_.primary_ecu_serial;
49  if (primary_ecu_serial_local.empty()) {
50  primary_ecu_serial_local = keys_.UptanePublicKey().KeyId();
51  }
52 
53  std::string primary_ecu_hardware_id = config_.primary_ecu_hardware_id;
54  if (primary_ecu_hardware_id.empty()) {
55  primary_ecu_hardware_id = Utils::getHostname();
56  if (primary_ecu_hardware_id == "") {
57  throw Error("Could not get current host name, please configure an hardware ID explicitely");
58  }
59  }
60 
61  ecu_serials.emplace_back(Uptane::EcuSerial(primary_ecu_serial_local),
62  Uptane::HardwareIdentifier(primary_ecu_hardware_id));
63 
64  for (const auto& s : secondaries_) {
65  ecu_serials.emplace_back(s.first, s.second->getHwId());
66  }
67 
68  storage_->storeEcuSerials(ecu_serials);
69 }
70 
71 void Initializer::resetEcuSerials() { storage_->clearEcuSerials(); }
72 
73 // Postcondition: (public, private) is in the storage. It should not be stored until secondaries are provisioned
74 void Initializer::initPrimaryEcuKeys() {
75  std::string key_pair;
76  try {
77  key_pair = keys_.generateUptaneKeyPair();
78  } catch (const std::exception& e) {
79  throw KeyGenerationError(e.what());
80  }
81 
82  if (key_pair.size() == 0U) {
83  throw KeyGenerationError("Unknow error");
84  }
85 }
86 
87 void Initializer::resetEcuKeys() { storage_->clearPrimaryKeys(); }
88 
89 bool Initializer::loadSetTlsCreds() {
90  keys_.copyCertsToCurl(*http_client_);
91  return keys_.isOk();
92 }
93 
94 // Postcondition: TLS credentials are in the storage
95 void Initializer::initTlsCreds() {
96  if (loadSetTlsCreds()) {
97  return;
98  }
99 
100  if (config_.mode != ProvisionMode::kSharedCred) {
101  throw StorageError("Shared credentials expected but not found");
102  }
103 
104  // Shared credential provision is required and possible => (automatically)
105  // provision with shared credentials.
106 
107  // set bootstrap credentials
108  Bootstrap boot(config_.provision_path, config_.p12_password);
109  http_client_->setCerts(boot.getCa(), CryptoSource::kFile, boot.getCert(), CryptoSource::kFile, boot.getPkey(),
110  CryptoSource::kFile);
111 
112  Json::Value data;
113  std::string device_id;
114  if (!storage_->loadDeviceId(&device_id)) {
115  throw StorageError("Unable to load device_id during shared credential provisioning");
116  }
117  data["deviceId"] = device_id;
118  data["ttl"] = config_.expiry_days;
119  HttpResponse response = http_client_->post(config_.server + "/devices", data);
120  if (!response.isOk()) {
121  Json::Value resp_code = response.getJson()["code"];
122  if (resp_code.isString() && resp_code.asString() == "device_already_registered") {
123  LOG_ERROR << "Device ID " << device_id << " is already registered.";
124  throw ServerOccupied();
125  }
126  const auto err = std::string("Shared credential provisioning failed: ") +
127  std::to_string(response.http_status_code) + " " + response.body;
128  throw ServerError(err);
129  }
130 
131  std::string pkey;
132  std::string cert;
133  std::string ca;
134  StructGuard<BIO> device_p12(BIO_new_mem_buf(response.body.c_str(), static_cast<int>(response.body.size())),
135  BIO_vfree);
136  if (!Crypto::parseP12(device_p12.get(), "", &pkey, &cert, &ca)) {
137  throw ServerError("Received malformed device credentials from the server");
138  }
139  storage_->storeTlsCreds(ca, cert, pkey);
140 
141  // set provisioned credentials
142  if (!loadSetTlsCreds()) {
143  throw Error("Failed to configure HTTP client with device credentials.");
144  }
145 
146  LOG_INFO << "Provisioned successfully on Device Gateway.";
147 }
148 
149 void Initializer::resetTlsCreds() {
150  if (config_.mode != ProvisionMode::kDeviceCred) {
151  storage_->clearTlsCreds();
152  }
153 }
154 
155 // Postcondition: "ECUs registered" flag set in the storage
156 void Initializer::initEcuRegister() {
157  if (storage_->loadEcuRegistered()) {
158  return;
159  }
160 
161  PublicKey uptane_public_key = keys_.UptanePublicKey();
162 
163  if (uptane_public_key.Type() == KeyType::kUnknown) {
164  throw StorageError("Invalid key in storage");
165  }
166 
167  EcuSerials ecu_serials;
168  // initEcuSerials should have been called by this point
169  if (!storage_->loadEcuSerials(&ecu_serials) || ecu_serials.size() < 1) {
170  throw StorageError("Could not load ECUs from storage");
171  }
172 
173  Json::Value all_ecus;
174  all_ecus["primary_ecu_serial"] = ecu_serials[0].first.ToString();
175  all_ecus["ecus"] = Json::arrayValue;
176  {
177  Json::Value primary_ecu;
178  primary_ecu["hardware_identifier"] = ecu_serials[0].second.ToString();
179  primary_ecu["ecu_serial"] = ecu_serials[0].first.ToString();
180  primary_ecu["clientKey"] = keys_.UptanePublicKey().ToUptane();
181  all_ecus["ecus"].append(primary_ecu);
182  }
183 
184  for (const auto& sec : secondaries_) {
185  const Uptane::EcuSerial ecu_serial = sec.first;
186  Uptane::SecondaryInterface& itf = *sec.second;
187 
188  const Uptane::HardwareIdentifier hw_id = itf.getHwId();
189  const PublicKey pub_key = itf.getPublicKey();
190  storage_->saveSecondaryInfo(ecu_serial, itf.Type(), pub_key);
191 
192  Json::Value ecu;
193  ecu["hardware_identifier"] = hw_id.ToString();
194  ecu["ecu_serial"] = ecu_serial.ToString();
195  ecu["clientKey"] = pub_key.ToUptane();
196  all_ecus["ecus"].append(ecu);
197  }
198 
199  HttpResponse response = http_client_->post(config_.ecu_registration_endpoint, all_ecus);
200  if (!response.isOk()) {
201  Json::Value resp_code = response.getJson()["code"];
202  if (resp_code.isString() &&
203  (resp_code.asString() == "ecu_already_registered" || resp_code.asString() == "device_already_registered")) {
204  throw ServerError("One or more ECUs are unexpectedly already registered");
205  }
206  const auto err =
207  std::string("Error registering device: ") + std::to_string(response.http_status_code) + " " + response.body;
208  throw ServerError(err);
209  }
210 
211  LOG_INFO << "ECUs have been successfully registered with the server.";
212 }
213 
214 void Initializer::initEcuReportCounter() {
215  std::vector<std::pair<Uptane::EcuSerial, int64_t>> ecu_cnt;
216 
217  if (storage_->loadEcuReportCounter(&ecu_cnt)) {
218  return;
219  }
220 
221  EcuSerials ecu_serials;
222 
223  if (!storage_->loadEcuSerials(&ecu_serials) || (ecu_serials.size() == 0)) {
224  throw Error("Could not load ECU serials");
225  }
226 
227  storage_->saveEcuReportCounter(Uptane::EcuSerial(ecu_serials[0].first.ToString()), 0);
228 }
229 
230 // Postcondition: "ECUs registered" flag set in the storage
231 Initializer::Initializer(const ProvisionConfig& config_in, std::shared_ptr<INvStorage> storage_in,
232  std::shared_ptr<HttpInterface> http_client_in, KeyManager& keys_in,
233  const std::map<Uptane::EcuSerial, std::shared_ptr<Uptane::SecondaryInterface>>& secondaries_in)
234  : config_(config_in),
235  storage_(std::move(storage_in)),
236  http_client_(std::move(http_client_in)),
237  keys_(keys_in),
238  secondaries_(secondaries_in) {
239  for (int i = 0; i < MaxInitializationAttempts; i++) {
240  initDeviceId();
241 
242  try {
243  initTlsCreds();
244  } catch (const ServerOccupied& e) {
245  // if a device with the same ID has already been registered to the server,
246  // generate a new one
247  resetDeviceId();
248  LOG_ERROR << "Device name is already registered. Retrying.";
249  continue;
250  }
251 
252  initPrimaryEcuKeys();
253 
254  initEcuSerials();
255 
256  initEcuReportCounter();
257 
258  initEcuRegister();
259 
260  storage_->storeEcuRegistered();
261 
262  return;
263  }
264 
265  throw Error(std::string("Initialization failed after ") + std::to_string(MaxInitializationAttempts) + " attempts");
266 }
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:55
Uptane::HardwareIdentifier
Definition: tuf.h:146
HttpResponse
Definition: httpinterface.h:17
Uptane::EcuSerial
Definition: tuf.h:177
PublicKey
Definition: crypto.h:26
Bootstrap
Definition: bootstrap.h:7
Uptane::SecondaryInterface
Definition: secondaryinterface.h:12