Aktualizr
C++ SOTA Client
keymanager.cc
1 #include "keymanager.h"
2 
3 #include <stdexcept>
4 
5 #include <boost/scoped_array.hpp>
6 #include <utility>
7 
8 // by using constexpr the compiler can optimize out method calls when the
9 // feature is disabled. We won't then need to link with the actual p11 engine
10 // implementation
11 #ifdef BUILD_P11
12 static constexpr bool built_with_p11 = true;
13 #else
14 static constexpr bool built_with_p11 = false;
15 #endif
16 
17 KeyManager::KeyManager(std::shared_ptr<INvStorage> backend, KeyManagerConfig config)
18  : backend_(std::move(backend)), config_(std::move(config)) {
19  if (built_with_p11) {
20  p11_ = std_::make_unique<P11EngineGuard>(config_.p11);
21  }
22 }
23 
24 void KeyManager::loadKeys(const std::string *pkey_content, const std::string *cert_content,
25  const std::string *ca_content) {
26  if (config_.tls_pkey_source == CryptoSource::kFile) {
27  std::string pkey;
28  if (pkey_content != nullptr) {
29  pkey = *pkey_content;
30  } else {
31  backend_->loadTlsPkey(&pkey);
32  }
33  if (!pkey.empty()) {
34  if (tmp_pkey_file == nullptr) {
35  tmp_pkey_file = std_::make_unique<TemporaryFile>("tls-pkey");
36  }
37  tmp_pkey_file->PutContents(pkey);
38  }
39  }
40  if (config_.tls_cert_source == CryptoSource::kFile) {
41  std::string cert;
42  if (cert_content != nullptr) {
43  cert = *cert_content;
44  } else {
45  backend_->loadTlsCert(&cert);
46  }
47  if (!cert.empty()) {
48  if (tmp_cert_file == nullptr) {
49  tmp_cert_file = std_::make_unique<TemporaryFile>("tls-cert");
50  }
51  tmp_cert_file->PutContents(cert);
52  }
53  }
54  if (config_.tls_ca_source == CryptoSource::kFile) {
55  std::string ca;
56  if (ca_content != nullptr) {
57  ca = *ca_content;
58  } else {
59  backend_->loadTlsCa(&ca);
60  }
61  if (!ca.empty()) {
62  if (tmp_ca_file == nullptr) {
63  tmp_ca_file = std_::make_unique<TemporaryFile>("tls-ca");
64  }
65  tmp_ca_file->PutContents(ca);
66  }
67  }
68 }
69 
70 std::string KeyManager::getPkeyFile() const {
71  std::string pkey_file;
72  if (config_.tls_pkey_source == CryptoSource::kPkcs11) {
73  if (!built_with_p11) {
74  throw std::runtime_error("Aktualizr was built without PKCS#11");
75  }
76  pkey_file = (*p11_)->getTlsPkeyId();
77  }
78  if (config_.tls_pkey_source == CryptoSource::kFile) {
79  if (tmp_pkey_file && !boost::filesystem::is_empty(tmp_pkey_file->PathString())) {
80  pkey_file = tmp_pkey_file->PathString();
81  }
82  }
83  return pkey_file;
84 }
85 
86 std::string KeyManager::getCertFile() const {
87  std::string cert_file;
88  if (config_.tls_cert_source == CryptoSource::kPkcs11) {
89  if (!built_with_p11) {
90  throw std::runtime_error("Aktualizr was built without PKCS#11");
91  }
92  cert_file = (*p11_)->getTlsCertId();
93  }
94  if (config_.tls_cert_source == CryptoSource::kFile) {
95  if (tmp_cert_file && !boost::filesystem::is_empty(tmp_cert_file->PathString())) {
96  cert_file = tmp_cert_file->PathString();
97  }
98  }
99  return cert_file;
100 }
101 
102 std::string KeyManager::getCaFile() const {
103  std::string ca_file;
104  if (config_.tls_ca_source == CryptoSource::kPkcs11) {
105  if (!built_with_p11) {
106  throw std::runtime_error("Aktualizr was built without PKCS#11");
107  }
108  ca_file = (*p11_)->getTlsCacertId();
109  }
110  if (config_.tls_ca_source == CryptoSource::kFile) {
111  if (tmp_ca_file && !boost::filesystem::is_empty(tmp_ca_file->PathString())) {
112  ca_file = tmp_ca_file->PathString();
113  }
114  }
115  return ca_file;
116 }
117 
118 std::string KeyManager::getPkey() const {
119  std::string pkey;
120  if (config_.tls_pkey_source == CryptoSource::kPkcs11) {
121  if (!built_with_p11) {
122  throw std::runtime_error("Aktualizr was built without PKCS#11");
123  }
124  pkey = (*p11_)->getTlsPkeyId();
125  }
126  if (config_.tls_pkey_source == CryptoSource::kFile) {
127  backend_->loadTlsPkey(&pkey);
128  }
129  return pkey;
130 }
131 
132 std::string KeyManager::getCert() const {
133  std::string cert;
134  if (config_.tls_cert_source == CryptoSource::kPkcs11) {
135  if (!built_with_p11) {
136  throw std::runtime_error("Aktualizr was built without PKCS#11");
137  }
138  cert = (*p11_)->getTlsCertId();
139  }
140  if (config_.tls_cert_source == CryptoSource::kFile) {
141  backend_->loadTlsCert(&cert);
142  }
143  return cert;
144 }
145 
146 std::string KeyManager::getCa() const {
147  std::string ca;
148  if (config_.tls_ca_source == CryptoSource::kPkcs11) {
149  if (!built_with_p11) {
150  throw std::runtime_error("Aktualizr was built without PKCS#11");
151  }
152  ca = (*p11_)->getTlsCacertId();
153  }
154  if (config_.tls_ca_source == CryptoSource::kFile) {
155  backend_->loadTlsCa(&ca);
156  }
157  return ca;
158 }
159 
160 std::string KeyManager::getCN() const {
161  std::string not_found_cert_message = "Certificate is not found, can't extract device_id";
162  std::string cert;
163  if (config_.tls_cert_source == CryptoSource::kFile) {
164  if (!backend_->loadTlsCert(&cert)) {
165  throw std::runtime_error(not_found_cert_message);
166  }
167  } else { // CryptoSource::kPkcs11
168  if (!built_with_p11) {
169  throw std::runtime_error("Aktualizr was built without PKCS#11 support, can't extract device_id");
170  }
171  if (!(*p11_)->readTlsCert(&cert)) {
172  throw std::runtime_error(not_found_cert_message);
173  }
174  }
175 
176  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(cert.c_str()), static_cast<int>(cert.size())), BIO_vfree);
177  StructGuard<X509> x(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free);
178  if (x == nullptr) {
179  throw std::runtime_error("Could not parse certificate");
180  }
181 
182  int len = X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, nullptr, 0);
183  if (len < 0) {
184  throw std::runtime_error("Could not get CN from certificate");
185  }
186  boost::scoped_array<char> buf(new char[len + 1]);
187  X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, buf.get(), len + 1);
188  std::string cn(buf.get());
189  return cn;
190 }
191 
192 void KeyManager::copyCertsToCurl(const std::shared_ptr<HttpInterface> &http) {
193  std::string pkey = getPkey();
194  std::string cert = getCert();
195  std::string ca = getCa();
196 
197  if ((pkey.size() != 0u) && (cert.size() != 0u) && (ca.size() != 0u)) {
198  http->setCerts(ca, config_.tls_ca_source, cert, config_.tls_cert_source, pkey, config_.tls_pkey_source);
199  }
200 }
201 
202 Json::Value KeyManager::signTuf(const Json::Value &in_data) const {
203  ENGINE *crypto_engine = nullptr;
204  std::string private_key;
205  if (config_.uptane_key_source == CryptoSource::kPkcs11) {
206  if (!built_with_p11) {
207  throw std::runtime_error("Aktualizr was built without PKCS#11");
208  }
209  crypto_engine = (*p11_)->getEngine();
210  private_key = config_.p11.uptane_key_id;
211  }
212  if (config_.uptane_key_source == CryptoSource::kFile) {
213  backend_->loadPrimaryPrivate(&private_key);
214  }
215  std::string b64sig = Utils::toBase64(
216  Crypto::Sign(config_.uptane_key_type, crypto_engine, private_key, Json::FastWriter().write(in_data)));
217  Json::Value signature;
218  signature["method"] = "rsassa-pss";
219  signature["sig"] = b64sig;
220 
221  Json::Value out_data;
222  signature["keyid"] = UptanePublicKey().KeyId();
223  out_data["signed"] = in_data;
224  out_data["signatures"] = Json::Value(Json::arrayValue);
225  out_data["signatures"].append(signature);
226  return out_data;
227 }
228 
229 std::string KeyManager::generateUptaneKeyPair() {
230  std::string primary_public;
231 
232  if (config_.uptane_key_source == CryptoSource::kFile) {
233  std::string primary_private;
234  if (!backend_->loadPrimaryKeys(&primary_public, &primary_private)) {
235  if (Crypto::generateKeyPair(config_.uptane_key_type, &primary_public, &primary_private)) {
236  backend_->storePrimaryKeys(primary_public, primary_private);
237  }
238  }
239  if (primary_public.empty() && primary_private.empty()) {
240  throw std::runtime_error("Could not get uptane keys");
241  }
242  } else {
243  if (!built_with_p11) {
244  throw std::runtime_error("Aktualizr was built without pkcs11 support!");
245  }
246  // dummy read to check if the key is present
247  if (!(*p11_)->readUptanePublicKey(&primary_public)) {
248  (*p11_)->generateUptaneKeyPair();
249  }
250  // really read the key
251  if (primary_public.empty() && !(*p11_)->readUptanePublicKey(&primary_public)) {
252  throw std::runtime_error("Could not get uptane keys");
253  }
254  }
255  return primary_public;
256 }
257 
258 PublicKey KeyManager::UptanePublicKey() const {
259  std::string primary_public;
260  if (config_.uptane_key_source == CryptoSource::kFile) {
261  if (!backend_->loadPrimaryPublic(&primary_public)) {
262  throw std::runtime_error("Could not get uptane public key!");
263  }
264  } else {
265  if (!built_with_p11) {
266  throw std::runtime_error("Aktualizr was built without pkcs11 support!");
267  }
268  // dummy read to check if the key is present
269  if (!(*p11_)->readUptanePublicKey(&primary_public)) {
270  throw std::runtime_error("Could not get uptane public key!");
271  }
272  }
273  return PublicKey(primary_public, config_.uptane_key_type);
274 }
STL namespace.