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.
		
		
		
		
		
			
		
			
				
					
					
						
							135 lines
						
					
					
						
							4.2 KiB
						
					
					
				
			
		
		
	
	
							135 lines
						
					
					
						
							4.2 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);
 | |
|   }
 | |
| 
 | |
|   QString jwt =
 | |
|       QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) +
 | |
|       '.' +
 | |
|       QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
 | |
| 
 | |
|   auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
 | |
|   auto sig = rsa_sign(hash);
 | |
|   jwt += '.' + sig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
 | |
|   return jwt;
 | |
| }
 | |
| 
 | |
| QString CommaApi::create_jwt() {
 | |
|   return create_jwt(*(new QVector<QPair<QString, QJsonValue>>()));
 | |
| }
 | |
| 
 | |
| RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, QVector<QPair<QString, QJsonValue>> payloads, bool disableWithScreen)
 | |
|   : disableWithScreen(disableWithScreen), QObject(parent)  {
 | |
|   networkAccessManager = new QNetworkAccessManager(this);
 | |
| 
 | |
|   QTimer* timer = new QTimer(this);
 | |
|   QObject::connect(timer, &QTimer::timeout, [=](){sendRequest(requestURL, payloads);});
 | |
|   timer->start(period_seconds * 1000);
 | |
| 
 | |
|   networkTimer = new QTimer(this);
 | |
|   networkTimer->setSingleShot(true);
 | |
|   networkTimer->setInterval(20000); // 20s before aborting
 | |
|   connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
 | |
| }
 | |
| 
 | |
| void RequestRepeater::sendRequest(QString requestURL, QVector<QPair<QString, QJsonValue>> payloads){
 | |
|   // No network calls onroad
 | |
|   if(GLWindow::ui_state.started){
 | |
|     return;
 | |
|   }
 | |
|   if (!active || (!GLWindow::ui_state.awake && disableWithScreen)) {
 | |
|     return;
 | |
|   }
 | |
|   if(reply != NULL){
 | |
|     return;
 | |
|   }
 | |
|   aborted = false;
 | |
|   QString token = CommaApi::create_jwt(payloads);
 | |
|   QNetworkRequest request;
 | |
|   request.setUrl(QUrl(requestURL));
 | |
|   request.setRawHeader("Authorization", ("JWT " + token).toUtf8());
 | |
| 
 | |
|   reply = networkAccessManager->get(request);
 | |
| 
 | |
|   networkTimer->start();
 | |
|   connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
 | |
| }
 | |
| 
 | |
| void RequestRepeater::requestTimeout(){
 | |
|   aborted = true;
 | |
|   reply->abort();
 | |
| }
 | |
| 
 | |
| // This function should always emit something
 | |
| void RequestRepeater::requestFinished(){
 | |
|   if(!aborted){
 | |
|     networkTimer->stop();
 | |
|     QString response = reply->readAll();
 | |
|     if (reply->error() == QNetworkReply::NoError) {
 | |
|       emit receivedResponse(response);
 | |
|     } else {
 | |
|       emit failedResponse(reply->errorString());
 | |
|     }
 | |
|   }else{
 | |
|     emit failedResponse("Custom Openpilot network timeout");
 | |
|   }
 | |
|   reply->deleteLater();
 | |
|   reply = NULL;
 | |
| }
 | |
| 
 |