#include "selfdrive/ui/qt/api.h" #include #include #include #include #include #include #include #include #include #include #include "common/util.h" #include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" namespace CommaApi { RSA *get_rsa_private_key() { static std::unique_ptr rsa_private(nullptr, RSA_free); if (!rsa_private) { FILE *fp = fopen(Path::rsa_file().c_str(), "rb"); if (!fp) { qDebug() << "No RSA private key found, please run manager.py or registration.py"; return nullptr; } rsa_private.reset(PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)); fclose(fp); } return rsa_private.get(); } QByteArray rsa_sign(const QByteArray &data) { RSA *rsa_private = get_rsa_private_key(); if (!rsa_private) return {}; QByteArray sig(RSA_size(rsa_private), Qt::Uninitialized); 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.size() == sig_len); return sig; } QString create_jwt(const QJsonObject &payloads, int expiry) { QJsonObject header = {{"alg", "RS256"}}; auto t = QDateTime::currentSecsSinceEpoch(); QJsonObject payload = {{"identity", getDongleId().value_or("")}, {"nbf", t}, {"iat", t}, {"exp", t + expiry}}; for (auto it = payloads.begin(); it != payloads.end(); ++it) { payload.insert(it.key(), it.value()); } 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); return jwt + "." + rsa_sign(hash).toBase64(b64_opts); } } // namespace CommaApi HttpRequest::HttpRequest(QObject *parent, bool create_jwt, int timeout) : create_jwt(create_jwt), QObject(parent) { networkTimer = new QTimer(this); networkTimer->setSingleShot(true); networkTimer->setInterval(timeout); connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout); } bool HttpRequest::active() const { return reply != nullptr; } bool HttpRequest::timeout() const { return reply && reply->error() == QNetworkReply::OperationCanceledError; } void HttpRequest::sendRequest(const QString &requestURL, const HttpRequest::Method method) { if (active()) { qDebug() << "HttpRequest is active"; return; } QString token; if (create_jwt) { token = CommaApi::create_jwt(); } else { QString token_json = QString::fromStdString(util::read_file(util::getenv("HOME") + "/.comma/auth.json")); QJsonDocument json_d = QJsonDocument::fromJson(token_json.toUtf8()); token = json_d["access_token"].toString(); } QNetworkRequest request; request.setUrl(QUrl(requestURL)); request.setRawHeader("User-Agent", getUserAgent().toUtf8()); if (!token.isEmpty()) { request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8()); } if (method == HttpRequest::Method::GET) { reply = nam()->get(request); } else if (method == HttpRequest::Method::DELETE) { reply = nam()->deleteResource(request); } networkTimer->start(); connect(reply, &QNetworkReply::finished, this, &HttpRequest::requestFinished); } void HttpRequest::requestTimeout() { reply->abort(); } void HttpRequest::requestFinished() { networkTimer->stop(); if (reply->error() == QNetworkReply::NoError) { emit requestDone(reply->readAll(), true, reply->error()); } else { QString error; if (reply->error() == QNetworkReply::OperationCanceledError) { nam()->clearAccessCache(); nam()->clearConnectionCache(); error = "Request timed out"; } else { error = reply->errorString(); } emit requestDone(error, false, reply->error()); } reply->deleteLater(); reply = nullptr; } QNetworkAccessManager *HttpRequest::nam() { static QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(qApp); return networkAccessManager; }