You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							147 lines
						
					
					
						
							4.6 KiB
						
					
					
				
			
		
		
	
	
							147 lines
						
					
					
						
							4.6 KiB
						
					
					
				#include <QDateTime>
 | 
						|
#include <QDebug>
 | 
						|
#include <QFile>
 | 
						|
#include <QJsonDocument>
 | 
						|
#include <QJsonObject>
 | 
						|
#include <QNetworkReply>
 | 
						|
#include <QNetworkRequest>
 | 
						|
#include <QString>
 | 
						|
#include <QWidget>
 | 
						|
#include <QTimer>
 | 
						|
#include <QRandomGenerator>
 | 
						|
 | 
						|
#include "api.hpp"
 | 
						|
#include "home.hpp"
 | 
						|
#include "common/params.h"
 | 
						|
#include "common/util.h"
 | 
						|
 | 
						|
#if defined(QCOM) || defined(QCOM2)
 | 
						|
const std::string private_key_path = "/persist/comma/id_rsa";
 | 
						|
#else
 | 
						|
const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa");
 | 
						|
#endif
 | 
						|
 | 
						|
QByteArray CommaApi::rsa_sign(QByteArray data) {
 | 
						|
  auto file = QFile(private_key_path.c_str());
 | 
						|
  if (!file.open(QIODevice::ReadOnly)) {
 | 
						|
    qDebug() << "No RSA private key found, please run manager.py or registration.py";
 | 
						|
    return QByteArray();
 | 
						|
  }
 | 
						|
  auto key = file.readAll();
 | 
						|
  file.close();
 | 
						|
  file.deleteLater();
 | 
						|
  BIO* mem = BIO_new_mem_buf(key.data(), key.size());
 | 
						|
  assert(mem);
 | 
						|
  RSA* rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
 | 
						|
  assert(rsa_private);
 | 
						|
  auto sig = QByteArray();
 | 
						|
  sig.resize(RSA_size(rsa_private));
 | 
						|
  unsigned int sig_len;
 | 
						|
  int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private);
 | 
						|
  assert(ret == 1);
 | 
						|
  assert(sig_len == sig.size());
 | 
						|
  BIO_free(mem);
 | 
						|
  RSA_free(rsa_private);
 | 
						|
  return sig;
 | 
						|
}
 | 
						|
 | 
						|
QString CommaApi::create_jwt(QVector<QPair<QString, QJsonValue>> payloads, int expiry) {
 | 
						|
  QString dongle_id = QString::fromStdString(Params().get("DongleId"));
 | 
						|
 | 
						|
  QJsonObject header;
 | 
						|
  header.insert("alg", "RS256");
 | 
						|
 | 
						|
  QJsonObject payload;
 | 
						|
  payload.insert("identity", dongle_id);
 | 
						|
 | 
						|
  auto t = QDateTime::currentSecsSinceEpoch();
 | 
						|
  payload.insert("nbf", t);
 | 
						|
  payload.insert("iat", t);
 | 
						|
  payload.insert("exp", t + expiry);
 | 
						|
  for (auto load : payloads) {
 | 
						|
    payload.insert(load.first, load.second);
 | 
						|
  }
 | 
						|
 | 
						|
  auto b64_opts = QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals;
 | 
						|
  QString jwt = QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64(b64_opts) + '.' +
 | 
						|
                QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(b64_opts);
 | 
						|
 | 
						|
  auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
 | 
						|
  auto sig = rsa_sign(hash);
 | 
						|
  jwt += '.' + sig.toBase64(b64_opts);
 | 
						|
  return jwt;
 | 
						|
}
 | 
						|
 | 
						|
RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, const QString &cache_key, bool disableWithScreen)
 | 
						|
  : disableWithScreen(disableWithScreen), cache_key(cache_key), QObject(parent)  {
 | 
						|
  networkAccessManager = new QNetworkAccessManager(this);
 | 
						|
 | 
						|
  reply = NULL;
 | 
						|
 | 
						|
  QTimer* timer = new QTimer(this);
 | 
						|
  QObject::connect(timer, &QTimer::timeout, [=](){sendRequest(requestURL);});
 | 
						|
  timer->start(period_seconds * 1000);
 | 
						|
 | 
						|
  networkTimer = new QTimer(this);
 | 
						|
  networkTimer->setSingleShot(true);
 | 
						|
  networkTimer->setInterval(20000);
 | 
						|
  connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
 | 
						|
 | 
						|
  if (!cache_key.isEmpty()) {
 | 
						|
    if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
 | 
						|
      QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RequestRepeater::sendRequest(QString requestURL){
 | 
						|
  if (GLWindow::ui_state.scene.started || !active || reply != NULL ||
 | 
						|
      (!GLWindow::ui_state.awake && disableWithScreen)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  QString token = CommaApi::create_jwt();
 | 
						|
  QNetworkRequest request;
 | 
						|
  request.setUrl(QUrl(requestURL));
 | 
						|
  request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8());
 | 
						|
 | 
						|
#ifdef QCOM
 | 
						|
  QSslConfiguration ssl = QSslConfiguration::defaultConfiguration();
 | 
						|
  ssl.setCaCertificates(QSslCertificate::fromPath("/usr/etc/tls/cert.pem",
 | 
						|
                        QSsl::Pem, QRegExp::Wildcard));
 | 
						|
  request.setSslConfiguration(ssl);
 | 
						|
#endif
 | 
						|
 | 
						|
  reply = networkAccessManager->get(request);
 | 
						|
 | 
						|
  networkTimer->start();
 | 
						|
  connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
 | 
						|
}
 | 
						|
 | 
						|
void RequestRepeater::requestTimeout(){
 | 
						|
  reply->abort();
 | 
						|
}
 | 
						|
 | 
						|
// This function should always emit something
 | 
						|
void RequestRepeater::requestFinished(){
 | 
						|
  if (reply->error() != QNetworkReply::OperationCanceledError) {
 | 
						|
    networkTimer->stop();
 | 
						|
    QString response = reply->readAll();
 | 
						|
    if (reply->error() == QNetworkReply::NoError) {
 | 
						|
      // save to cache
 | 
						|
      if (!cache_key.isEmpty()) {
 | 
						|
        Params().put(cache_key.toStdString(), response.toStdString());
 | 
						|
      }
 | 
						|
      emit receivedResponse(response);
 | 
						|
    } else {
 | 
						|
      if (!cache_key.isEmpty()) {
 | 
						|
        Params().remove(cache_key.toStdString());
 | 
						|
      }
 | 
						|
      emit failedResponse(reply->errorString());
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    emit timeoutResponse("timeout");
 | 
						|
  }
 | 
						|
  reply->deleteLater();
 | 
						|
  reply = NULL;
 | 
						|
}
 | 
						|
 |