nopenpilot/selfdrive/ui/qt/api.cc

140 lines
4.2 KiB
C++

#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 "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;
}
HttpRequest::HttpRequest(QObject *parent, QString requestURL, const QString &cache_key) : cache_key(cache_key), QObject(parent) {
networkAccessManager = new QNetworkAccessManager(this);
reply = NULL;
networkTimer = new QTimer(this);
networkTimer->setSingleShot(true);
networkTimer->setInterval(20000);
connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
sendRequest(requestURL);
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 HttpRequest::sendRequest(QString requestURL){
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 HttpRequest::requestTimeout(){
reply->abort();
}
// This function should always emit something
void HttpRequest::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;
}