Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
crypto.cc
1 #include "crypto.h"
2 
3 #include <boost/algorithm/hex.hpp>
4 #include <boost/scoped_array.hpp>
5 #include <iostream>
6 
7 #include <sodium.h>
8 
9 #include "logging/logging.h"
10 #include "openssl_compat.h"
11 #include "utilities/utils.h"
12 
13 PublicKey::PublicKey(const boost::filesystem::path &path) : value_(Utils::readFile(path)) {
14  type_ = Crypto::IdentifyRSAKeyType(value_);
15 }
16 
17 PublicKey::PublicKey(Json::Value uptane_json) {
18  if (!uptane_json["keytype"].isString()) {
19  type_ = KeyType::kUnknown;
20  return;
21  }
22  if (!uptane_json["keyval"].isObject()) {
23  type_ = KeyType::kUnknown;
24  return;
25  }
26 
27  if (!uptane_json["keyval"]["public"].isString()) {
28  type_ = KeyType::kUnknown;
29  return;
30  }
31 
32  std::string keytype = uptane_json["keytype"].asString();
33  std::string keyvalue = uptane_json["keyval"]["public"].asString();
34 
35  std::transform(keytype.begin(), keytype.end(), keytype.begin(), ::tolower);
36 
37  KeyType type;
38  if (keytype == "ed25519") {
39  type = KeyType::kED25519;
40  } else if (keytype == "rsa") {
41  type = Crypto::IdentifyRSAKeyType(keyvalue);
42  if (type == KeyType::kUnknown) {
43  LOG_WARNING << "Couldn't identify length of RSA key";
44  }
45  } else {
46  type = KeyType::kUnknown;
47  }
48  type_ = type;
49  value_ = keyvalue;
50 }
51 
52 PublicKey::PublicKey(const std::string &value, KeyType type) : value_(value), type_(type) {
53  if (Crypto::IsRsaKeyType(type)) {
54  if (type != Crypto::IdentifyRSAKeyType(value)) {
55  std::logic_error("RSA key length is incorrect");
56  }
57  }
58 }
59 
60 bool PublicKey::VerifySignature(const std::string &signature, const std::string &message) const {
61  switch (type_) {
62  case KeyType::kED25519:
63  return Crypto::ED25519Verify(boost::algorithm::unhex(value_), Utils::fromBase64(signature), message);
64  case KeyType::kRSA2048:
65  case KeyType::kRSA3072:
66  case KeyType::kRSA4096:
67  return Crypto::RSAPSSVerify(value_, Utils::fromBase64(signature), message);
68  default:
69  return false;
70  }
71 }
72 
73 bool PublicKey::operator==(const PublicKey &rhs) const { return value_ == rhs.value_ && type_ == rhs.type_; }
74 Json::Value PublicKey::ToUptane() const {
75  Json::Value res;
76  switch (type_) {
77  case KeyType::kRSA2048:
78  case KeyType::kRSA3072:
79  case KeyType::kRSA4096:
80  res["keytype"] = "RSA";
81  break;
82  case KeyType::kED25519:
83  res["keytype"] = "ED25519";
84  break;
85  case KeyType::kUnknown:
86  res["keytype"] = "unknown";
87  break;
88  default:
89  throw std::range_error("Unknown key type in PublicKey::ToUptane");
90  }
91  res["keyval"]["public"] = value_;
92  return res;
93 }
94 
95 std::string PublicKey::KeyId() const {
96  std::string key_content = value_;
97  boost::algorithm::trim_right_if(key_content, boost::algorithm::is_any_of("\n"));
98  std::string keyid = boost::algorithm::hex(Crypto::sha256digest(Utils::jsonToCanonicalStr(Json::Value(key_content))));
99  std::transform(keyid.begin(), keyid.end(), keyid.begin(), ::tolower);
100  return keyid;
101 }
102 
103 std::string Crypto::sha256digest(const std::string &text) {
104  unsigned char sha256_hash[crypto_hash_sha256_BYTES];
105  crypto_hash_sha256(sha256_hash, reinterpret_cast<const unsigned char *>(text.c_str()), text.size());
106  return std::string(reinterpret_cast<char *>(sha256_hash), crypto_hash_sha256_BYTES);
107 }
108 
109 std::string Crypto::sha512digest(const std::string &text) {
110  unsigned char sha512_hash[crypto_hash_sha512_BYTES];
111  crypto_hash_sha512(sha512_hash, reinterpret_cast<const unsigned char *>(text.c_str()), text.size());
112  return std::string(reinterpret_cast<char *>(sha512_hash), crypto_hash_sha512_BYTES);
113 }
114 
115 std::string Crypto::RSAPSSSign(ENGINE *engine, const std::string &private_key, const std::string &message) {
116  StructGuard<EVP_PKEY> key(nullptr, EVP_PKEY_free);
117  StructGuard<RSA> rsa(nullptr, RSA_free);
118  if (engine != nullptr) {
119  // TODO(OTA-2138): this call leaks memory somehow...
120  key.reset(ENGINE_load_private_key(engine, private_key.c_str(), nullptr, nullptr));
121 
122  if (key == nullptr) {
123  LOG_ERROR << "ENGINE_load_private_key failed with error " << ERR_error_string(ERR_get_error(), nullptr);
124  return std::string();
125  }
126 
127  rsa.reset(EVP_PKEY_get1_RSA(key.get()));
128  if (rsa == nullptr) {
129  LOG_ERROR << "EVP_PKEY_get1_RSA failed with error " << ERR_error_string(ERR_get_error(), nullptr);
130  return std::string();
131  }
132  } else {
133  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(private_key.c_str()), static_cast<int>(private_key.size())),
134  BIO_vfree);
135  key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
136  if (key != nullptr) {
137  rsa.reset(EVP_PKEY_get1_RSA(key.get()));
138  }
139 
140  if (rsa == nullptr) {
141  LOG_ERROR << "PEM_read_bio_PrivateKey failed with error " << ERR_error_string(ERR_get_error(), nullptr);
142  return std::string();
143  }
144 
145 #if AKTUALIZR_OPENSSL_PRE_11
146  RSA_set_method(rsa.get(), RSA_PKCS1_SSLeay());
147 #else
148  RSA_set_method(rsa.get(), RSA_PKCS1_OpenSSL());
149 #endif
150  }
151 
152  const auto sign_size = static_cast<unsigned int>(RSA_size(rsa.get()));
153  boost::scoped_array<unsigned char> EM(new unsigned char[sign_size]);
154  boost::scoped_array<unsigned char> pSignature(new unsigned char[sign_size]);
155 
156  std::string digest = Crypto::sha256digest(message);
157  int status = RSA_padding_add_PKCS1_PSS(rsa.get(), EM.get(), reinterpret_cast<const unsigned char *>(digest.c_str()),
158  EVP_sha256(), -1 /* maximum salt length*/);
159  if (status == 0) {
160  LOG_ERROR << "RSA_padding_add_PKCS1_PSS failed with error " << ERR_error_string(ERR_get_error(), nullptr);
161  return std::string();
162  }
163 
164  /* perform digital signature */
165  status = RSA_private_encrypt(RSA_size(rsa.get()), EM.get(), pSignature.get(), rsa.get(), RSA_NO_PADDING);
166  if (status == -1) {
167  LOG_ERROR << "RSA_private_encrypt failed with error " << ERR_error_string(ERR_get_error(), nullptr);
168  return std::string();
169  }
170  std::string retval = std::string(reinterpret_cast<char *>(pSignature.get()), sign_size);
171  return retval;
172 }
173 
174 std::string Crypto::Sign(KeyType key_type, ENGINE *engine, const std::string &private_key, const std::string &message) {
175  if (key_type == KeyType::kED25519) {
176  return Crypto::ED25519Sign(boost::algorithm::unhex(private_key), message);
177  }
178  return Crypto::RSAPSSSign(engine, private_key, message);
179 }
180 
181 std::string Crypto::ED25519Sign(const std::string &private_key, const std::string &message) {
182  unsigned char sig[crypto_sign_BYTES];
183  crypto_sign_detached(sig, nullptr, reinterpret_cast<const unsigned char *>(message.c_str()), message.size(),
184  reinterpret_cast<const unsigned char *>(private_key.c_str()));
185  return std::string(reinterpret_cast<char *>(sig), crypto_sign_BYTES);
186 }
187 
188 bool Crypto::RSAPSSVerify(const std::string &public_key, const std::string &signature, const std::string &message) {
189  StructGuard<RSA> rsa(nullptr, RSA_free);
190  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(public_key.c_str()), static_cast<int>(public_key.size())),
191  BIO_vfree);
192  {
193  RSA *r = nullptr;
194  if (PEM_read_bio_RSA_PUBKEY(bio.get(), &r, nullptr, nullptr) == nullptr) {
195  LOG_ERROR << "PEM_read_bio_RSA_PUBKEY failed with error " << ERR_error_string(ERR_get_error(), nullptr);
196  return false;
197  }
198  rsa.reset(r);
199  }
200 
201 #if AKTUALIZR_OPENSSL_PRE_11
202  RSA_set_method(rsa.get(), RSA_PKCS1_SSLeay());
203 #else
204  RSA_set_method(rsa.get(), RSA_PKCS1_OpenSSL());
205 #endif
206 
207  const auto size = static_cast<unsigned int>(RSA_size(rsa.get()));
208  boost::scoped_array<unsigned char> pDecrypted(new unsigned char[size]);
209  /* now we will verify the signature
210  Start by a RAW decrypt of the signature
211  */
212  int status =
213  RSA_public_decrypt(static_cast<int>(signature.size()), reinterpret_cast<const unsigned char *>(signature.c_str()),
214  pDecrypted.get(), rsa.get(), RSA_NO_PADDING);
215  if (status == -1) {
216  LOG_ERROR << "RSA_public_decrypt failed with error " << ERR_error_string(ERR_get_error(), nullptr);
217  return false;
218  }
219 
220  std::string digest = Crypto::sha256digest(message);
221 
222  /* verify the data */
223  status = RSA_verify_PKCS1_PSS(rsa.get(), reinterpret_cast<const unsigned char *>(digest.c_str()), EVP_sha256(),
224  pDecrypted.get(), -2 /* salt length recovered from signature*/);
225 
226  return status == 1;
227 }
228 bool Crypto::ED25519Verify(const std::string &public_key, const std::string &signature, const std::string &message) {
229  if (public_key.size() < crypto_sign_PUBLICKEYBYTES || signature.size() < crypto_sign_BYTES) {
230  return false;
231  }
232  return crypto_sign_verify_detached(reinterpret_cast<const unsigned char *>(signature.c_str()),
233  reinterpret_cast<const unsigned char *>(message.c_str()), message.size(),
234  reinterpret_cast<const unsigned char *>(public_key.c_str())) == 0;
235 }
236 
237 bool Crypto::parseP12(BIO *p12_bio, const std::string &p12_password, std::string *out_pkey, std::string *out_cert,
238  std::string *out_ca) {
239 #if AKTUALIZR_OPENSSL_PRE_11
240  SSLeay_add_all_algorithms();
241 #endif
242  StructGuard<PKCS12> p12(d2i_PKCS12_bio(p12_bio, nullptr), PKCS12_free);
243  if (p12 == nullptr) {
244  LOG_ERROR << "Could not read from " << p12_bio << " file pointer";
245  return false;
246  }
247 
248  // use a lambda here because sk_X509_pop_free is a macro
249  auto stackx509_free = [](STACK_OF(X509) * stack) {
250  sk_X509_pop_free(stack, X509_free); // NOLINT
251  };
252 
253  StructGuard<EVP_PKEY> pkey(nullptr, EVP_PKEY_free);
254  StructGuard<X509> x509_cert(nullptr, X509_free);
255  StructGuard<STACK_OF(X509)> ca_certs(nullptr, stackx509_free);
256  {
257  EVP_PKEY *pk;
258  X509 *x509c = nullptr;
259  STACK_OF(X509) *cacs = nullptr;
260  if (PKCS12_parse(p12.get(), p12_password.c_str(), &pk, &x509c, &cacs) == 0) {
261  LOG_ERROR << "Could not parse file from " << p12_bio << " source pointer";
262  return false;
263  }
264  pkey.reset(pk);
265  x509_cert.reset(x509c);
266  ca_certs.reset(cacs);
267  }
268 
269  StructGuard<BIO> pkey_pem_sink(BIO_new(BIO_s_mem()), BIO_vfree);
270  if (pkey_pem_sink == nullptr) {
271  LOG_ERROR << "Could not open pkey buffer for writing";
272  return false;
273  }
274  PEM_write_bio_PrivateKey(pkey_pem_sink.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
275 
276  char *pkey_buf;
277  auto pkey_len = BIO_get_mem_data(pkey_pem_sink.get(), &pkey_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
278  *out_pkey = std::string(pkey_buf, static_cast<size_t>(pkey_len));
279 
280  char *cert_buf;
281  size_t cert_len;
282  StructGuard<BIO> cert_sink(BIO_new(BIO_s_mem()), BIO_vfree);
283  if (cert_sink == nullptr) {
284  LOG_ERROR << "Could not open certificate buffer for writing";
285  return false;
286  }
287  PEM_write_bio_X509(cert_sink.get(), x509_cert.get());
288 
289  char *ca_buf;
290  size_t ca_len;
291  StructGuard<BIO> ca_sink(BIO_new(BIO_s_mem()), BIO_vfree);
292  if (ca_sink == nullptr) {
293  LOG_ERROR << "Could not open ca buffer for writing";
294  return false;
295  }
296  X509 *ca_cert = nullptr;
297  for (int i = 0; i < sk_X509_num(ca_certs.get()); i++) { // NOLINT
298  ca_cert = sk_X509_value(ca_certs.get(), i); // NOLINT
299  PEM_write_bio_X509(ca_sink.get(), ca_cert);
300  PEM_write_bio_X509(cert_sink.get(), ca_cert);
301  }
302  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
303  ca_len = static_cast<size_t>(BIO_get_mem_data(ca_sink.get(), &ca_buf));
304  *out_ca = std::string(ca_buf, ca_len);
305 
306  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
307  cert_len = static_cast<size_t>(BIO_get_mem_data(cert_sink.get(), &cert_buf));
308  *out_cert = std::string(cert_buf, cert_len);
309 
310  return true;
311 }
312 
313 bool Crypto::extractSubjectCN(const std::string &cert, std::string *cn) {
314  StructGuard<BIO> bio(BIO_new_mem_buf(const_cast<char *>(cert.c_str()), static_cast<int>(cert.size())), BIO_vfree);
315  StructGuard<X509> x(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free);
316  if (x == nullptr) {
317  return false;
318  }
319 
320  int len = X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, nullptr, 0);
321  if (len < 0) {
322  return false;
323  }
324  boost::scoped_array<char> buf(new char[len + 1]);
325  X509_NAME_get_text_by_NID(X509_get_subject_name(x.get()), NID_commonName, buf.get(), len + 1);
326  *cn = std::string(buf.get());
327  return true;
328 }
329 
330 StructGuard<EVP_PKEY> Crypto::generateRSAKeyPairEVP(KeyType key_type) {
331  int bits;
332  switch (key_type) {
333  case KeyType::kRSA2048:
334  bits = 2048;
335  break;
336  case KeyType::kRSA3072:
337  bits = 3072;
338  break;
339  case KeyType::kRSA4096:
340  bits = 4096;
341  break;
342  default:
343  return {nullptr, EVP_PKEY_free};
344  }
345 
346  int ret;
347 
348  ret = RAND_status();
349  if (ret != 1) { /* random generator has NOT been seeded with enough data */
350  ret = RAND_poll();
351  if (ret != 1) { /* seed data was NOT generated */
352  return {nullptr, EVP_PKEY_free};
353  }
354  }
355 
356  StructGuard<BIGNUM> bne(BN_new(), BN_free);
357  ret = BN_set_word(bne.get(), RSA_F4);
358  if (ret != 1) {
359  return {nullptr, EVP_PKEY_free};
360  }
361  StructGuard<RSA> rsa(RSA_new(), RSA_free);
362  ret = RSA_generate_key_ex(rsa.get(), bits, /* number of bits for the key - 2048 is a sensible value */
363  bne.get(), /* exponent - RSA_F4 is defined as 0x10001L */
364  nullptr); /* callback argument - not needed in this case */
365  if (ret != 1) {
366  return {nullptr, EVP_PKEY_free};
367  }
368 
369  StructGuard<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
370  // release the rsa pointer here, pkey is the new owner
371  EVP_PKEY_assign_RSA(pkey.get(), rsa.release()); // NOLINT
372  return pkey;
373 }
374 
375 /**
376  * Generate a RSA keypair
377  * @param key_type Algorithm used to generate the key
378  * @param public_key Generated public part of key
379  * @param private_key Generated private part of key
380  * @return true if the keys are present at the end of this function (either they were created or existed already)
381  * false if key generation failed
382  */
383 bool Crypto::generateRSAKeyPair(KeyType key_type, std::string *public_key, std::string *private_key) {
384  int ret = 0;
385  StructGuard<EVP_PKEY> pkey = generateRSAKeyPairEVP(key_type);
386  if (pkey == nullptr) {
387  return false;
388  }
389 
390  char *pubkey_buf;
391  StructGuard<BIO> pubkey_sink(BIO_new(BIO_s_mem()), BIO_vfree);
392  if (pubkey_sink == nullptr) {
393  return false;
394  }
395  ret = PEM_write_bio_PUBKEY(pubkey_sink.get(), pkey.get());
396  if (ret != 1) {
397  return false;
398  }
399  auto pubkey_len = BIO_get_mem_data(pubkey_sink.get(), &pubkey_buf); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
400  *public_key = std::string(pubkey_buf, static_cast<size_t>(pubkey_len));
401 
402  char *privkey_buf;
403  StructGuard<BIO> privkey_sink(BIO_new(BIO_s_mem()), BIO_vfree);
404  if (privkey_sink == nullptr) {
405  return false;
406  }
407 
408  ret = PEM_write_bio_RSAPrivateKey(privkey_sink.get(), static_cast<RSA *>(EVP_PKEY_get0(pkey.get())), nullptr, nullptr,
409  0, nullptr, nullptr);
410  if (ret != 1) {
411  return false;
412  }
413  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
414  auto privkey_len = BIO_get_mem_data(privkey_sink.get(), &privkey_buf);
415  *private_key = std::string(privkey_buf, static_cast<size_t>(privkey_len));
416  return true;
417 }
418 
419 bool Crypto::generateEDKeyPair(std::string *public_key, std::string *private_key) {
420  unsigned char pk[crypto_sign_PUBLICKEYBYTES];
421  unsigned char sk[crypto_sign_SECRETKEYBYTES];
422  crypto_sign_keypair(pk, sk);
423  *public_key = boost::algorithm::hex(std::string(reinterpret_cast<char *>(pk), crypto_sign_PUBLICKEYBYTES));
424  // std::transform(public_key->begin(), public_key->end(), public_key->begin(), ::tolower);
425  *private_key = boost::algorithm::hex(std::string(reinterpret_cast<char *>(sk), crypto_sign_SECRETKEYBYTES));
426  // std::transform(private_key->begin(), private_key->end(), private_key->begin(), ::tolower);
427  return true;
428 }
429 
430 bool Crypto::generateKeyPair(KeyType key_type, std::string *public_key, std::string *private_key) {
431  if (key_type == KeyType::kED25519) {
432  return Crypto::generateEDKeyPair(public_key, private_key);
433  }
434  return Crypto::generateRSAKeyPair(key_type, public_key, private_key);
435 }
436 
437 bool Crypto::IsRsaKeyType(KeyType type) {
438  switch (type) {
439  case KeyType::kRSA2048:
440  case KeyType::kRSA3072:
441  case KeyType::kRSA4096:
442  return true;
443  default:
444  return false;
445  }
446 }
447 KeyType Crypto::IdentifyRSAKeyType(const std::string &public_key_pem) {
448  StructGuard<BIO> bufio(BIO_new_mem_buf(reinterpret_cast<const void *>(public_key_pem.c_str()),
449  static_cast<int>(public_key_pem.length())),
450  BIO_vfree);
451  if (bufio.get() == nullptr) {
452  throw std::runtime_error("BIO_new_mem_buf failed");
453  }
454  StructGuard<::RSA> rsa(PEM_read_bio_RSA_PUBKEY(bufio.get(), nullptr, nullptr, nullptr), RSA_free);
455 
456  if (rsa.get() == nullptr) {
457  return KeyType::kUnknown;
458  }
459 
460  int key_length = RSA_size(rsa.get()) * 8;
461  // It is not clear from the OpenSSL documentation if RSA_size returns
462  // exactly 2048 or 4096, or if this can vary. For now we will assume that if
463  // OpenSSL has been asked to generate a 'N bit' key, RSA_size() will return
464  // exactly N
465  switch (key_length) {
466  case 2048:
467  return KeyType::kRSA2048;
468  case 3072:
469  return KeyType::kRSA3072;
470  case 4096:
471  return KeyType::kRSA4096;
472  default:
473  LOG_WARNING << "Weird key length:" << key_length;
474  return KeyType::kUnknown;
475  }
476 }
PublicKey::ToUptane
Json::Value ToUptane() const
Uptane Json representation of this public key.
Definition: crypto.cc:74
Utils
Definition: utils.h:13
PublicKey
Definition: crypto.h:26
Crypto::generateRSAKeyPair
static bool generateRSAKeyPair(KeyType key_type, std::string *public_key, std::string *private_key)
Generate a RSA keypair.
Definition: crypto.cc:383
PublicKey::VerifySignature
bool VerifySignature(const std::string &signature, const std::string &message) const
Verify a signature using this public key.
Definition: crypto.cc:60