diff options
author | Pawel Polanski <pawel.3.polanski@nokia.com> | 2010-12-13 17:17:41 +0100 |
---|---|---|
committer | Pawel Polanski <pawel.3.polanski@nokia.com> | 2010-12-14 08:45:11 +0100 |
commit | 993771f04cd2803aa3b24d9555a87306cec8bf34 (patch) | |
tree | b6f24405ea7124ea527d1a7de53a86eadcd6e50f /src/plugins/qt4projectmanager | |
parent | df43d55e04a9e29513b98ea34f4a5f5eec3ab20b (diff) | |
download | qt-creator-993771f04cd2803aa3b24d9555a87306cec8bf34.tar.gz |
Capabilities and IMEIs can now be extracted form Dev Certs for SymbianOS
Reviewed-by: Alessandro Portale
Diffstat (limited to 'src/plugins/qt4projectmanager')
5 files changed, 475 insertions, 36 deletions
diff --git a/src/plugins/qt4projectmanager/qt-s60/certificatepathchooser.cpp b/src/plugins/qt4projectmanager/qt-s60/certificatepathchooser.cpp index 7c31c85329..c7dabc83e3 100644 --- a/src/plugins/qt4projectmanager/qt-s60/certificatepathchooser.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/certificatepathchooser.cpp @@ -40,7 +40,16 @@ CertificatePathChooser::CertificatePathChooser(QWidget *parent) : bool CertificatePathChooser::validatePath(const QString &path, QString *errorMessage) { - if (Utils::PathChooser::validatePath(path, errorMessage)) - return S60CertificateInfo::validateCertificate(path, errorMessage) == S60CertificateInfo::CertificateValid; + if (Utils::PathChooser::validatePath(path, errorMessage)) { + QScopedPointer<S60CertificateInfo> certInfoPtr(new S60CertificateInfo(path)); + if (certInfoPtr.data()->validateCertificate() == S60CertificateInfo::CertificateValid) { + if (errorMessage) + *errorMessage = certInfoPtr.data()->toHtml(); + return true; + } else { + if (errorMessage) + *errorMessage = certInfoPtr.data()->errorString(); + } + } return false; } diff --git a/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.cpp b/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.cpp index 42ce0a79e4..9faa9ccef1 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.cpp @@ -32,42 +32,161 @@ #include <QDateTime> #include <QFileInfo> #include <QCoreApplication> +#include <QTextStream> #include "s60symbiancertificate.h" -S60CertificateInfo::CertificateState S60CertificateInfo::validateCertificate(const QString &certFilePath, QString *errorString) +namespace { + const char * const SIMPLE_DATE_FORMAT = "dd.MM.yyyy"; +} + +struct Capability { + const char *name; + const int value; +}; + +static const Capability capability[] = +{ + { "LocalServices", S60CertificateInfo::LocalServices }, + { "Location", S60CertificateInfo::Location }, + { "NetworkServices", S60CertificateInfo::NetworkServices }, + { "ReadUserData", S60CertificateInfo::ReadUserData }, + { "UserEnvironment", S60CertificateInfo::UserEnvironment }, + { "WriteUserData", S60CertificateInfo::WriteUserData }, + { "PowerMgmt", S60CertificateInfo::PowerMgmt }, + { "ProtServ", S60CertificateInfo::ProtServ }, + { "ReadDeviceData", S60CertificateInfo::ReadDeviceData }, + { "SurroundingsDD", S60CertificateInfo::SurroundingsDD }, + { "SwEvent", S60CertificateInfo::SwEvent }, + { "TrustedUI", S60CertificateInfo::TrustedUI }, + { "WriteDeviceData", S60CertificateInfo::WriteDeviceData }, + { "CommDD", S60CertificateInfo::CommDD }, + { "DiskAdmin", S60CertificateInfo::DiskAdmin }, + { "NetworkControl", S60CertificateInfo::NetworkControl }, + { "MultimediaDD", S60CertificateInfo::MultimediaDD }, + { "AllFiles", S60CertificateInfo::AllFiles }, + { "DRM", S60CertificateInfo::DRM }, + { "TCB", S60CertificateInfo::TCB } +}; + +QStringList createCapabilityList(uint capabilities) +{ + const int capabilityCount = sizeof(capability)/sizeof(capability[0]); + QStringList capabilityList; + for(int i = 0; i < capabilityCount; ++i) + if (capabilities&capability[i].value) + capabilityList << QLatin1String(capability[i].name); + return capabilityList; +} + +S60CertificateInfo::S60CertificateInfo(const QString &filePath, QObject* parent) + : QObject(parent), + m_certificate(new S60SymbianCertificate(filePath)), + m_filePath(filePath) +{ +} + +S60CertificateInfo::~S60CertificateInfo() +{ + delete m_certificate; +} + +S60CertificateInfo::CertificateState S60CertificateInfo::validateCertificate() { CertificateState result = CertificateValid; - S60SymbianCertificate *certificate = new S60SymbianCertificate(certFilePath); - if (certificate->isValid()) { + if (m_certificate->isValid()) { QDateTime currentTime(QDateTime::currentDateTimeUtc()); - QDateTime endTime(certificate->endTime()); - QDateTime startTime(certificate->startTime()); + QDateTime endTime(m_certificate->endTime()); + QDateTime startTime(m_certificate->startTime()); if (currentTime > endTime) { - if (errorString) - *errorString = QCoreApplication::translate( - "S60Utils::validateCertificate", - "The \"%1\" certificate has already expired and cannot be used.\nExpiration date: %2.") - .arg(QFileInfo(certFilePath).fileName()) - .arg(endTime.toLocalTime().toString()); + m_errorString = tr("The \"%1\" certificate has already expired and cannot be used." + "\nExpiration date: %2.") + .arg(QFileInfo(m_filePath).fileName()) + .arg(endTime.toLocalTime().toString(QLatin1String(SIMPLE_DATE_FORMAT))); result = CertificateError; } else if (currentTime < startTime) { - if (errorString) - *errorString = QCoreApplication::translate( - "S60Utils::validateCertificate", - "The \"%1\" certificate is not yet valid.\nValid from: %2.") - .arg(QFileInfo(certFilePath).fileName()) - .arg(startTime.toLocalTime().toString()); + m_errorString = tr("The \"%1\" certificate is not yet valid.\nValid from: %2.") + .arg(QFileInfo(m_filePath).fileName()) + .arg(startTime.toLocalTime().toString(QLatin1String(SIMPLE_DATE_FORMAT))); result = CertificateWarning; //This certificate may be valid in the near future } } else { - if (errorString) - *errorString = QCoreApplication::translate( - "S60Utils::validateCertificate", - "The \"%1\" certificate is not a valid X.509 certificate.") - .arg(QFileInfo(certFilePath).baseName()); + m_errorString = tr("The \"%1\" certificate is not a valid X.509 certificate.") + .arg(QFileInfo(m_filePath).baseName()); result = CertificateError; } - delete certificate; return result; } + +QString S60CertificateInfo::errorString() const +{ + return m_errorString.isEmpty()?m_certificate->errorString():m_errorString; +} + +quint32 S60CertificateInfo::capabilitiesSupported() +{ + return NoInformation; +} + +QString S60CertificateInfo::toHtml() +{ + const QStringList capabilityList(m_certificate->subjectInfo(QLatin1String("1.2.826.0.1.1796587.1.1.1.6"))); + + QString htmlString; + QTextStream str(&htmlString); + str << "<html><body><table>" + << "<tr><td><b>" << tr("Type: ") << "</b></td>"; + + if (!capabilityList.isEmpty()) + str << "<td>" << tr("Developer certificate") << "</td>"; + if (m_certificate->isSelfSigned()) + str << "<td>" << tr("Self signed certificate") << "</td>"; + str << "</tr>"; + + QString issuer; + QStringList issuerOrganizationList(m_certificate->issuerInfo("X520.Organization")); + if (!issuerOrganizationList.isEmpty()) + issuer = issuerOrganizationList.join(QString(" ")); + + QString subject; + QStringList subjectOrganizationList(m_certificate->subjectInfo("X520.Organization")); + if (!subjectOrganizationList.isEmpty()) + subject = subjectOrganizationList.join(QString(" ")); + + QDateTime startDate(m_certificate->startTime().toLocalTime()); + QDateTime endDate(m_certificate->endTime().toLocalTime()); + + str << "<tr><td><b>" << tr("Issued by: ") + << "</b></td><td>" << issuer << "</td></tr>" + << "<tr><td><b>" << tr("Issued to: ") + << "</b></td><td>" << subject << "</td></tr>" + << "<tr><td><b>" << tr("Valid from: ") + << "</b></td><td>" << startDate.toString(QLatin1String(SIMPLE_DATE_FORMAT)) << "</td></tr>" + << "<tr><td><b>" << tr("Valid to: ") + << "</b></td><td>" << endDate.toString(QLatin1String(SIMPLE_DATE_FORMAT)) << "</td></tr>"; + + if (!capabilityList.isEmpty()) { + bool isOk(false); + quint32 capabilities = capabilityList.at(0).toLong(&isOk); + if (isOk) { + str << "<tr><td><b>" << tr("Capabilities: ") + << "</b></td><td>" << createCapabilityList(capabilities).join(" ") << "</td></tr>"; + } + } + + const QStringList imeiList(m_certificate->subjectInfo(QLatin1String("1.2.826.0.1.1796587.1.1.1.1"))); + if (!imeiList.isEmpty()) { + QString imeiListString; + QString space(" "); + int MAX_DISPLAYED_IMEI_COUNT = 30; + if (imeiList.count() > MAX_DISPLAYED_IMEI_COUNT) {//1000 items would be too much :) + for (int i = 0; i < MAX_DISPLAYED_IMEI_COUNT; ++i) + imeiListString += imeiList.at(i) + space; + imeiListString.replace(imeiListString.length()-1, 1, QString("...")); + } else + imeiListString = imeiList.join(space); + str << "<tr><td><b>" << tr("Supporting %n device(s): ", "", imeiList.count()) + << "</b></td><td>" << imeiListString << "</td></tr>"; + } + return htmlString; +} diff --git a/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.h b/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.h index 7a63f18aad..fdcf634a92 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60certificateinfo.h @@ -30,19 +30,60 @@ #ifndef S60CERTIFICATEINFO_H #define S60CERTIFICATEINFO_H +#include <QtCore/QObject> #include <QtCore/QtGlobal> QT_FORWARD_DECLARE_CLASS(QString) +QT_FORWARD_DECLARE_CLASS(S60SymbianCertificate) -class S60CertificateInfo +class S60CertificateInfo : public QObject { + Q_OBJECT + public: enum CertificateState { CertificateValid, CertificateWarning, CertificateError }; - static CertificateState validateCertificate(const QString &certFilePath, QString *errorString = 0); + + enum S60Capability { + TCB = 1 << (31-0), + CommDD = 1 << (31-1), + PowerMgmt = 1 << (31-2), + MultimediaDD = 1 << (31-3), + ReadDeviceData = 1 << (31-4), + WriteDeviceData = 1 << (31-5), + DRM = 1 << (31-6), + TrustedUI = 1 << (31-7), + ProtServ = 1 << (31-8), + DiskAdmin = 1 << (31-9), + NetworkControl = 1 << (31-10), + AllFiles = 1 << (31-11), + SwEvent = 1 << (31-12), + NetworkServices = 1 << (31-13), + LocalServices = 1 << (31-14), + ReadUserData = 1 << (31-15), + WriteUserData = 1 << (31-16), + Location = 1 << (31-17), + SurroundingsDD = 1 << (31-18), + UserEnvironment = 1 << (31-19), + + NoInformation = 0 + }; + + explicit S60CertificateInfo(const QString &filePath, QObject* parent = 0); + ~S60CertificateInfo(); + + CertificateState validateCertificate(); + quint32 capabilitiesSupported(); + QString toHtml(); + QString errorString() const; + +private: + S60SymbianCertificate *m_certificate; + QString m_filePath; + QString m_errorString; }; #endif // S60CERTIFICATEINFO_H diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp index cf8b61aada..0a5103db28 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp @@ -469,20 +469,20 @@ bool S60CreatePackageStep::validateCustomSigningResources() ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); return false; } - - S60CertificateInfo::CertificateState certState = S60CertificateInfo::validateCertificate(customSignaturePath(), &errorString); + QScopedPointer<S60CertificateInfo> certInfoPtr(new S60CertificateInfo(customSignaturePath())); + S60CertificateInfo::CertificateState certState = certInfoPtr.data()->validateCertificate(); switch (certState) { case S60CertificateInfo::CertificateError: - emit addOutput(errorString, BuildStep::ErrorMessageOutput); + emit addOutput(certInfoPtr.data()->errorString(), BuildStep::ErrorMessageOutput); emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error, - errorString, + certInfoPtr.data()->errorString(), QString(), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); return false; case S60CertificateInfo::CertificateWarning: - emit addOutput(errorString, BuildStep::MessageOutput); + emit addOutput(certInfoPtr.data()->errorString(), BuildStep::MessageOutput); emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Warning, - errorString, + certInfoPtr.data()->errorString(), QString(), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); break; diff --git a/src/plugins/qt4projectmanager/qt-s60/s60symbiancertificate.cpp b/src/plugins/qt4projectmanager/qt-s60/s60symbiancertificate.cpp index 4c827e310f..f239f4995e 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60symbiancertificate.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60symbiancertificate.cpp @@ -38,10 +38,275 @@ #include <botan/bigint.h> #include <botan/oids.h> #include <botan/pem.h> +#include <botan/sha160.h> +#include <botan/oids.h> +#include <botan/libstate.h> +#include <botan/bit_ops.h> #include <algorithm> +#include <memory> using namespace Botan; +namespace { + const char * const CERT_IMEI_FIELD_NAME = "1.2.826.0.1.1796587.1.1.1.1"; + const char * const CERT_CAPABILITY_FIELD_NAME = "1.2.826.0.1.1796587.1.1.1.6"; +} + +// ======== S60CertificateExtension + +/* +* X.509 S60 Certificate Extension +*/ +class S60CertificateExtension : Certificate_Extension +{ +public: + OID oid_of() const; + + virtual S60CertificateExtension* copy() const = 0; + virtual ~S60CertificateExtension() {} +protected: + friend class S60Extensions; + virtual bool should_encode() const { return false; } + virtual MemoryVector<byte> encode_inner() const = 0; + virtual void decode_inner(const MemoryRegion<byte>&) = 0; +}; + +// ======== S60DeviceIdListConstraint + +class S60DeviceIdListConstraint : public S60CertificateExtension +{ +public: + S60CertificateExtension* copy() const { return new S60DeviceIdListConstraint(imei_list); } + + S60DeviceIdListConstraint() {} + S60DeviceIdListConstraint(const std::vector<ASN1_String>& o) : imei_list(o) {} + + std::vector<ASN1_String> get_imei_list() const { return imei_list; } +private: + std::string config_id() const { return "deviceid_list_constraint"; } + std::string oid_name() const { return CERT_IMEI_FIELD_NAME; } + + MemoryVector<byte> encode_inner() const; + void decode_inner(const MemoryRegion<byte>&); + void contents_to(Data_Store&, Data_Store&) const; + + std::vector<ASN1_String> imei_list; +}; + +/* +* Encode the extension +*/ +MemoryVector<byte> S60DeviceIdListConstraint::encode_inner() const +{ + qFatal("Encoding S60 extensions is not supported."); + return MemoryVector<byte>(); +} + +/* +* Decode the extension +*/ +#include "botan/hex.h" +void S60DeviceIdListConstraint::decode_inner(const MemoryRegion<byte>& in) +{ + BER_Decoder(in) + .start_cons(SEQUENCE) + .decode_list(imei_list) + .end_cons(); +} + +/* +* Return a textual representation +*/ +void S60DeviceIdListConstraint::contents_to(Data_Store& subject, Data_Store&) const +{ + for(u32bit j = 0; j != imei_list.size(); ++j) + subject.add(CERT_IMEI_FIELD_NAME, imei_list[j].value()); +} + +// ======== S60CapabilityConstraint + +class S60CapabilityConstraint : public S60CertificateExtension +{ +public: + S60CertificateExtension* copy() const { return new S60CapabilityConstraint(capabilities); } + + S60CapabilityConstraint() {} + S60CapabilityConstraint(const SecureVector<byte>& o) : capabilities(o) {} + + SecureVector<byte> get_capability_list() const { return capabilities; } +private: + std::string config_id() const { return "capability_constraint"; } + std::string oid_name() const { return "CERT_CAPABILITY_FIELD_NAME"; } + + MemoryVector<byte> encode_inner() const; + void decode_inner(const MemoryRegion<byte>&); + void contents_to(Data_Store&, Data_Store&) const; + + SecureVector<byte> capabilities; +}; + +/* +* Encode the extension +*/ +MemoryVector<byte> S60CapabilityConstraint::encode_inner() const +{ + qFatal("Encoding S60 extensions is not supported."); + return MemoryVector<byte>(); +} + +/* +* Decode the extension +*/ +void S60CapabilityConstraint::decode_inner(const MemoryRegion<byte>& in) +{ + BER_Decoder(in) + .decode(capabilities, BIT_STRING) + .verify_end(); +} + +/* +* Return a textual representation +*/ +void S60CapabilityConstraint::contents_to(Data_Store& subject, Data_Store&) const +{ + quint32 capabilitiesValue = 0; + for(u32bit j = 0; j != sizeof(quint32); ++j) { + quint32 capabilitie(capabilities[sizeof(quint32)-1-j]); + capabilitiesValue |= capabilitie << 8*j; + } + subject.add(CERT_CAPABILITY_FIELD_NAME, capabilitiesValue); +} + +// ======== S60Extensions + +class S60Extensions : public ASN1_Object +{ +public: + void encode_into(class DER_Encoder&) const; + void decode_from(class BER_Decoder&); + + void contents_to(Data_Store&, Data_Store&) const; + + void add(Certificate_Extension* extn) + { extensions.push_back(extn); } + + S60Extensions& operator=(const S60Extensions&); + + S60Extensions(const S60Extensions&); + S60Extensions(bool st = true) : should_throw(st) {} + ~S60Extensions(); +private: + static Certificate_Extension* get_extension(const OID&); + + std::vector<Certificate_Extension*> extensions; + bool should_throw; +}; + +/* +* S60Extensions Copy Constructor +*/ +S60Extensions::S60Extensions(const S60Extensions& extensions) : ASN1_Object() +{ + *this = extensions; +} + +/* +* Extensions Assignment Operator +*/ +S60Extensions& S60Extensions::operator=(const S60Extensions& other) +{ + for(u32bit j = 0; j != extensions.size(); ++j) + delete extensions[j]; + extensions.clear(); + + for(u32bit j = 0; j != other.extensions.size(); ++j) + extensions.push_back(other.extensions[j]->copy()); + + return (*this); +} + +/* +* Return the OID of this extension +*/ +OID Certificate_Extension::oid_of() const +{ + return OIDS::lookup(oid_name()); +} + +/* +* Encode an Extensions list +*/ +void S60Extensions::encode_into(DER_Encoder& to_object) const +{ + Q_UNUSED(to_object); + qFatal("Encoding S60 extensions is not supported."); +} + +/* +* Decode a list of Extensions +*/ +void S60Extensions::decode_from(BER_Decoder& from_source) +{ + for(u32bit j = 0; j != extensions.size(); ++j) + delete extensions[j]; + extensions.clear(); + + BER_Decoder sequence = from_source.start_cons(SEQUENCE); + while(sequence.more_items()) + { + OID oid; + MemoryVector<byte> value; + bool critical; + + sequence.start_cons(SEQUENCE) + .decode(oid) + .decode_optional(critical, BOOLEAN, UNIVERSAL, false) + .decode(value, OCTET_STRING) + .verify_end() + .end_cons(); + + S60CertificateExtension* ext = 0; + if (OIDS::name_of(oid, CERT_IMEI_FIELD_NAME)) + ext = new S60DeviceIdListConstraint(); + + if (OIDS::name_of(oid, CERT_CAPABILITY_FIELD_NAME)) + ext = new S60CapabilityConstraint(); + + if(!ext) + { + if(!critical || !should_throw) + continue; + + throw Decoding_Error("Encountered unknown X.509 extension marked " + "as critical; OID = " + oid.as_string()); + } + ext->decode_inner(value); + extensions.push_back(ext); + } + sequence.verify_end(); +} + +/* +* Write the extensions to an info store +*/ +void S60Extensions::contents_to(Data_Store& subject_info, + Data_Store& issuer_info) const +{ + for(u32bit j = 0; j != extensions.size(); ++j) + extensions[j]->contents_to(subject_info, issuer_info); +} + +/* +* Delete an Extensions list +*/ +S60Extensions::~S60Extensions() +{ + for(u32bit j = 0; j != extensions.size(); ++j) + delete extensions[j]; +} + + + // ======== S60SymbianCertificatePrivate class S60SymbianCertificatePrivate : private Botan::X509_Object @@ -242,6 +507,12 @@ void S60SymbianCertificatePrivate::force_decode() if(v3_exts_data.type_tag == 3 && v3_exts_data.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) { + S60Extensions s60extensions(false); + + BER_Decoder(v3_exts_data.value).decode(s60extensions).verify_end(); + + s60extensions.contents_to(m_subject, m_issuer); + Extensions extensions(false); BER_Decoder(v3_exts_data.value).decode(extensions).verify_end(); @@ -458,7 +729,6 @@ AlternativeName create_alt_name(const Data_Store& info) } // ======== S60SymbianCertificate -#include <QDebug> S60SymbianCertificate::S60SymbianCertificate(const QString &filename) : m_d(0) { @@ -498,7 +768,7 @@ QStringList S60SymbianCertificate::subjectInfo(const QString &name) for(i = subjectInfo.begin(); i != subjectInfo.end(); ++i) result << QString::fromStdString(*i); } catch (Botan::Exception &e) { - m_errorString = QString::fromLatin1(e.what()); + m_errorString = QString::fromLatin1(e.what()); } return result; } @@ -514,7 +784,7 @@ QStringList S60SymbianCertificate::issuerInfo(const QString &name) for(i = issuerInfo.begin(); i != issuerInfo.end(); ++i) result << QString::fromStdString(*i); } catch (Botan::Exception &e) { - m_errorString = QString::fromLatin1(e.what()); + m_errorString = QString::fromLatin1(e.what()); } return result; } |