Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
keymanager.cc
1 #include "keymanager.h"
2 
3 #include <stdexcept>
4 #include <utility>
5 
6 #include <boost/scoped_array.hpp>
7 
8 #include "crypto/openssl_compat.h"
9 #include "libaktualizr/types.h"
10 #include "storage/invstorage.h"
11 
12 #if defined(ANDROID)
13 #include "androidkeystore.h"
14 #endif
15 
16 // by using constexpr the compiler can optimize out method calls when the
17 // feature is disabled. We won't then need to link with the actual p11 engine
18 // implementation
19 #ifdef BUILD_P11
20 static constexpr bool built_with_p11 = true;
21 #else
22 static constexpr bool built_with_p11 = false;
23 #endif
24 
25 KeyManager::KeyManager(std::shared_ptr<INvStorage> backend, KeyManagerConfig config)
26  : backend_(std::move(backend)), config_(std::move(config)) {
27  if (built_with_p11) {
28  p11_ = std_::make_unique<P11EngineGuard>(config_.p11);
29  }
30 }
31 
32 void KeyManager::loadKeys(const std::string *pkey_content, const std::string *cert_content,
33  const std::string *ca_content) {
34  if (config_.tls_pkey_source == CryptoSource::kFile || config_.tls_pkey_source == CryptoSource::kAndroid) {
35  std::string pkey;
36  if (pkey_content != nullptr) {
37  pkey = *pkey_content;
38  } else {
39  backend_->loadTlsPkey(&pkey);
40  }
41  if (!pkey.empty()) {
42  if (tmp_pkey_file == nullptr) {
43  tmp_pkey_file = std_::make_unique<TemporaryFile>("tls-pkey");
44  }
45  tmp_pkey_file->PutContents(pkey);
46  }
47  }
48  if (config_.tls_cert_source == CryptoSource::kFile || config_.tls_cert_source == CryptoSource::kAndroid) {
49  std::string cert;
50  if (cert_content != nullptr) {
51  cert = *cert_content;
52  } else {
53  backend_->loadTlsCert(&cert);
54  }
55  if (!cert.empty()) {
56  if (tmp_cert_file == nullptr) {
57  tmp_cert_file = std_::make_unique<TemporaryFile>("tls-cert");
58  }
59  tmp_cert_file->PutContents(cert);
60  }
61  }
62  if (config_.tls_ca_source == CryptoSource::kFile || config_.tls_ca_source == CryptoSource::kAndroid) {
63  std::string ca;
64  if (ca_content != nullptr) {
65  ca = *ca_content;
66  } else {
67  backend_->loadTlsCa(&ca);
68  }
69  if (!ca.empty()) {
70  if (tmp_ca_file == nullptr) {
71  tmp_ca_file = std_::make_unique<TemporaryFile>("tls-ca");
72  }
73  tmp_ca_file->PutContents(ca);
74  }
75  }
76 }
77 
78 std::string KeyManager::getPkeyFile() const {
79  std::string pkey_file;
80  if (config_.tls_pkey_source == CryptoSource::kPkcs11) {
81  if (!built_with_p11) {
82  throw std::runtime_error("Aktualizr was built without PKCS#11");
83  }
84  pkey_file = (*p11_)->getTlsPkeyId();
85  }
86  if (config_.tls_pkey_source == CryptoSource::kFile || config_.tls_pkey_source == CryptoSource::kAndroid) {
87  if (tmp_pkey_file && !boost::filesystem::is_empty(tmp_pkey_file->PathString())) {
88  pkey_file = tmp_pkey_file->PathString();
89  }
90  }
91  return pkey_file;
92 }
93 
94 std::string KeyManager::getCertFile() const {
95  std::string cert_file;
96  if (config_.tls_cert_source == CryptoSource::kPkcs11) {
97  if (!built_with_p11) {
98  throw std::runtime_error("Aktualizr was built without PKCS#11");
99  }
100  cert_file = (*p11_)->getTlsCertId();
101  }
102  if (config_.tls_cert_source == CryptoSource::kFile || config_.tls_cert_source == CryptoSource::kAndroid) {
103  if (tmp_cert_file && !boost::filesystem::is_empty(tmp_cert_file->PathString())) {
104  cert_file = tmp_cert_file->PathString();
105  }
106  }
107  return cert_file;
108 }
109 
110 std::string KeyManager::getCaFile() const {
111  std::string ca_file;
112  if (config_.tls_ca_source == CryptoSource::kPkcs11) {
113  if (!built_with_p11) {
114  throw std::runtime_error("Aktualizr was built without PKCS#11");
115  }
116  ca_file = (*p11_)->getTlsCacertId();
117  }
118  if (config_.tls_ca_source == CryptoSource::kFile || config_.tls_ca_source == CryptoSource::kAndroid) {
119  if (tmp_ca_file && !boost::filesystem::is_empty(tmp_ca_file->PathString())) {
120  ca_file = tmp_ca_file->PathString();
121  }
122  }
123  return ca_file;
124 }
125 
126 std::string KeyManager::getPkey() const {
127  std::string pkey;
128  if (config_.tls_pkey_source == CryptoSource::kPkcs11) {
129  if (!built_with_p11) {
130  throw std::runtime_error("Aktualizr was built without PKCS#11");
131  }
132  pkey = (*p11_)->getTlsPkeyId();
133  }
134  if (config_.tls_pkey_source == CryptoSource::kFile || config_.tls_pkey_source == CryptoSource::kAndroid) {
135  backend_->loadTlsPkey(&pkey);
136  }
137  return pkey;
138 }
139 
140 std::string KeyManager::getCert() const {
141  std::string cert;
142  if (config_.tls_cert_source == CryptoSource::kPkcs11) {
143  if (!built_with_p11) {
144  throw std::runtime_error("Aktualizr was built without PKCS#11");
145  }
146  cert = (*p11_)->getTlsCertId();
147  }
148  if (config_.tls_cert_source == CryptoSource::kFile || config_.tls_cert_source == CryptoSource::kAndroid) {
149  backend_->loadTlsCert(&cert);
150  }
151  return cert;
152 }
153 
154 std::string KeyManager::getCa() const {
155  std::string ca;
156  if (config_.tls_ca_source == CryptoSource::kPkcs11) {
157  if (!built_with_p11) {
158  throw std::runtime_error("Aktualizr was built without PKCS#11");
159  }
160  ca = (*p11_)->getTlsCacertId();
161  }
162  if (config_.tls_ca_source == CryptoSource::kFile || config_.tls_ca_source == CryptoSource::kAndroid) {
163  backend_->loadTlsCa(&ca);
164  }
165  return ca;
166 }
167 
168 std::string KeyManager::getCN() const {
169  std::string not_found_cert_message = "Certificate is not found, can't extract device_id";
170  std::string cert;
171  if (config_.tls_cert_source == CryptoSource::kFile || config_.tls_cert_source == CryptoSource::kAndroid) {
172  if (!backend_->loadTlsCert(&cert)) {
173  throw std::runtime_error(not_found_cert_message);
174  }
175  } else { // CryptoSource::kPkcs11
176  if (!built_with_p11) {
177  throw std::runtime_error("Aktualizr was built without PKCS#11 support, can't extract device_id");
178  }
179  if (!(*p11_)->readTlsCert(&cert)) {
180  throw std::runtime_error(not_found_cert_message);
181  }
182  }
183 
184  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(cert.c_str()), static_cast<int>(cert.size())), BIO_vfree);
185  StructGuard<X509> x(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free);
186  if (x == nullptr) {
187  throw std::runtime_error("Could not parse certificate");
188  }
189 
190  int len = X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, nullptr, 0);
191  if (len < 0) {
192  throw std::runtime_error("Could not get CN from certificate");
193  }
194  boost::scoped_array<char> buf(new char[len + 1]);
195  X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, buf.get(), len + 1);
196  std::string cn(buf.get());
197  return cn;
198 }
199 
200 void KeyManager::getCertInfo(std::string *subject, std::string *issuer, std::string *not_before,
201  std::string *not_after) const {
202  std::string not_found_cert_message = "Certificate is not found, can't extract device certificate";
203  std::string cert;
204  if (config_.tls_cert_source == CryptoSource::kFile || config_.tls_cert_source == CryptoSource::kAndroid) {
205  if (!backend_->loadTlsCert(&cert)) {
206  throw std::runtime_error(not_found_cert_message);
207  }
208  } else { // CryptoSource::kPkcs11
209  if (!built_with_p11) {
210  throw std::runtime_error("Aktualizr was built without PKCS#11 support, can't extract device certificate");
211  }
212  if (!(*p11_)->readTlsCert(&cert)) {
213  throw std::runtime_error(not_found_cert_message);
214  }
215  }
216 
217  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(cert.c_str()), static_cast<int>(cert.size())), BIO_vfree);
218  StructGuard<X509> x(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free);
219  if (x == nullptr) {
220  throw std::runtime_error("Could not parse certificate");
221  }
222 
223  StructGuard<BIO> subj_bio(BIO_new(BIO_s_mem()), BIO_vfree);
224  X509_NAME_print_ex(subj_bio.get(), X509_get_subject_name(x.get()), 1, 0);
225  char *subj_buf = nullptr;
226  auto subj_len = BIO_get_mem_data(subj_bio.get(), &subj_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
227  if (subj_buf == nullptr) {
228  throw std::runtime_error("Could not parse certificate subject");
229  }
230  *subject = std::string(subj_buf, static_cast<size_t>(subj_len));
231 
232  StructGuard<BIO> issuer_bio(BIO_new(BIO_s_mem()), BIO_vfree);
233  X509_NAME_print_ex(issuer_bio.get(), X509_get_issuer_name(x.get()), 1, 0);
234  char *issuer_buf = nullptr;
235  auto issuer_len = BIO_get_mem_data(issuer_bio.get(), &issuer_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
236  if (issuer_buf == nullptr) {
237  throw std::runtime_error("Could not parse certificate issuer");
238  }
239  *issuer = std::string(issuer_buf, static_cast<size_t>(issuer_len));
240 
241 #if AKTUALIZR_OPENSSL_PRE_11
242  const ASN1_TIME *nb_asn1 = X509_get_notBefore(x.get());
243 #else
244  const ASN1_TIME *nb_asn1 = X509_get0_notBefore(x.get());
245 #endif
246  StructGuard<BIO> nb_bio(BIO_new(BIO_s_mem()), BIO_vfree);
247  ASN1_TIME_print(nb_bio.get(), nb_asn1);
248  char *nb_buf;
249  auto nb_len = BIO_get_mem_data(nb_bio.get(), &nb_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
250  *not_before = std::string(nb_buf, static_cast<size_t>(nb_len));
251 
252 #if AKTUALIZR_OPENSSL_PRE_11
253  const ASN1_TIME *na_asn1 = X509_get_notAfter(x.get());
254 #else
255  const ASN1_TIME *na_asn1 = X509_get0_notAfter(x.get());
256 #endif
257  StructGuard<BIO> na_bio(BIO_new(BIO_s_mem()), BIO_vfree);
258  ASN1_TIME_print(na_bio.get(), na_asn1);
259  char *na_buf;
260  auto na_len = BIO_get_mem_data(na_bio.get(), &na_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
261  *not_after = std::string(na_buf, static_cast<size_t>(na_len));
262 }
263 
264 void KeyManager::copyCertsToCurl(HttpInterface &http) const {
265  std::string pkey = getPkey();
266  std::string cert = getCert();
267  std::string ca = getCa();
268 
269  if ((pkey.size() != 0U) && (cert.size() != 0U) && (ca.size() != 0U)) {
270  http.setCerts(ca, config_.tls_ca_source, cert, config_.tls_cert_source, pkey, config_.tls_pkey_source);
271  }
272 }
273 
274 Json::Value KeyManager::signTuf(const Json::Value &in_data) const {
275  ENGINE *crypto_engine = nullptr;
276  std::string private_key;
277  if (config_.uptane_key_source == CryptoSource::kPkcs11) {
278  if (!built_with_p11) {
279  throw std::runtime_error("Aktualizr was built without PKCS#11");
280  }
281  crypto_engine = (*p11_)->getEngine();
282  private_key = config_.p11.uptane_key_id;
283  }
284 
285  std::string b64sig;
286  if (config_.uptane_key_source == CryptoSource::kAndroid) {
287 #if defined(ANDROID)
288  b64sig = AndroidKeyStore::instance().signData(Utils::jsonToCanonicalStr(in_data));
289 #else
290  throw std::runtime_error("Aktualizr was built without Android support");
291 #endif
292  } else {
293  if (config_.uptane_key_source == CryptoSource::kFile) {
294  backend_->loadPrimaryPrivate(&private_key);
295  }
296  b64sig = Utils::toBase64(
297  Crypto::Sign(config_.uptane_key_type, crypto_engine, private_key, Utils::jsonToCanonicalStr(in_data)));
298  }
299 
300  Json::Value signature;
301  switch (config_.uptane_key_type) {
302  case KeyType::kRSA2048:
303  case KeyType::kRSA3072:
304  case KeyType::kRSA4096:
305  signature["method"] = "rsassa-pss";
306  break;
307  case KeyType::kED25519:
308  signature["method"] = "ed25519";
309  break;
310  default:
311  throw std::runtime_error("Unknown key type");
312  }
313  signature["sig"] = b64sig;
314 
315  Json::Value out_data;
316  signature["keyid"] = UptanePublicKey().KeyId();
317  out_data["signed"] = in_data;
318  out_data["signatures"] = Json::Value(Json::arrayValue);
319  out_data["signatures"].append(signature);
320  return out_data;
321 }
322 
323 std::string KeyManager::generateUptaneKeyPair() {
324  std::string primary_public;
325 
326  if (config_.uptane_key_source == CryptoSource::kFile) {
327  std::string primary_private;
328  if (!backend_->loadPrimaryKeys(&primary_public, &primary_private)) {
329  bool result_ = Crypto::generateKeyPair(config_.uptane_key_type, &primary_public, &primary_private);
330  if (result_) {
331  backend_->storePrimaryKeys(primary_public, primary_private);
332  }
333  }
334  if (primary_public.empty() && primary_private.empty()) {
335  throw std::runtime_error("Could not get uptane keys");
336  }
337  } else if (config_.uptane_key_source == CryptoSource::kAndroid) {
338 #if defined(ANDROID)
339  primary_public = AndroidKeyStore::instance().getPublicKey();
340  if (primary_public.empty()) {
341  primary_public = AndroidKeyStore::instance().generateKeyPair();
342  }
343 #else
344  throw std::runtime_error("Aktualizr was built without Android support");
345 #endif
346  if (primary_public.empty()) {
347  throw std::runtime_error("Could not get uptane keys");
348  }
349  } else {
350  if (!built_with_p11) {
351  throw std::runtime_error("Aktualizr was built without pkcs11 support!");
352  }
353  // dummy read to check if the key is present
354  if (!(*p11_)->readUptanePublicKey(&primary_public)) {
355  (*p11_)->generateUptaneKeyPair();
356  }
357  // really read the key
358  if (primary_public.empty() && !(*p11_)->readUptanePublicKey(&primary_public)) {
359  throw std::runtime_error("Could not get uptane keys");
360  }
361  }
362  return primary_public;
363 }
364 
365 PublicKey KeyManager::UptanePublicKey() const {
366  std::string primary_public;
367  if (config_.uptane_key_source == CryptoSource::kFile) {
368  if (!backend_->loadPrimaryPublic(&primary_public)) {
369  throw std::runtime_error("Could not get uptane public key!");
370  }
371  } else if (config_.uptane_key_source == CryptoSource::kAndroid) {
372 #if defined(ANDROID)
373  primary_public = AndroidKeyStore::instance().getPublicKey();
374 #else
375  throw std::runtime_error("Aktualizr was built without Android support");
376 #endif
377  if (primary_public.empty()) {
378  throw std::runtime_error("Could not get uptane public key!");
379  }
380  } else {
381  if (!built_with_p11) {
382  throw std::runtime_error("Aktualizr was built without pkcs11 support!");
383  }
384  // dummy read to check if the key is present
385  if (!(*p11_)->readUptanePublicKey(&primary_public)) {
386  throw std::runtime_error("Could not get uptane public key!");
387  }
388  }
389  return PublicKey(primary_public, config_.uptane_key_type);
390 }
STL namespace.