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