Use SHA512 signatures for the spotify blob.
SHA512 is unsupported by reasonable QCA versions so we must use libcrypto++ instead.
This commit is contained in:
parent
4cbe098b83
commit
319b8a5824
@ -58,6 +58,7 @@ find_package(FFTW3)
|
||||
|
||||
pkg_check_modules(CDIO libcdio)
|
||||
pkg_check_modules(CHROMAPRINT REQUIRED libchromaprint)
|
||||
pkg_check_modules(CRYPTOPP libcrypto++)
|
||||
pkg_check_modules(GIO gio-2.0)
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
|
||||
@ -71,7 +72,6 @@ pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.7)
|
||||
pkg_check_modules(LIBPULSE libpulse)
|
||||
pkg_check_modules(LIBXML libxml-2.0)
|
||||
pkg_check_modules(QCA qca2)
|
||||
pkg_check_modules(QJSON REQUIRED QJson)
|
||||
pkg_check_modules(SPOTIFY libspotify>=12.1.45)
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||
@ -273,10 +273,11 @@ optional_component(LIBPULSE ON "Pulse audio integration"
|
||||
|
||||
optional_component(VISUALISATIONS ON "Visualisations")
|
||||
|
||||
if(NOT HAVE_SPOTIFY_BLOB AND NOT QCA_FOUND)
|
||||
message(FATAL_ERROR "Either QCA must be available or the non-GPL Spotify "
|
||||
if(NOT HAVE_SPOTIFY_BLOB AND NOT CRYPTOPP_FOUND)
|
||||
message(FATAL_ERROR "Either crypto++ must be available or the non-GPL Spotify "
|
||||
"code must be compiled in")
|
||||
elseif(QCA_FOUND)
|
||||
elseif(CRYPTOPP_FOUND)
|
||||
set(HAVE_CRYPTOPP ON)
|
||||
set(HAVE_SPOTIFY_DOWNLOADER ON)
|
||||
endif()
|
||||
|
||||
|
6
debian/control
vendored
6
debian/control
vendored
@ -15,6 +15,7 @@ Build-Depends: debhelper (>= 7),
|
||||
libboost-serialization-dev,
|
||||
libcdio-cdda1,
|
||||
libchromaprint-dev,
|
||||
libcrypto++-dev,
|
||||
libechonest-dev,
|
||||
libglew1.5-dev |
|
||||
libglew-dev,
|
||||
@ -25,14 +26,12 @@ Build-Depends: debhelper (>= 7),
|
||||
libgstreamer1.0-dev,
|
||||
libgstreamer-plugins-base1.0-dev,
|
||||
libgpod-dev,
|
||||
libimobiledevice-dev,
|
||||
libplist-dev,
|
||||
libusbmuxd-dev,
|
||||
libmtp-dev,
|
||||
libqjson-dev,
|
||||
protobuf-compiler,
|
||||
libprotobuf-dev,
|
||||
libqca2-dev,
|
||||
libfftw3-dev,
|
||||
libsparsehash-dev,
|
||||
libsqlite3-dev,
|
||||
@ -49,8 +48,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
|
||||
gstreamer1.0-plugins-good,
|
||||
gstreamer1.0-plugins-ugly,
|
||||
gstreamer1.0-pulseaudio,
|
||||
libprojectm-data | projectm-data,
|
||||
libqca2-plugin-ossl
|
||||
libprojectm-data | projectm-data
|
||||
Description: Modern music player and library organiser inspired by Amarok 1.4
|
||||
Clementine focuses on a fast and easy-to-use interface for searching and
|
||||
playing your music.
|
||||
|
4
dist/clementine.spec.in
vendored
4
dist/clementine.spec.in
vendored
@ -13,11 +13,11 @@ BuildRequires: desktop-file-utils liblastfm-devel taglib-devel gettext
|
||||
BuildRequires: qt4-devel boost-devel gcc-c++ glew-devel libgpod-devel
|
||||
BuildRequires: cmake gstreamer1-devel gstreamer1-plugins-base-devel
|
||||
BuildRequires: libmtp-devel protobuf-devel protobuf-compiler libcdio-devel
|
||||
BuildRequires: qjson-devel qca2-devel fftw-devel sparsehash-devel
|
||||
BuildRequires: qjson-devel cryptopp-devel fftw-devel sparsehash-devel
|
||||
BuildRequires: sqlite-devel pulseaudio-libs-devel libechonest-devel
|
||||
BuildRequires: libchromaprint-devel
|
||||
|
||||
Requires: libgpod protobuf-lite libcdio qjson qca-ossl sqlite
|
||||
Requires: libgpod protobuf-lite libcdio qjson sqlite
|
||||
|
||||
# GStreamer codec dependencies
|
||||
Requires: gstreamer1-plugins-ugly
|
||||
|
@ -855,7 +855,7 @@ optional_source(HAVE_SPOTIFY_DOWNLOADER
|
||||
HEADERS
|
||||
internet/spotify/spotifyblobdownloader.h
|
||||
INCLUDE_DIRECTORIES
|
||||
${QCA_INCLUDE_DIRS}
|
||||
${CRYPTOPP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Platform specific - OS X
|
||||
@ -1316,9 +1316,9 @@ endif(HAVE_BREAKPAD)
|
||||
|
||||
if(HAVE_SPOTIFY_DOWNLOADER)
|
||||
target_link_libraries(clementine_lib
|
||||
${QCA_LIBRARIES}
|
||||
${CRYPTOPP_LIBRARIES}
|
||||
)
|
||||
link_directories(${QCA_LIBRARY_DIRS})
|
||||
link_directories(${CRYPTOPP_LIBRARY_DIRS})
|
||||
endif(HAVE_SPOTIFY_DOWNLOADER)
|
||||
|
||||
if(HAVE_LIBPULSE)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#cmakedefine HAVE_AUDIOCD
|
||||
#cmakedefine HAVE_BOX
|
||||
#cmakedefine HAVE_BREAKPAD
|
||||
#cmakedefine HAVE_CRYPTOPP
|
||||
#cmakedefine HAVE_DBUS
|
||||
#cmakedefine HAVE_DEVICEKIT
|
||||
#cmakedefine HAVE_DROPBOX
|
||||
@ -36,7 +37,6 @@
|
||||
#cmakedefine HAVE_LIBMTP
|
||||
#cmakedefine HAVE_LIBPULSE
|
||||
#cmakedefine HAVE_MOODBAR
|
||||
#cmakedefine HAVE_QCA
|
||||
#cmakedefine HAVE_SEAFILE
|
||||
#cmakedefine HAVE_SKYDRIVE
|
||||
#cmakedefine HAVE_SPARKLE
|
||||
|
@ -21,25 +21,34 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "spotifyservice.h"
|
||||
#include "core/arraysize.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#ifdef HAVE_QCA
|
||||
#include <QtCrypto>
|
||||
#endif // HAVE_QCA
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
const char* SpotifyBlobDownloader::kSignatureSuffix = ".sha1";
|
||||
#ifdef HAVE_CRYPTOPP
|
||||
#include <crypto++/pkcspad.h>
|
||||
#include <crypto++/rsa.h>
|
||||
#endif // HAVE_CRYPTOPP
|
||||
|
||||
namespace {
|
||||
static const char PEM_HEADER[] = "-----BEGIN PUBLIC KEY-----\n";
|
||||
static const char PEM_FOOTER[] = "\n-----END PUBLIC KEY-----";
|
||||
}
|
||||
|
||||
const char* SpotifyBlobDownloader::kSignatureSuffix = ".sha512";
|
||||
|
||||
SpotifyBlobDownloader::SpotifyBlobDownloader(const QString& version,
|
||||
const QString& path,
|
||||
@ -125,32 +134,11 @@ void SpotifyBlobDownloader::ReplyFinished() {
|
||||
file_data[filename] = reply->readAll();
|
||||
}
|
||||
|
||||
#ifdef HAVE_QCA
|
||||
// Load the public key
|
||||
QCA::ConvertResult conversion_result;
|
||||
QCA::PublicKey key = QCA::PublicKey::fromPEMFile(
|
||||
":/clementine-spotify-public.pem", &conversion_result);
|
||||
if (QCA::ConvertGood != conversion_result) {
|
||||
ShowError("Failed to load Spotify public key");
|
||||
if (!CheckSignature(file_data, signature_filenames)) {
|
||||
qLog(Warning) << "Signature checks failed";
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify signatures
|
||||
for (const QString& signature_filename : signature_filenames) {
|
||||
QString actual_filename = signature_filename;
|
||||
actual_filename.remove(kSignatureSuffix);
|
||||
|
||||
qLog(Debug) << "Verifying" << actual_filename << "against"
|
||||
<< signature_filename;
|
||||
|
||||
if (!key.verifyMessage(file_data[actual_filename],
|
||||
file_data[signature_filename], QCA::EMSA3_SHA1)) {
|
||||
ShowError("Invalid signature: " + actual_filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_QCA
|
||||
|
||||
// Make the destination directory and write the files into it
|
||||
QDir().mkpath(path_);
|
||||
|
||||
@ -195,6 +183,63 @@ void SpotifyBlobDownloader::ReplyFinished() {
|
||||
EmitFinished();
|
||||
}
|
||||
|
||||
bool SpotifyBlobDownloader::CheckSignature(
|
||||
const QMap<QString, QByteArray>& file_data,
|
||||
const QStringList& signature_filenames) {
|
||||
#ifdef HAVE_CRYPTOPP
|
||||
QFile public_key_file(":/clementine-spotify-public.pem");
|
||||
public_key_file.open(QIODevice::ReadOnly);
|
||||
QByteArray public_key_data = ConvertPEMToDER(public_key_file.readAll());
|
||||
|
||||
try {
|
||||
CryptoPP::ByteQueue bytes;
|
||||
bytes.Put(reinterpret_cast<const byte*>(public_key_data.constData()),
|
||||
public_key_data.size());
|
||||
bytes.MessageEnd();
|
||||
|
||||
CryptoPP::RSA::PublicKey public_key;
|
||||
public_key.Load(bytes);
|
||||
|
||||
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA512>::Verifier verifier(
|
||||
public_key);
|
||||
|
||||
for (const QString& signature_filename : signature_filenames) {
|
||||
QString actual_filename = signature_filename;
|
||||
actual_filename.remove(kSignatureSuffix);
|
||||
|
||||
const bool result = verifier.VerifyMessage(
|
||||
reinterpret_cast<const byte*>(file_data[actual_filename].constData()),
|
||||
file_data[actual_filename].size(),
|
||||
reinterpret_cast<const byte*>(
|
||||
file_data[signature_filename].constData()),
|
||||
file_data[signature_filename].size());
|
||||
qLog(Debug) << "Verifying" << actual_filename << "against"
|
||||
<< signature_filename << result;
|
||||
if (!result) {
|
||||
ShowError("Invalid signature: " + actual_filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (std::exception e) {
|
||||
// This should only happen if we fail to parse our own key.
|
||||
qLog(Debug) << "Verifying spotify blob signature failed:" << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HAVE_CRYPTOPP
|
||||
}
|
||||
|
||||
QByteArray SpotifyBlobDownloader::ConvertPEMToDER(const QByteArray& pem) {
|
||||
// Ensure we used char[] not char*
|
||||
static_assert(sizeof(PEM_HEADER) > sizeof(char*), "PEM_HEADER mis-declared");
|
||||
static_assert(sizeof(PEM_FOOTER) > sizeof(char*), "PEM_FOOTER mis-declared");
|
||||
int start = pem.indexOf(PEM_HEADER) + arraysize(PEM_HEADER) - 1;
|
||||
int length = pem.indexOf(PEM_FOOTER) - start;
|
||||
return QByteArray::fromBase64(pem.mid(start, length));
|
||||
}
|
||||
|
||||
void SpotifyBlobDownloader::ReplyProgress() {
|
||||
int progress = 0;
|
||||
int total = 0;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef INTERNET_SPOTIFY_SPOTIFYBLOBDOWNLOADER_H_
|
||||
#define INTERNET_SPOTIFY_SPOTIFYBLOBDOWNLOADER_H_
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
@ -52,6 +53,10 @@ class SpotifyBlobDownloader : public QObject {
|
||||
void ShowError(const QString& message);
|
||||
void EmitFinished();
|
||||
|
||||
bool CheckSignature(const QMap<QString, QByteArray>& file_data,
|
||||
const QStringList& signature_filenames);
|
||||
static QByteArray ConvertPEMToDER(const QByteArray& pem);
|
||||
|
||||
private:
|
||||
QString version_;
|
||||
QString path_;
|
||||
|
@ -78,10 +78,6 @@
|
||||
|
||||
#include <echonest/Config.h>
|
||||
|
||||
#ifdef HAVE_SPOTIFY_DOWNLOADER
|
||||
#include <QtCrypto>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
@ -398,10 +394,6 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SPOTIFY_DOWNLOADER
|
||||
QCA::Initializer qca_initializer;
|
||||
#endif
|
||||
|
||||
// Resources
|
||||
Q_INIT_RESOURCE(data);
|
||||
Q_INIT_RESOURCE(translations);
|
||||
|
Loading…
x
Reference in New Issue
Block a user