openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
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.

143 lines
4.2 KiB

#include "selfdrive/ui/qt/api.h"
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <QApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDebug>
#include <QJsonDocument>
#include <QNetworkRequest>
#include <memory>
#include <string>
#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, decltype(&RSA_free)> 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;
}