diff --git a/extension/content.js b/extension/content.js index d4c7558a..dd90cf48 100644 --- a/extension/content.js +++ b/extension/content.js @@ -108,7 +108,7 @@ function TokenSigning() { \n\ }); \n\ } \n\ this.getCertificate = function(options) { \n\ - var msg = {type: "CERT", lang: options.lang}; \n\ + var msg = {type: "CERT", lang: options.lang, filter: options.filter}; \n\ console.log("getCertificate()"); \n\ return messagePromise(msg); \n\ }; \n\ diff --git a/extension/manifest.json b/extension/manifest.json index 6024f095..208054dd 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,6 +1,6 @@ { "name": "Token signing", - "version": "0.0.26", + "version": "0.0.27", "minimum_chrome_version": "40.0", "manifest_version": 2, "description": "Use your eID smart card on the web", diff --git a/extension/page.js b/extension/page.js index 6ea47db6..ffed6b49 100644 --- a/extension/page.js +++ b/extension/page.js @@ -75,7 +75,7 @@ function TokenSigning() { }); } this.getCertificate = function(options) { - var msg = {type: "CERT", lang: options.lang}; + var msg = {type: "CERT", lang: options.lang, filter: options.filter}; console.log("getCertificate()"); return messagePromise(msg); }; diff --git a/host-linux/CertificateSelection.h b/host-linux/CertificateSelection.h index e139d557..14041d96 100644 --- a/host-linux/CertificateSelection.h +++ b/host-linux/CertificateSelection.h @@ -29,32 +29,49 @@ #include #include #include +#include #include #include +#include + class CertificateSelection: public QDialog { public: - static QVariantMap getCert() + static QVariantMap getCert(bool forSigning) { try { QList certs; PKCS11Path::Params p11 = PKCS11Path::getPkcs11ModulePath(); - for (auto &token : PKCS11CardManager::instance(p11.path)->getAvailableTokens()) { - PKCS11CardManager *manager = PKCS11CardManager::instance(p11.path)->getManagerForReader(token); - if (!manager -> hasSignCert()) { - delete manager; + for (const PKCS11CardManager::Token &token : PKCS11CardManager::instance(p11.path)->tokens()) { + QByteArray data = QByteArray::fromRawData((const char*)token.cert.data(), token.cert.size()); + QSslCertificate cert(data, QSsl::Der); + + bool isCA = false; + for(const QSslCertificateExtension &ex: cert.extensions()) + { + if(ex.name() == "basicConstraints") + isCA = ex.value().toMap().value("ca").toBool(); + } + + if (isCA) continue; + + ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING*)X509_get_ext_d2i((X509*)cert.handle(), NID_key_usage, 0, 0); + const int keyUsageNonRepudiation = 1; + const bool isNonRepudiation = ASN1_BIT_STRING_get_bit(keyusage, keyUsageNonRepudiation); + ASN1_BIT_STRING_free(keyusage); + if (!((forSigning && isNonRepudiation) || (!forSigning && !isNonRepudiation))) { + _log("certificate is non-repu: %u, requesting signing certificate %u, moving on to next token...", isNonRepudiation, forSigning); + continue; } - QByteArray data((const char*)&manager->getSignCert()[0], manager->getSignCert().size()); - QSslCertificate cert(data, QSsl::Der); + if (QDateTime::currentDateTime() < cert.expiryDate() && !isDuplicate(certs, data.toHex())) { certs << (QStringList() - << manager->getCN().c_str() - << manager->getType().c_str() + << cert.subjectInfo(QSslCertificate::CommonName).join("") + << cert.subjectInfo(QSslCertificate::Organization).join("") << cert.expiryDate().toString("dd.MM.yyyy") << data.toHex()); } - delete manager; } if (certs.empty()) return {{"result", "no_certificates"}}; diff --git a/host-linux/Signer.h b/host-linux/Signer.h index 48a3d840..a75749f4 100644 --- a/host-linux/Signer.h +++ b/host-linux/Signer.h @@ -30,12 +30,15 @@ #include #include #include +#include #include #include #include #include +#include + class Signer: public QDialog { enum { UserCancel = 0, @@ -55,26 +58,39 @@ class Signer: public QDialog { return {{"result", "invalid_argument"}}; } - std::unique_ptr manager; + std::vector data = fromHex(cert); + PKCS11CardManager::Token selected; PKCS11Path::Params p11 = PKCS11Path::getPkcs11ModulePath(); try { - for (auto &token : PKCS11CardManager::instance(p11.path)->getAvailableTokens()) { - manager.reset(PKCS11CardManager::instance(p11.path)->getManagerForReader(token)); - if (manager->getSignCert() == fromHex(cert)) { + for (const PKCS11CardManager::Token &token : PKCS11CardManager::instance(p11.path)->tokens()) { + if (token.cert == data) { + selected = token; break; } - manager.reset(); } - - if(!manager) - return {{"result", "invalid_argument"}}; } catch (const std::runtime_error &a) { return {{"result", "technical_error"}}; } + if(selected.cert.empty()) + return {{"result", "invalid_argument"}}; + + QSslCertificate c(QByteArray::fromRawData((const char*)data.data(), data.size()), QSsl::Der); + ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING*)X509_get_ext_d2i((X509*)c.handle(), NID_key_usage, 0, 0); + const int keyUsageNonRepudiation = 1; + QString label; + if (ASN1_BIT_STRING_get_bit(keyusage, keyUsageNonRepudiation)) { + label = Labels::l10n.get(selected.pinpad ? "sign PIN pinpad" : "sign PIN").c_str(); + label.replace("PIN", p11.signPINLabel.c_str()); + } + else { + label = Labels::l10n.get(selected.pinpad ? "auth PIN pinpad" : "auth PIN").c_str(); + label.replace("PIN", p11.authPINLabel.c_str()); + } + bool isInitialCheck = true; - for (int retriesLeft = manager->getPIN2RetryCount(); retriesLeft > 0; ) { - Signer dialog(p11.signPINLabel.c_str(), manager->isPinpad()); + for (int retriesLeft = selected.retry; retriesLeft > 0; ) { + Signer dialog(label, selected.minPinLen, selected.pinpad); if (retriesLeft < 3) { dialog.errorLabel->show(); dialog.errorLabel->setText(QString("%1%2 %3") @@ -83,14 +99,14 @@ class Signer: public QDialog { .arg(retriesLeft)); } isInitialCheck = false; - dialog.nameLabel->setText(manager->getCN().c_str()); + dialog.nameLabel->setText(c.subjectInfo(QSslCertificate::CommonName).join("")); std::future< std::vector > signature; - if (manager->isPinpad()) { + if (selected.pinpad) { signature = std::async(std::launch::async, [&](){ std::vector result; try { - result = manager->sign(fromHex(hash), nullptr); + result = PKCS11CardManager::instance(p11.path)->sign(selected, fromHex(hash), nullptr); dialog.accept(); } catch (const AuthenticationError &) { --retriesLeft; @@ -115,15 +131,15 @@ class Signer: public QDialog { case TechnicalError: return {{"result", "technical_error"}}; default: - if (manager->isPinpad()) { + if (selected.pinpad) { return {{"signature", toHex(signature.get())}}; } } try { - if (!manager->isPinpad()) { - std::vector result = manager->sign(fromHex(hash), - dialog.pin->text().toUtf8().constData()); + if (!selected.pinpad) { + std::vector result = PKCS11CardManager::instance(p11.path)->sign( + selected, fromHex(hash), dialog.pin->text().toUtf8().constData()); return {{"signature", toHex(result)}}; } } catch (const AuthenticationBadInput &) { @@ -141,16 +157,16 @@ class Signer: public QDialog { private: static QByteArray toHex(const std::vector &data) { - return QByteArray((const char*)data.data(), data.size()).toHex(); + return QByteArray::fromRawData((const char*)data.data(), data.size()).toHex(); } static std::vector fromHex(const QString &data) { QByteArray bin = QByteArray::fromHex(data.toLatin1()); - return std::vector(bin.constData(), bin.constData() + bin.size()); + return std::vector(bin.cbegin(), bin.cend()); } - Signer(const QString &label, bool isPinpad) + Signer(const QString &label, unsigned long minPinLen, bool isPinpad) : nameLabel(new QLabel(this)) , pinLabel(new QLabel(this)) , errorLabel(new QLabel(this)) @@ -162,9 +178,7 @@ class Signer: public QDialog { setMinimumWidth(400); setWindowFlags(Qt::WindowStaysOnTopHint); - setWindowTitle(Labels::l10n.get("signing").c_str()); - pinLabel->setText(Labels::l10n.get(isPinpad ? "sign PIN pinpad" : "sign PIN").c_str()); - pinLabel->setText(pinLabel->text().replace("PIN", label)); + pinLabel->setText(label); errorLabel->setTextFormat(Qt::RichText); errorLabel->hide(); @@ -193,10 +207,10 @@ class Signer: public QDialog { pin = new QLineEdit(this); pin->setEchoMode(QLineEdit::Password); pin->setFocus(); - pin->setValidator(new QRegExpValidator(QRegExp("\\d{5,12}"), pin)); + pin->setValidator(new QRegExpValidator(QRegExp(QString("\\d{%1,12}").arg(minPinLen)), pin)); pin->setMaxLength(12); - connect(pin, &QLineEdit::textEdited, [&](const QString &text){ - ok->setEnabled(text.size() >= 5); + connect(pin, &QLineEdit::textEdited, [=](const QString &text){ + ok->setEnabled(text.size() >= minPinLen); }); layout->addWidget(pin); diff --git a/host-linux/chrome-host.cpp b/host-linux/chrome-host.cpp index 836af84d..810e42a7 100644 --- a/host-linux/chrome-host.cpp +++ b/host-linux/chrome-host.cpp @@ -109,7 +109,7 @@ void Application::parse() resp = Signer::sign(json.value("hash").toString(), cert); } } else if (type == "CERT") { - resp = CertificateSelection::getCert(); + resp = CertificateSelection::getCert(json.value("filter").toString() != "AUTH"); cert = resp.value("cert").toString(); } else { resp = {{"result", "invalid_argument"}}; diff --git a/host-linux/chrome-token-signing.pro b/host-linux/chrome-token-signing.pro index b7193d62..68eb09f3 100644 --- a/host-linux/chrome-token-signing.pro +++ b/host-linux/chrome-token-signing.pro @@ -2,7 +2,7 @@ TEMPLATE = app CONFIG += console c++11 link_pkgconfig CONFIG -= app_bundle QT += widgets network -isEmpty(VERSION):VERSION=1.0.4.0 +isEmpty(VERSION):VERSION=1.0.6.0 PKGCONFIG += openssl libpcsclite INCLUDEPATH += ../host-shared LIBS += -ldl diff --git a/host-osx/CertificateSelection.h b/host-osx/CertificateSelection.h index 01b9a4a7..cd142dc0 100644 --- a/host-osx/CertificateSelection.h +++ b/host-osx/CertificateSelection.h @@ -20,6 +20,6 @@ @interface CertificateSelection : NSObject -+ (NSDictionary *)show; ++ (NSDictionary *)show:(bool)forSigning; @end diff --git a/host-osx/CertificateSelection.mm b/host-osx/CertificateSelection.mm index 1dd1564c..ea1b3d41 100644 --- a/host-osx/CertificateSelection.mm +++ b/host-osx/CertificateSelection.mm @@ -26,6 +26,7 @@ #import #import +#include #define _L(KEY) @(Labels::l10n.get(KEY).c_str()) @@ -50,35 +51,57 @@ - (bool)isDuplicate:(NSString*)certificate { return false; } -- (instancetype)init +- (instancetype)init:(bool)forSigning { if (self = [super init]) { certificates = [[NSMutableArray alloc] init]; try { NSDateFormatter *df = [[NSDateFormatter alloc] init]; df.dateFormat = @"dd.MM.YYYY"; - NSDateFormatter *asn1 = [[NSDateFormatter alloc] init]; - asn1.dateFormat = @"yyyyMMddHHmmss'Z'"; - asn1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; PKCS11Path::Params p11 = PKCS11Path::getPkcs11ModulePath(); - for (auto &token : PKCS11CardManager::instance(p11.path)->getAvailableTokens()) { - PKCS11CardManager *local = PKCS11CardManager::instance(p11.path)->getManagerForReader(token); - if (!local -> hasSignCert()) { - _log("no signing certificate, moving on to next token..."); - delete local; + for (const PKCS11CardManager::Token &token : PKCS11CardManager::instance(p11.path)->tokens()) { + CFDataRef data = CFDataCreateWithBytesNoCopy(nil, token.cert.data(), token.cert.size(), kCFAllocatorNull); + SecCertificateRef cert = SecCertificateCreateWithData(nil, data); + CFRelease(data); + NSDictionary *dict = CFBridgingRelease(SecCertificateCopyValues(cert, nil, nil)); + CFRelease(cert); + + NSDictionary *bc = dict[(__bridge NSString*)kSecOIDBasicConstraints][(__bridge NSString*)kSecPropertyKeyValue][1]; + if ([@"YES" isEqualToString:bc[(__bridge NSString*)kSecPropertyKeyValue]]) { + _log("Cert is CA"); continue; } - NSDate *date = [asn1 dateFromString:@(local->getValidTo().c_str())]; - if ([date compare:NSDate.date] > 0 && ![self isDuplicate:@(BinaryUtils::bin2hex(local->getSignCert()).c_str())]) { - _log("token has valid signing certificate, adding it to selection"); - [certificates addObject: @{ - @"cert": @(BinaryUtils::bin2hex(local->getSignCert()).c_str()), - @"validTo": [df stringFromDate:date], - @"CN": @(local->getCN().c_str()), - @"type": @(local->getType().c_str()), - }]; + + NSNumber *ku = dict[(__bridge NSString*)kSecOIDKeyUsage][(__bridge NSString*)kSecPropertyKeyValue]; + const bool isNonRepudiation = ku.unsignedIntValue & kSecKeyUsageNonRepudiation; + if (!((forSigning && isNonRepudiation) || (!forSigning && !isNonRepudiation))) { + _log("certificate is non-repu: %u, requesting signing certificate %u, moving on to next token...", isNonRepudiation, forSigning); + continue; + } + + NSDateComponents *components = [[NSDateComponents alloc] init]; + components.year = 2001; + NSNumber *na = dict[(__bridge NSString*)kSecOIDX509V1ValidityNotAfter][(__bridge NSString*)kSecPropertyKeyValue]; + NSDate *date = [NSDate dateWithTimeInterval:na.intValue sinceDate:[NSCalendar.currentCalendar dateFromComponents:components]]; + NSString *hex = @(BinaryUtils::bin2hex(token.cert).c_str()); + if ([date compare:NSDate.date] <= 0 || [self isDuplicate:hex]) { + _log("token has expired or is duplicate"); + continue; + } + + _log("token has valid signing certificate, adding it to selection"); + NSString *cn = [NSString string]; + NSString *type = [NSString string]; + for (NSDictionary *item in dict[(__bridge NSString*)kSecOIDX509V1SubjectName][(__bridge NSString*)kSecPropertyKeyValue]) { + if ([item[(__bridge NSString*)kSecPropertyKeyLabel] isEqualToString:(__bridge NSString*)kSecOIDCommonName]) { + cn = item[(__bridge NSString*)kSecPropertyKeyValue]; + } + if ([item[(__bridge NSString*)kSecPropertyKeyLabel] isEqualToString:(__bridge NSString*)kSecOIDOrganizationName]) { + type = item[(__bridge NSString*)kSecPropertyKeyValue]; + } } - delete local; + + [certificates addObject: @{@"cert": hex, @"validTo": [df stringFromDate:date], @"CN": cn, @"type": type}]; } } catch (const std::runtime_error &e) { self = nil; @@ -106,9 +129,9 @@ - (instancetype)init return self; } -+ (NSDictionary *)show ++ (NSDictionary *)show:(bool)forSigning { - CertificateSelection *dialog = [[CertificateSelection alloc] init]; + CertificateSelection *dialog = [[CertificateSelection alloc] init:forSigning]; if (!dialog) { return @{@"result": @"technical_error"}; } diff --git a/host-osx/PINDialog.mm b/host-osx/PINDialog.mm index 54ab322a..ad92c57d 100644 --- a/host-osx/PINDialog.mm +++ b/host-osx/PINDialog.mm @@ -36,6 +36,7 @@ @interface PINPanel () { IBOutlet NSSecureTextField *pinField; IBOutlet NSTextField *pinFieldLabel; IBOutlet NSProgressIndicator *progressBar; + unsigned long minPinLen; } @end @@ -57,8 +58,7 @@ - (instancetype)init:(NSString*)label pinpad:(BOOL)pinpad okButton.title = _L("sign"); cancelButton.title = _L("cancel"); } - pinFieldLabel.stringValue = [_L(pinpad ? "sign PIN pinpad" : "sign PIN") stringByReplacingOccurrencesOfString:@"PIN" withString:label]; - window.title =_L("signing"); + pinFieldLabel.stringValue = label; } return self; } @@ -95,40 +95,63 @@ + (NSDictionary *)show:(NSDictionary*)params cert:(NSString *)cert } PKCS11Path::Params p11 = PKCS11Path::getPkcs11ModulePath(); - std::unique_ptr selected; + PKCS11CardManager::Token selected; try { - for (auto &token : PKCS11CardManager::instance(p11.path)->getAvailableTokens()) { - selected.reset(PKCS11CardManager::instance(p11.path)->getManagerForReader(token)); - if (BinaryUtils::hex2bin(cert.UTF8String) == selected->getSignCert()) { + for (const PKCS11CardManager::Token &token : PKCS11CardManager::instance(p11.path)->tokens()) { + if (BinaryUtils::hex2bin(cert.UTF8String) == token.cert) { + selected = token; break; } - selected.reset(); } } catch(const std::runtime_error &) { return @{@"result": @"technical_error"}; } - if (!selected) { + if (selected.cert.empty()) { return @{@"result": @"invalid_argument"}; } bool isInitialCheck = true; - for (int retriesLeft = selected->getPIN2RetryCount(); retriesLeft > 0; ) { - PINPanel *dialog = [[PINPanel alloc] init:[NSString stringWithUTF8String:p11.signPINLabel.c_str()] pinpad:selected->isPinpad()]; + for (int retriesLeft = selected.retry; retriesLeft > 0; ) { + + CFDataRef data = CFDataCreateWithBytesNoCopy(nil, selected.cert.data(), selected.cert.size(), kCFAllocatorNull); + SecCertificateRef cert = SecCertificateCreateWithData(nil, data); + CFRelease(data); + NSDictionary *dict = CFBridgingRelease(SecCertificateCopyValues(cert, nil, nil)); + CFRelease(cert); + + NSNumber *ku = dict[(__bridge NSString*)kSecOIDKeyUsage][(__bridge NSString*)kSecPropertyKeyValue]; + NSString *cn = [NSString string]; + for (NSDictionary *item in dict[(__bridge NSString*)kSecOIDX509V1SubjectName][(__bridge NSString*)kSecPropertyKeyValue]) { + if ([item[(__bridge NSString*)kSecPropertyKeyLabel] isEqualToString:(__bridge NSString*)kSecOIDCommonName]) { + cn = item[(__bridge NSString*)kSecPropertyKeyValue]; + } + } + + NSString *label = [NSString string]; + if (ku.unsignedIntValue & kSecKeyUsageNonRepudiation) { + label = [_L(selected.pinpad ? "sign PIN pinpad" : "sign PIN") stringByReplacingOccurrencesOfString:@"PIN" withString:@(p11.signPINLabel.c_str())]; + } + else { + label = [_L(selected.pinpad ? "auth PIN pinpad" : "auth PIN") stringByReplacingOccurrencesOfString:@"PIN" withString:@(p11.authPINLabel.c_str())]; + } + PINPanel *dialog = [[PINPanel alloc] init:label pinpad:selected.pinpad]; if (!dialog) { return @{@"result": @"technical_error"}; } + dialog->minPinLen = selected.minPinLen; + dialog->nameLabel.stringValue = cn; NSDictionary *pinpadresult; std::future future; NSTimer *timer; - if (selected->isPinpad()) { + if (selected.pinpad) { timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:dialog selector:@selector(handleTimerTick:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode]; future = std::async(std::launch::async, [&]() { try { - std::vector signature = selected->sign(hash, nullptr); + std::vector signature = PKCS11CardManager::instance(p11.path)->sign(selected, hash, nullptr); pinpadresult = @{@"signature":@(BinaryUtils::bin2hex(signature).c_str())}; [NSApp stopModal]; } @@ -149,7 +172,6 @@ + (NSDictionary *)show:(NSDictionary*)params cert:(NSString *)cert }); } - dialog->nameLabel.stringValue = @(selected->getCN().c_str()); if (retriesLeft < 3) { dialog->messageField.stringValue = [NSString stringWithFormat:@"%@%@ %u", (isInitialCheck ? @"" : _L("incorrect PIN2")), @@ -171,7 +193,7 @@ + (NSDictionary *)show:(NSDictionary*)params cert:(NSString *)cert return @{@"result": @"user_cancel"}; } - if (selected->isPinpad()) { + if (selected.pinpad) { future.wait(); if (pinpadresult) { return pinpadresult; @@ -179,7 +201,7 @@ + (NSDictionary *)show:(NSDictionary*)params cert:(NSString *)cert } else { try { - std::vector signature = selected->sign(hash, dialog->pinField.stringValue.UTF8String); + std::vector signature = PKCS11CardManager::instance(p11.path)->sign(selected, hash, dialog->pinField.stringValue.UTF8String); return @{@"signature":@(BinaryUtils::bin2hex(signature).c_str())}; } catch(const AuthenticationBadInput &) { @@ -211,7 +233,7 @@ - (void)controlTextDidChange:(NSNotification*)notification; pinField.stringValue = [[pinField.stringValue componentsSeparatedByCharactersInSet: NSCharacterSet.decimalDigitCharacterSet.invertedSet] componentsJoinedByString:@""]; - okButton.enabled = pinField.stringValue.length >= 5; + okButton.enabled = pinField.stringValue.length >= minPinLen; } - (void)handleTimerTick:(NSTimer*)timer diff --git a/host-osx/PINDialog.xib b/host-osx/PINDialog.xib index 7d0155c5..a2281bc9 100644 --- a/host-osx/PINDialog.xib +++ b/host-osx/PINDialog.xib @@ -1,25 +1,9 @@ - - - + + - + + @@ -35,11 +19,11 @@ - + - + @@ -76,7 +60,7 @@ Gw - + @@ -93,7 +77,7 @@ Gw - + @@ -101,7 +85,7 @@ Gw - + @@ -109,7 +93,7 @@ Gw - + diff --git a/host-osx/PINPadDialog.xib b/host-osx/PINPadDialog.xib index d1dd3785..841f8e0e 100644 --- a/host-osx/PINPadDialog.xib +++ b/host-osx/PINPadDialog.xib @@ -1,25 +1,9 @@ - - - + + - + + @@ -27,22 +11,22 @@ - + - + - + - + @@ -50,7 +34,7 @@ - + @@ -58,7 +42,7 @@ - + diff --git a/host-osx/chrome-host.mm b/host-osx/chrome-host.mm index 0ef8004b..d047595a 100644 --- a/host-osx/chrome-host.mm +++ b/host-osx/chrome-host.mm @@ -112,7 +112,7 @@ int main(int argc, const char * argv[]) { result = @{@"result": @"not_allowed"}; } else if([dict[@"type"] isEqualToString:@"CERT"]) { - result = [CertificateSelection show]; + result = [CertificateSelection show:![@"AUTH" isEqualToString:dict[@"filter"]]]; cert = (NSString*)result[@"cert"]; } else if ([dict[@"type"] isEqualToString:@"SIGN"]) { diff --git a/host-shared/Labels.cpp b/host-shared/Labels.cpp index 5846bbbe..bb0a0e18 100644 --- a/host-shared/Labels.cpp +++ b/host-shared/Labels.cpp @@ -45,6 +45,20 @@ void Labels::setLanguage(const std::string &language) { Labels::lstring Labels::get(const std::string &labelKey) const { static const std::map > labels = { + { "auth PIN", { + T("Autentimiseks sisesta PIN:"), + T("For authentication enter PIN:"), + T("Для идентификации введите PIN-код:"), + T("Norėdami patvirtinti tapatybę, įveskite PIN:"), + T("Lai autentificētos, ievadi PIN:"), + } }, + { "auth PIN pinpad", { + T("Autentimiseks sisesta PIN kaardilugeja sõrmistikult"), + T("For authentication enter PIN from PIN pad"), + T("Для идентификации введите PIN-код при помощи клавиатуры"), + T("Norėdami patvirtinti tapatybę, įveskite PIN, pasinaudodami klaviatūra"), + T("Lai autentificētos, ievadi PIN no PIN ievades ierīces"), + } }, { "sign PIN", { T("Allkirjastamiseks sisesta PIN:"), T("For signing enter PIN:"), @@ -73,13 +87,6 @@ Labels::lstring Labels::get(const std::string &labelKey) const { T("Neteisingas PIN! "), T("Nepareizs PIN! "), } }, - { "signing", { - T("Allkirjastamine"), - T("Signing"), - T("Подписание"), - T("Pasirašymas"), - T("Parakstīšana"), - } }, { "PIN2 blocked", { T("PIN2 blokeeritud, ei saa allkirjastada!"), T("PIN2 blocked, cannot sign!"), diff --git a/host-shared/PKCS11CardManager.h b/host-shared/PKCS11CardManager.h index 4b7166f9..6f37f819 100644 --- a/host-shared/PKCS11CardManager.h +++ b/host-shared/PKCS11CardManager.h @@ -18,8 +18,6 @@ #pragma once -//TODO: use polymorphism for Windows/Linux/OSX specifics. This is such an ifndef mess - #include "pkcs11.h" #include "Logger.h" @@ -31,16 +29,9 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX -#include "ContextMaintainer.h" -#include "BinaryUtils.h" #include //Using afx.h instead of windows.h because of MFC -#include -#elif defined(__APPLE__) -#include -#include #else #include -#include #endif #define BINARY_SHA1_LENGTH 20 @@ -90,19 +81,10 @@ class PKCS11CardManager { private: #ifdef _WIN32 HINSTANCE library = 0; - PCCERT_CONTEXT cert = NULL; -#elif defined(__APPLE__) - void *library = nullptr; - SecCertificateRef cert = nullptr; #else void *library = nullptr; - X509 *cert = nullptr; #endif CK_FUNCTION_LIST_PTR fl = nullptr; - CK_TOKEN_INFO tokenInfo; - CK_SESSION_HANDLE session = 0; - std::vector signCert; - size_t certIndex = 0; template void Call(const char *file, int line, const char *function, Func func, Args... args) const @@ -127,7 +109,7 @@ class PKCS11CardManager { } } - std::vector findObject(CK_OBJECT_CLASS objectClass, CK_ULONG max = 2) const { + std::vector findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS objectClass, CK_ULONG max = 2) const { if (!fl) { throw std::runtime_error("PKCS11 is not loaded"); } @@ -141,95 +123,12 @@ class PKCS11CardManager { return objectHandle; } - bool isSignCertificate(const std::vector &certificateCandidate) { -#ifdef _WIN32 - if (ContextMaintainer::isSelectedCertificate(BinaryUtils::bin2hex(certificateCandidate))) { - //Can only be true when certificate has already been chosen - return true; - } - bool validForSigning = false; - if (cert = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certificateCandidate.data(), certificateCandidate.size())) { - _log("new certificate handle created."); - BYTE keyUsage; - CertGetIntendedKeyUsage(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert->pCertInfo, &keyUsage, 1); - if ((keyUsage & CERT_NON_REPUDIATION_KEY_USAGE) && (CertVerifyTimeValidity(NULL, cert->pCertInfo) == 0)) { - validForSigning = true; - } - } - return validForSigning; -#elif defined(__APPLE__) - CFDataRef data = CFDataCreateWithBytesNoCopy(nil, certificateCandidate.data(), certificateCandidate.size(), kCFAllocatorNull); - SecCertificateRef cert = SecCertificateCreateWithData(nil, data); - CFRelease(data); - NSDictionary *dict = CFBridgingRelease(SecCertificateCopyValues(cert, nil, nil)); - CFRelease(cert); - NSDictionary *bc = dict[(__bridge NSString*)kSecOIDBasicConstraints][(__bridge NSString*)kSecPropertyKeyValue][1]; - NSNumber *ku = dict[(__bridge NSString*)kSecOIDKeyUsage][(__bridge NSString*)kSecPropertyKeyValue]; - const bool isCa = [@"YES" isEqualToString:bc[(__bridge NSString*)kSecPropertyKeyValue]]; - const bool isNonRepudiation = ku.unsignedIntValue & kSecKeyUsageNonRepudiation; - _log("non repudiation && not ca: %s", isNonRepudiation && !isCa ? "true" : "false"); - return isNonRepudiation && !isCa; -#else - const unsigned char *p = certificateCandidate.data(); - X509 *cert = d2i_X509(NULL, &p, certificateCandidate.size()); - ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING*)X509_get_ext_d2i(cert, NID_key_usage, 0, 0); - BASIC_CONSTRAINTS * cons = (BASIC_CONSTRAINTS*)X509_get_ext_d2i(cert, NID_basic_constraints, 0, 0); - const int keyUsageNonRepudiation = 1; - const bool isNonRepudiation = ASN1_BIT_STRING_get_bit(keyusage, keyUsageNonRepudiation); - const bool isCa = cons && cons->ca > 0; - ASN1_BIT_STRING_free(keyusage); - BASIC_CONSTRAINTS_free(cons); - X509_free(cert); - _log("non repudiation && not ca: %s", isNonRepudiation && !isCa ? "true" : "false"); - return isNonRepudiation && !isCa; -#endif - } - - void findSigningCertificate() { - std::vector certificateObjectHandle = findObject(CKO_CERTIFICATE); - size_t certificateCount = certificateObjectHandle.size(); - _log("certificate count: %i", certificateCount); - if (certificateObjectHandle.empty()) { - throw std::runtime_error("Could not read cert"); - } - - for (size_t i = 0; i < certificateCount; i++) { - _log("check cert %i", i); - std::vector certCandidate; - CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0}; - C(GetAttributeValue, session, certificateObjectHandle[i], &attribute, 1); - certCandidate.resize(attribute.ulValueLen, 0); - attribute.pValue = certCandidate.data(); - C(GetAttributeValue, session, certificateObjectHandle[i], &attribute, 1); - if (isSignCertificate(certCandidate)) { - signCert = certCandidate; - certIndex = i; -#ifdef __APPLE__ - CFDataRef data = CFDataCreateWithBytesNoCopy(nil, signCert.data(), signCert.size(), kCFAllocatorNull); - cert = SecCertificateCreateWithData(nil, data); - CFRelease(data); -#elif !defined(_WIN32) - const unsigned char *p = signCert.data(); - cert = d2i_X509(NULL, &p, signCert.size()); -#endif - break; - } - } - } - - PKCS11CardManager(CK_SLOT_ID slotID, CK_FUNCTION_LIST_PTR fl) : fl(fl) { - C(GetTokenInfo, slotID, &tokenInfo); - C(OpenSession, slotID, CKF_SERIAL_SESSION, nullptr, nullptr, &session); - findSigningCertificate(); - } - PKCS11CardManager(const std::string &module) { CK_C_GetFunctionList C_GetFunctionList = nullptr; #ifdef _WIN32 library = LoadLibraryA(module.c_str()); if (library) - _log("library loaded"); - C_GetFunctionList = CK_C_GetFunctionList(GetProcAddress(library, "C_GetFunctionList")); + C_GetFunctionList = CK_C_GetFunctionList(GetProcAddress(library, "C_GetFunctionList")); #else library = dlopen(module.c_str(), RTLD_LOCAL | RTLD_NOW); if (library) @@ -237,7 +136,7 @@ class PKCS11CardManager { #endif if (!C_GetFunctionList) { - _log("Function List not loaded"); + _log("Function List not loaded %s", module.c_str()); throw std::runtime_error("PKCS11 is not loaded"); } Call(__FILE__, __LINE__, "C_GetFunctionList", C_GetFunctionList, &fl); @@ -253,18 +152,6 @@ class PKCS11CardManager { } ~PKCS11CardManager() { - if (session) - C(CloseSession, session); -#ifdef _WIN32 - if (cert) - CertFreeCertificateContext(cert); -#elif defined(__APPLE__) - if (cert) - CFRelease(cert); -#else - if (cert) - X509_free(cert); -#endif if (!library) return; C(Finalize, nullptr); @@ -275,7 +162,17 @@ class PKCS11CardManager { #endif } - std::vector getAvailableTokens() const { + struct Token { + std::string label; + CK_SLOT_ID slotID; + std::vector cert; + size_t index; + int retry; + bool pinpad; + unsigned long minPinLen, maxPinLen; + }; + + std::vector tokens() const { if (!fl) { throw std::runtime_error("PKCS11 is not loaded"); } @@ -285,29 +182,54 @@ class PKCS11CardManager { std::vector slotIDs(slotCount, 0); C(GetSlotList, CK_TRUE, slotIDs.data(), &slotCount); std::reverse(slotIDs.begin(), slotIDs.end()); - return slotIDs; - } - PKCS11CardManager *getManagerForReader(CK_SLOT_ID slotId) { - if (!fl) { - _log("PKCS11 is not loaded"); - throw std::runtime_error("PKCS11 is not loaded"); + std::vector result; + for (CK_SLOT_ID slotID : slotIDs) + { + CK_TOKEN_INFO tokenInfo; + C(GetTokenInfo, slotID, &tokenInfo); + CK_SESSION_HANDLE session = 0; + C(OpenSession, slotID, CKF_SERIAL_SESSION, nullptr, nullptr, &session); + + std::vector objs = findObject(session, CKO_CERTIFICATE); + for (size_t i = 0; i < objs.size(); ++i) { + CK_ATTRIBUTE attribute = { CKA_VALUE, nullptr, 0 }; + C(GetAttributeValue, session, objs[i], &attribute, 1); + std::vector cert(attribute.ulValueLen, 0); + attribute.pValue = cert.data(); + C(GetAttributeValue, session, objs[i], &attribute, 1); + result.push_back({ std::string((const char*)tokenInfo.label, sizeof(tokenInfo.label)), slotID, cert, i, + [&] { + if (tokenInfo.flags & CKF_USER_PIN_LOCKED) return 0; + if (tokenInfo.flags & CKF_USER_PIN_FINAL_TRY) return 1; + if (tokenInfo.flags & CKF_USER_PIN_COUNT_LOW) return 2; + return 3; + }(), + bool(tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH), + tokenInfo.ulMinPinLen, + tokenInfo.ulMaxPinLen, + }); + } + + C(CloseSession, session); } - return new PKCS11CardManager(slotId, fl); + return result; } - std::vector sign(const std::vector &hash, const char *pin) const { + std::vector sign(const Token &token, const std::vector &hash, const char *pin) const { if (!fl) { throw std::runtime_error("PKCS11 is not loaded"); } + CK_SESSION_HANDLE session = 0; + C(OpenSession, token.slotID, CKF_SERIAL_SESSION, nullptr, nullptr, &session); C(Login, session, CKU_USER, (unsigned char*)pin, pin ? strlen(pin) : 0); - std::vector privateKeyHandle = findObject(CKO_PRIVATE_KEY); + std::vector privateKeyHandle = findObject(session, CKO_PRIVATE_KEY); if (privateKeyHandle.empty()) { throw std::runtime_error("Could not read private key"); } CK_MECHANISM mechanism = {CKM_RSA_PKCS, 0, 0}; - _log("found %i private keys in slot, using key in position %i", privateKeyHandle.size(), certIndex); - C(SignInit, session, &mechanism, privateKeyHandle[certIndex]); + _log("found %i private keys in slot, using key in position %i", privateKeyHandle.size(), token.index); + C(SignInit, session, &mechanism, privateKeyHandle[token.index]); std::vector hashWithPadding; switch (hash.size()) { case BINARY_SHA1_LENGTH: @@ -334,116 +256,8 @@ class PKCS11CardManager { std::vector signature(signatureLength, 0); C(Sign, session, hashWithPadding.data(), hashWithPadding.size(), signature.data(), &signatureLength); C(Logout, session); + C(CloseSession, session); return signature; } - - bool isPinpad() const { - return tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH; - } - - int getPIN2RetryCount() const { - if (tokenInfo.flags & CKF_USER_PIN_LOCKED) return 0; - if (tokenInfo.flags & CKF_USER_PIN_FINAL_TRY) return 1; - if (tokenInfo.flags & CKF_USER_PIN_COUNT_LOW) return 2; - return 3; - } - - std::vector getSignCert() const { - return signCert; - } - - bool hasSignCert() { - return !signCert.empty(); - } - -#ifdef __APPLE__ - std::string getCN() const { - if (!cert) { - throw std::runtime_error("Could not parse cert"); - } - std::string result; - CFStringRef commonName = nil; - if (SecCertificateCopyCommonName(cert, &commonName)) - return result; - NSString *cn = CFBridgingRelease(commonName); - result = cn.UTF8String; - return result; - } - - std::string getType() const { - if (!cert) { - throw std::runtime_error("Could not parse cert"); - } - - NSArray *keys = @[(__bridge NSString*)kSecOIDX509V1SubjectName]; - NSDictionary *dict = CFBridgingRelease(SecCertificateCopyValues(cert, (__bridge CFArrayRef)keys, nil)); - if (!dict) { - throw std::runtime_error("Could not parse cert"); - } - - std::string result; - for (NSDictionary *item in dict[(__bridge NSString*)kSecOIDX509V1SubjectName][(__bridge NSString*)kSecPropertyKeyValue]) { - if ([item[(__bridge NSString*)kSecPropertyKeyLabel] isEqualToString:(__bridge NSString*)kSecOIDOrganizationName]) { - NSString *value = item[(__bridge NSString*)kSecPropertyKeyValue]; - result = value.UTF8String; - return result; - } - } - return result; - } - - std::string getValidTo() const { - if (!cert) { - throw std::runtime_error("Could not parse cert"); - } - - NSArray *keys = @[(__bridge NSString*)kSecOIDX509V1ValidityNotAfter]; - NSDictionary *dict = CFBridgingRelease(SecCertificateCopyValues(cert, (__bridge CFArrayRef)keys, nil)); - if (!dict) { - throw std::runtime_error("Could not parse cert"); - } - - NSDateComponents *components = [[NSDateComponents alloc] init]; - components.year = 2001; - NSDateFormatter *asn1 = [[NSDateFormatter alloc] init]; - asn1.dateFormat = @"yyyyMMddHHmmss'Z'"; - asn1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; - - NSNumber *na = dict[(__bridge NSString*)kSecOIDX509V1ValidityNotAfter][(__bridge NSString*)kSecPropertyKeyValue]; - NSDate *date = [NSDate dateWithTimeInterval:na.intValue sinceDate:[NSCalendar.currentCalendar dateFromComponents:components]]; - std::string result; - result = [asn1 stringFromDate:date].UTF8String; - return result; - } -#elif !defined(_WIN32) - std::string getSubjectX509Name(const std::string &object) const { - if (!cert) { - throw std::runtime_error("Could not parse cert"); - } - std::string X509Value(1024, 0); - int length = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), OBJ_txt2nid(object.c_str()), &X509Value[0], int(X509Value.size())); - X509Value.resize(std::max(0, length)); - _log("%s length is %i, %s", object.c_str(), length, X509Value.c_str()); - return X509Value; - } - - std::string getCN() const { - return getSubjectX509Name("commonName"); - } - - std::string getType() const { - return getSubjectX509Name("organizationName"); - } - - std::string getValidTo() const { - if (!cert) { - throw std::runtime_error("Could not parse cert"); - } - ASN1_GENERALIZEDTIME *gt = ASN1_TIME_to_generalizedtime(X509_get_notAfter(cert), nullptr); - std::string timeAsString((const char *) gt->data, gt->length); - ASN1_GENERALIZEDTIME_free(gt); - return timeAsString; - } -#endif }; diff --git a/host-shared/PKCS11Path.cpp b/host-shared/PKCS11Path.cpp index 954e747e..ca3fd54d 100644 --- a/host-shared/PKCS11Path.cpp +++ b/host-shared/PKCS11Path.cpp @@ -122,33 +122,33 @@ PKCS11Path::Params PKCS11Path::getPkcs11ModulePath() { #endif static const std::map m = { #ifdef _WIN32 - {"3BFD1800008031FE4553434536302D43443134352D46CD", {"C:\\Windows\\System32\\aetpkss1.dll", "PIN"}}, + {"3BFD1800008031FE4553434536302D43443134352D46CD", {"C:\\Windows\\System32\\aetpkss1.dll", "PIN", "PIN"}}, #else - {"3BFE9400FF80B1FA451F034573744549442076657220312E3043", {estPath, "PIN2"}}, - {"3B6E00FF4573744549442076657220312E30", {estPath, "PIN2"}}, - {"3BDE18FFC080B1FE451F034573744549442076657220312E302B", {estPath, "PIN2"}}, - {"3B5E11FF4573744549442076657220312E30", {estPath, "PIN2"}}, - {"3B6E00004573744549442076657220312E30", {estPath, "PIN2"}}, + {"3BFE9400FF80B1FA451F034573744549442076657220312E3043", {estPath, "PIN1", "PIN2"}}, + {"3B6E00FF4573744549442076657220312E30", {estPath, "PIN1", "PIN2"}}, + {"3BDE18FFC080B1FE451F034573744549442076657220312E302B", {estPath, "PIN1", "PIN2"}}, + {"3B5E11FF4573744549442076657220312E30", {estPath, "PIN1", "PIN2"}}, + {"3B6E00004573744549442076657220312E30", {estPath, "PIN1", "PIN2"}}, - {"3BFE1800008031FE454573744549442076657220312E30A8", {estPath, "PIN2"}}, - {"3BFE1800008031FE45803180664090A4561B168301900086", {estPath, "PIN2"}}, - {"3BFE1800008031FE45803180664090A4162A0083019000E1", {estPath, "PIN2"}}, - {"3BFE1800008031FE45803180664090A4162A00830F9000EF", {estPath, "PIN2"}}, + {"3BFE1800008031FE454573744549442076657220312E30A8", {estPath, "PIN1", "PIN2"}}, + {"3BFE1800008031FE45803180664090A4561B168301900086", {estPath, "PIN1", "PIN2"}}, + {"3BFE1800008031FE45803180664090A4162A0083019000E1", {estPath, "PIN1", "PIN2"}}, + {"3BFE1800008031FE45803180664090A4162A00830F9000EF", {estPath, "PIN1", "PIN2"}}, - {"3BF9180000C00A31FE4553462D3443432D303181", {estPath, "PIN2"}}, - {"3BF81300008131FE454A434F5076323431B7", {estPath, "PIN2"}}, - {"3BFA1800008031FE45FE654944202F20504B4903", {estPath, "PIN2"}}, - {"3BFE1800008031FE45803180664090A4162A00830F9000EF", {estPath, "PIN2"}}, + {"3BF9180000C00A31FE4553462D3443432D303181", {estPath, "PIN1", "PIN2"}}, + {"3BF81300008131FE454A434F5076323431B7", {estPath, "PIN1", "PIN2"}}, + {"3BFA1800008031FE45FE654944202F20504B4903", {estPath, "PIN1", "PIN2"}}, + {"3BFE1800008031FE45803180664090A4162A00830F9000EF", {estPath, "PIN1", "PIN2"}}, - {"3BDD18008131FE45904C41545649412D65494490008C", {latPath, "PIN2"}}, + {"3BDD18008131FE45904C41545649412D65494490008C", {latPath, "PIN1", "PIN2"}}, - {"3B7B940000806212515646696E454944", {finPath, "PIN2"}}, + {"3B7B940000806212515646696E454944", {finPath, "PIN1", "PIN2"}}, #endif - {"3BF81300008131FE45536D617274417070F8", {litPath, "PIN"}}, - {"3B7D94000080318065B08311C0A983009000", {litPath, "PIN"}}, - {"3B7D94000080318065B0831100C883009000", {litPath, "PIN"}}, - {"3B9F9681B1FE451F070064051EB20031B0739621DB00900050", {litPath, "PIN"}}, - {"3B9F90801FC30068104405014649534531C800000000", {litPath, "PIN"}}, + {"3BF81300008131FE45536D617274417070F8", {litPath, "PIN", "PIN"}}, + {"3B7D94000080318065B08311C0A983009000", {litPath, "PIN", "PIN"}}, + {"3B7D94000080318065B0831100C883009000", {litPath, "PIN", "PIN"}}, + {"3B9F9681B1FE451F070064051EB20031B0739621DB00900050", {litPath, "PIN", "PIN"}}, + {"3B9F90801FC30068104405014649534531C800000000", {litPath, "PIN", "PIN"}}, }; for (const std::string &atr : atrList()) { diff --git a/host-shared/PKCS11Path.h b/host-shared/PKCS11Path.h index 3822e534..27141860 100644 --- a/host-shared/PKCS11Path.h +++ b/host-shared/PKCS11Path.h @@ -28,7 +28,7 @@ class PKCS11Path { struct Params { std::string path; - std::string signPINLabel; + std::string authPINLabel, signPINLabel; }; static Params getPkcs11ModulePath(); }; diff --git a/host-windows/CertificateSelector.cpp b/host-windows/CertificateSelector.cpp index 54485a13..af2a502b 100644 --- a/host-windows/CertificateSelector.cpp +++ b/host-windows/CertificateSelector.cpp @@ -71,7 +71,7 @@ CertificateSelector* CertificateSelector::createCertificateSelector() return new NativeCertificateSelector(); } -std::vector CertificateSelector::showDialog(HCERTSTORE store, PFNCFILTERPROC filter_proc) +std::vector CertificateSelector::showDialog(HCERTSTORE store, PFNCFILTERPROC filter_proc) const { std::wstring title = Labels::l10n.get("select certificate"); std::wstring text = Labels::l10n.get("cert info"); diff --git a/host-windows/CertificateSelector.h b/host-windows/CertificateSelector.h index d82aeb11..f0c86389 100644 --- a/host-windows/CertificateSelector.h +++ b/host-windows/CertificateSelector.h @@ -28,9 +28,9 @@ class CertificateSelector { static CertificateSelector* createCertificateSelector(); virtual ~CertificateSelector() = default; - virtual std::vector getCert() throw(UserCancelledException, TechnicalException) = 0; + virtual std::vector getCert(bool forSigning) const throw(UserCancelledException, TechnicalException) = 0; protected: CertificateSelector() = default; - std::vector showDialog(HCERTSTORE store, PFNCFILTERPROC filter_proc); + std::vector showDialog(HCERTSTORE store, PFNCFILTERPROC filter_proc) const; }; \ No newline at end of file diff --git a/host-windows/DialogManager.cpp b/host-windows/DialogManager.cpp deleted file mode 100644 index 6eea2bb7..00000000 --- a/host-windows/DialogManager.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Chrome Token Signing Native Host -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "DialogManager.h" -#include "PinDialog.h" -#include "HostExceptions.h" -#include "Labels.h" -#include "Logger.h" - -using namespace std; - -DialogManager::DialogManager() { - HMODULE hModule = ::GetModuleHandle(NULL); - if (hModule == NULL) { - _log("MFC initialization failed. Module handle is null"); - throw TechnicalException("MFC initialization failed. Module handle is null"); - } - // initialize MFC - if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0)) { - _log("MFC initialization failed"); - throw TechnicalException("MFC initialization failed"); - } -} - -char* DialogManager::getPin() { - _log("Showing pin entry dialog"); - PinDialog dialog; - if (dialog.DoModal() != IDOK) { - _log("User cancelled"); - throw UserCancelledException(); - } - return dialog.getPin(); -} - -void DialogManager::showWrongPinError(int triesLeft) { - _log("Showing incorrect pin error dialog, %i tries left", triesLeft); - wstring msg = Labels::l10n.get("tries left") + L" " + to_wstring(triesLeft); - MessageBox(NULL, msg.c_str(), Labels::l10n.get("incorrect PIN2").c_str(), MB_OK | MB_ICONERROR); -} - -void DialogManager::showPinBlocked() { - _log("Showing pin blocked dialog"); - MessageBox(NULL, Labels::l10n.get("PIN2 blocked").c_str(), L"PIN Blocked", MB_OK | MB_ICONERROR); -} diff --git a/host-windows/DialogManager.h b/host-windows/DialogManager.h deleted file mode 100644 index b58b350f..00000000 --- a/host-windows/DialogManager.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Chrome Token Signing Native Host -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#pragma once - -#include - -class DialogManager -{ -public: - DialogManager(); - char* getPin(); - void showWrongPinError(int triesLeft); - void showPinBlocked(); -}; - diff --git a/host-windows/NativeCertificateSelector.cpp b/host-windows/NativeCertificateSelector.cpp index 9a030008..93658dcf 100644 --- a/host-windows/NativeCertificateSelector.cpp +++ b/host-windows/NativeCertificateSelector.cpp @@ -17,51 +17,49 @@ */ #include "NativeCertificateSelector.h" -#include "BinaryUtils.h" #include "HostExceptions.h" using namespace std; - -BOOL isCardInReader(PCCERT_CONTEXT certContext) { +static bool isValid(PCCERT_CONTEXT certContext, bool forSigning) { DWORD flags = CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG; NCRYPT_KEY_HANDLE key = 0; DWORD spec = 0; - BOOL ncrypt = FALSE; - CryptAcquireCertificatePrivateKey(certContext, flags, 0, &key, &spec, &ncrypt); + BOOL freeKey = FALSE; + CryptAcquireCertificatePrivateKey(certContext, flags, 0, &key, &spec, &freeKey); if (!key) { return FALSE; } - if (ncrypt) { - NCryptFreeObject(key); + switch (spec) + { + case CERT_NCRYPT_KEY_SPEC: + if (freeKey) + NCryptFreeObject(key); + break; + case AT_KEYEXCHANGE: + case AT_SIGNATURE: + default: + if (freeKey) + CryptReleaseContext(key, 0); + break; } - return TRUE; -} - -BOOL WINAPI isValidForSigning(PCCERT_CONTEXT certContext) { - BYTE keyUsage; + BYTE keyUsage = 0; CertGetIntendedKeyUsage(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certContext->pCertInfo, &keyUsage, 1); - if (!(keyUsage & CERT_NON_REPUDIATION_KEY_USAGE)) { - return FALSE; - } - if (CertVerifyTimeValidity(NULL, certContext->pCertInfo) != 0) { - return FALSE; - } - - return isCardInReader(certContext); + return CertVerifyTimeValidity(NULL, certContext->pCertInfo) == 0 && ( + (forSigning && keyUsage & CERT_NON_REPUDIATION_KEY_USAGE) || + (!forSigning && keyUsage & CERT_DIGITAL_SIGNATURE_KEY_USAGE)); } -vector NativeCertificateSelector::getCert() { +vector NativeCertificateSelector::getCert(bool forSigning) const { HCERTSTORE store = CertOpenSystemStore(0, L"MY"); - if (!store) - { + if (!store) { throw TechnicalException("Failed to open Cert Store"); } PCCERT_CONTEXT pCertContextForEnumeration = nullptr; int certificatesCount = 0; while (pCertContextForEnumeration = CertEnumCertificatesInStore(store, pCertContextForEnumeration)) { - if (isValidForSigning(pCertContextForEnumeration)) { + if (isValid(pCertContextForEnumeration, forSigning)) { certificatesCount++; } } @@ -73,7 +71,14 @@ vector NativeCertificateSelector::getCert() { throw NoCertificatesException(); } - return showDialog(store, [](PCCERT_CONTEXT certContext, BOOL *pfInitialSelectedCert, void *pvCallbackData) -> BOOL { - return isValidForSigning(certContext); - }); + if (forSigning) { + return showDialog(store, [](PCCERT_CONTEXT certContext, BOOL *pfInitialSelectedCert, void *pvCallbackData) -> BOOL { + return isValid(certContext, true); + }); + } + else { + return showDialog(store, [](PCCERT_CONTEXT certContext, BOOL *pfInitialSelectedCert, void *pvCallbackData) -> BOOL { + return isValid(certContext, false); + }); + } } \ No newline at end of file diff --git a/host-windows/NativeCertificateSelector.h b/host-windows/NativeCertificateSelector.h index bfd2f038..711ebaed 100644 --- a/host-windows/NativeCertificateSelector.h +++ b/host-windows/NativeCertificateSelector.h @@ -22,6 +22,6 @@ class NativeCertificateSelector : public CertificateSelector { public: - NativeCertificateSelector() : CertificateSelector(){} - std::vector getCert() override; + NativeCertificateSelector() = default; + std::vector getCert(bool forSigning) const override; }; \ No newline at end of file diff --git a/host-windows/PKCS11CertificateSelector.cpp b/host-windows/PKCS11CertificateSelector.cpp index f962f7dc..fe98c1eb 100644 --- a/host-windows/PKCS11CertificateSelector.cpp +++ b/host-windows/PKCS11CertificateSelector.cpp @@ -19,74 +19,55 @@ #include "PKCS11CertificateSelector.h" #include "PKCS11CardManager.h" #include "Logger.h" -#include "BinaryUtils.h" #include "HostExceptions.h" #include using namespace std; -void PKCS11CertificateSelector::initialize() { - if (hMemoryStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL)) { +PKCS11CertificateSelector::PKCS11CertificateSelector(const string &_driverPath) + : CertificateSelector() + , driverPath(_driverPath) +{} + +vector PKCS11CertificateSelector::getCert(bool forSigning) const { + HCERTSTORE hMemoryStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL); + if (hMemoryStore) { _log("Opened a memory store."); - } else { + } + else { _log("Error opening a memory store."); throw TechnicalException("Error opening a memory store."); } - fetchAllSigningCertificates(); -} -void PKCS11CertificateSelector::fetchAllSigningCertificates() { try { - unique_ptr manager; - bool certificateAddedToMemoryStore = false; - vector foundTokens = createCardManager()->getAvailableTokens(); - for (auto &token : foundTokens) { - try { - manager.reset(createCardManager()->getManagerForReader(token)); - } - catch (const PKCS11TokenNotRecognized &ex) { - _log("%s", ex.what()); + for (const auto &token : PKCS11CardManager::instance(driverPath)->tokens()) { + PCCERT_CONTEXT cert = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, token.cert.data(), token.cert.size()); + if (!cert) continue; - } - catch (const PKCS11TokenNotPresent &ex) { - _log("%s", ex.what()); + _log("new certificate handle created."); + + BYTE keyUsage = 0; + CertGetIntendedKeyUsage(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert->pCertInfo, &keyUsage, 1); + if (CertVerifyTimeValidity(NULL, cert->pCertInfo) != 0 || + (forSigning && !(keyUsage & CERT_NON_REPUDIATION_KEY_USAGE)) || + (!forSigning && keyUsage & CERT_NON_REPUDIATION_KEY_USAGE)) { + CertFreeCertificateContext(cert); continue; } - if (manager -> hasSignCert()) { - addCertificateToMemoryStore(manager->getSignCert()); - certificateAddedToMemoryStore = true; - } - } - if (foundTokens.size() > 0 && !certificateAddedToMemoryStore) { - throw PKCS11TokenNotRecognized(); + if (CertAddCertificateContextToStore(hMemoryStore, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { + _log("Certificate added to the memory store."); + } + else { + _log("Could not add the certificate to the memory store."); + } } } catch (const std::runtime_error &a) { _log("Technical error: %s", a.what()); + CertCloseStore(hMemoryStore, 0); throw TechnicalException("Error getting certificate manager: " + string(a.what())); } -} - -PKCS11CardManager* PKCS11CertificateSelector::createCardManager() { - if (driverPath.empty()) { - return PKCS11CardManager::instance(); - } - return PKCS11CardManager::instance(driverPath); -} - -void PKCS11CertificateSelector::addCertificateToMemoryStore(std::vector signCert) { - PCCERT_CONTEXT cert = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, signCert.data(), signCert.size()); - if (CertAddCertificateContextToStore(hMemoryStore, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { - _log("Certificate added to the memory store."); - } - else { - _log("Could not add the certificate to the memory store."); - throw TechnicalException("Could not add certificate to the memory store."); - } -} -vector PKCS11CertificateSelector::getCert() -{ return showDialog(hMemoryStore, nullptr); } diff --git a/host-windows/PKCS11CertificateSelector.h b/host-windows/PKCS11CertificateSelector.h index 94da0767..fffd8722 100644 --- a/host-windows/PKCS11CertificateSelector.h +++ b/host-windows/PKCS11CertificateSelector.h @@ -19,23 +19,13 @@ #pragma once #include "CertificateSelector.h" -#include "PKCS11CardManager.h" #include class PKCS11CertificateSelector : public CertificateSelector { public: - PKCS11CertificateSelector(const std::string &_driverPath) : CertificateSelector(){ - driverPath = _driverPath; - initialize(); - } - std::vector getCert() override; + PKCS11CertificateSelector(const std::string &_driverPath); + std::vector getCert(bool forSigning) const override; private: - void initialize(); - void fetchAllSigningCertificates(); - void addCertificateToMemoryStore(std::vector signCert); - PKCS11CardManager* createCardManager(); - - HCERTSTORE hMemoryStore; std::string driverPath; }; \ No newline at end of file diff --git a/host-windows/PinDialog.cpp b/host-windows/PinDialog.cpp index c847d5bf..639e07b2 100644 --- a/host-windows/PinDialog.cpp +++ b/host-windows/PinDialog.cpp @@ -39,7 +39,7 @@ void PinDialog::OnBnClickedOk() { BOOL PinDialog::OnInitDialog() { BOOL result = CDialog::OnInitDialog(); - GetDlgItem(IDC_PIN_MESSAGE)->SetWindowText(_L("sign PIN")); + GetDlgItem(IDC_PIN_MESSAGE)->SetWindowText(label.c_str()); GetDlgItem(IDOK)->SetWindowText(_L("sign")); GetDlgItem(IDCANCEL)->SetWindowText(_L("cancel")); return result; diff --git a/host-windows/PinDialog.h b/host-windows/PinDialog.h index 5968cc7a..11863511 100644 --- a/host-windows/PinDialog.h +++ b/host-windows/PinDialog.h @@ -21,13 +21,14 @@ #include "resource.h" #include +#include class PinDialog : public CDialog { DECLARE_DYNAMIC(PinDialog) public: - PinDialog(CWnd* pParent = NULL) : CDialog(PinDialog::IDD, pParent) {} + PinDialog(const std::wstring &_label, CWnd* pParent = NULL) : CDialog(PinDialog::IDD, pParent), label(_label) {} char* getPin(); afx_msg void OnBnClickedOk(); @@ -40,4 +41,5 @@ class PinDialog : public CDialog private: char* pin; + std::wstring label; }; diff --git a/host-windows/Pkcs11Signer.cpp b/host-windows/Pkcs11Signer.cpp index b6bf8b6f..25e99a87 100644 --- a/host-windows/Pkcs11Signer.cpp +++ b/host-windows/Pkcs11Signer.cpp @@ -18,86 +18,112 @@ #include "Pkcs11Signer.h" #include "PKCS11CardManager.h" +#include "Labels.h" #include "Logger.h" #include "BinaryUtils.h" #include "HostExceptions.h" -#include "DialogManager.h" +#include "PinDialog.h" + +#include using namespace std; Pkcs11Signer::Pkcs11Signer(const string &pkcs11ModulePath, const string &certInHex) : Signer(certInHex) { + HMODULE hModule = ::GetModuleHandle(NULL); + if (hModule == NULL) { + _log("MFC initialization failed. Module handle is null"); + throw TechnicalException("MFC initialization failed. Module handle is null"); + } + // initialize MFC + if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0)) { + _log("MFC initialization failed"); + throw TechnicalException("MFC initialization failed"); + } // Init static card manager pkcs11ModulePath.empty() ? PKCS11CardManager::instance() : PKCS11CardManager::instance(pkcs11ModulePath); } -unique_ptr Pkcs11Signer::getCardManager() { +vector Pkcs11Signer::sign(const vector &digest) { + _log("Signing using PKCS#11 module"); + + PKCS11CardManager::Token selected; try { - for (auto &token : PKCS11CardManager::instance()->getAvailableTokens()) { - try { - unique_ptr manager(PKCS11CardManager::instance()->getManagerForReader(token)); - if (manager->getSignCert() == BinaryUtils::hex2bin(getCertInHex())) - return manager; - } - catch (const PKCS11TokenNotRecognized &ex) { - _log("%s", ex.what()); - } - catch (const PKCS11TokenNotPresent &ex) { - _log("%s", ex.what()); + for (const PKCS11CardManager::Token &token : PKCS11CardManager::instance()->tokens()) { + if (token.cert == BinaryUtils::hex2bin(getCertInHex())) { + selected = token; + break; } } } catch (const runtime_error &a) { - _log("Technical error: %s",a.what()); + _log("Technical error: %s", a.what()); throw TechnicalException("Error getting certificate manager: " + string(a.what())); } - _log("No card manager found for this certificate"); - throw InvalidArgumentException("No card manager found for this certificate"); -} -vector Pkcs11Signer::sign(const vector &digest) { - _log("Signing using PKCS#11 module"); - unique_ptr manager = getCardManager(); - pinTriesLeft = manager->getPIN2RetryCount(); + if (selected.cert.empty()) { + _log("No card manager found for this certificate"); + throw InvalidArgumentException("No card manager found for this certificate"); + } + + pinTriesLeft = selected.retry; try { validatePinNotBlocked(); - char* signingPin = askPin(); - return manager->sign(digest, signingPin); + return PKCS11CardManager::instance()->sign(selected, digest, askPin()); } catch (const AuthenticationError &) { _log("Wrong pin"); + pinTriesLeft--; handleWrongPinEntry(); return sign(digest); } catch (const AuthenticationBadInput &) { _log("Bad pin input"); + pinTriesLeft--; handleWrongPinEntry(); return sign(digest); } } char* Pkcs11Signer::askPin() { - char* signingPin = dialog.getPin(); - if (strlen(signingPin) < 4) { + _log("Showing pin entry dialog"); + wstring label = Labels::l10n.get("sign PIN"); + vector data = BinaryUtils::hex2bin(getCertInHex()); + PCCERT_CONTEXT cert = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, data.data(), data.size()); + if (cert) { + BYTE keyUsage = 0; + CertGetIntendedKeyUsage(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert->pCertInfo, &keyUsage, 1); + if (!(keyUsage & CERT_NON_REPUDIATION_KEY_USAGE)) + label = Labels::l10n.get("auth PIN"); + CertFreeCertificateContext(cert); + } + + PinDialog dialog(label); + if (dialog.DoModal() != IDOK) { + _log("User cancelled"); + throw UserCancelledException(); + } + if (strlen(dialog.getPin()) < 4) { _log("Pin is too short"); - dialog.showWrongPinError(pinTriesLeft); + handleWrongPinEntry(); return askPin(); } - return signingPin; + return dialog.getPin(); } void Pkcs11Signer::validatePinNotBlocked() { if (pinTriesLeft <= 0) { _log("PIN2 retry count is zero"); - dialog.showPinBlocked(); + MessageBox(NULL, Labels::l10n.get("PIN2 blocked").c_str(), L"PIN Blocked", MB_OK | MB_ICONERROR); throw PinBlockedException(); } } void Pkcs11Signer::handleWrongPinEntry() { - pinTriesLeft--; validatePinNotBlocked(); - dialog.showWrongPinError(pinTriesLeft); + _log("Showing incorrect pin error dialog, %i tries left", pinTriesLeft); + wstring msg = Labels::l10n.get("tries left") + L" " + to_wstring(pinTriesLeft); + MessageBox(NULL, msg.c_str(), Labels::l10n.get("incorrect PIN2").c_str(), MB_OK | MB_ICONERROR); } \ No newline at end of file diff --git a/host-windows/Pkcs11Signer.h b/host-windows/Pkcs11Signer.h index 40f24d61..b176fbad 100644 --- a/host-windows/Pkcs11Signer.h +++ b/host-windows/Pkcs11Signer.h @@ -20,9 +20,6 @@ #include "Signer.h" #include "PKCS11CardManager.h" -#include "DialogManager.h" - -#include class Pkcs11Signer : public Signer { public: @@ -30,8 +27,6 @@ class Pkcs11Signer : public Signer { std::vector sign(const std::vector &digest) override; private: int pinTriesLeft; - DialogManager dialog; - std::unique_ptr getCardManager(); void validatePinNotBlocked(); char* askPin(); void handleWrongPinEntry(); diff --git a/host-windows/RequestHandler.cpp b/host-windows/RequestHandler.cpp index 85c13622..5dff590e 100644 --- a/host-windows/RequestHandler.cpp +++ b/host-windows/RequestHandler.cpp @@ -41,7 +41,7 @@ jsonxx::Object RequestHandler::handleRequest() { handleVersionRequest(); } else if (type == "CERT") { - handleCertRequest(); + handleCertRequest(!jsonRequest.has("filter") || jsonRequest.get("filter") != "AUTH"); } else if (type == "SIGN" && hasSignRequestArguments()) { handleSignRequest(); @@ -112,10 +112,10 @@ void RequestHandler::handleVersionRequest() { jsonResponse << "version" << VERSION; } -void RequestHandler::handleCertRequest() { +void RequestHandler::handleCertRequest(bool forSigning) { validateSecureOrigin(); - CertificateSelector * certificateSelector = CertificateSelector::createCertificateSelector(); - string selectedCert = BinaryUtils::bin2hex(certificateSelector->getCert()); + CertificateSelector *certificateSelector = CertificateSelector::createCertificateSelector(); + string selectedCert = BinaryUtils::bin2hex(certificateSelector->getCert(forSigning)); ContextMaintainer::saveCertificate(selectedCert); jsonResponse << "cert" << selectedCert; } diff --git a/host-windows/RequestHandler.h b/host-windows/RequestHandler.h index a272f7f8..626883b6 100644 --- a/host-windows/RequestHandler.h +++ b/host-windows/RequestHandler.h @@ -32,7 +32,7 @@ class RequestHandler { jsonxx::Object jsonResponse; void handleVersionRequest(); - void handleCertRequest(); + void handleCertRequest(bool forSigning); void handleSignRequest(); void handleException(const BaseException &e); jsonxx::Object notAllowed(); diff --git a/host-windows/Resource.rc b/host-windows/Resource.rc index 6f23fbc6..bcc4a021 100644 Binary files a/host-windows/Resource.rc and b/host-windows/Resource.rc differ diff --git a/host-windows/host-windows.vcxproj b/host-windows/host-windows.vcxproj index 51d5654d..188b0f39 100644 --- a/host-windows/host-windows.vcxproj +++ b/host-windows/host-windows.vcxproj @@ -101,7 +101,6 @@ - @@ -122,7 +121,6 @@ - diff --git a/host-windows/host-windows.vcxproj.filters b/host-windows/host-windows.vcxproj.filters index 4763c3c6..2c88a21b 100644 --- a/host-windows/host-windows.vcxproj.filters +++ b/host-windows/host-windows.vcxproj.filters @@ -54,9 +54,6 @@ Header Files - - Header Files - Header Files @@ -110,9 +107,6 @@ Source Files - - Source Files - Source Files