# 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 ;
}