Skip to content
This repository has been archived by the owner on Sep 1, 2023. It is now read-only.

Commit

Permalink
Authentication support
Browse files Browse the repository at this point in the history
Signed-off-by: Raul Metsma <raul@metsma.ee>
  • Loading branch information
metsma authored and Telgat committed Jul 6, 2017
1 parent a3ae695 commit f108f1f
Show file tree
Hide file tree
Showing 34 changed files with 395 additions and 629 deletions.
2 changes: 1 addition & 1 deletion extension/content.js
Expand Up @@ -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\
Expand Down
2 changes: 1 addition & 1 deletion 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",
Expand Down
2 changes: 1 addition & 1 deletion extension/page.js
Expand Up @@ -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);
};
Expand Down
37 changes: 27 additions & 10 deletions host-linux/CertificateSelection.h
Expand Up @@ -29,32 +29,49 @@
#include <QLabel>
#include <QPushButton>
#include <QSslCertificate>
#include <QSslCertificateExtension>
#include <QTreeWidget>
#include <QVBoxLayout>

#include <openssl/x509v3.h>

class CertificateSelection: public QDialog {
public:
static QVariantMap getCert()
static QVariantMap getCert(bool forSigning)
{
try {
QList<QStringList> 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"}};
Expand Down
66 changes: 40 additions & 26 deletions host-linux/Signer.h
Expand Up @@ -30,12 +30,15 @@
#include <QProgressBar>
#include <QPushButton>
#include <QRegExpValidator>
#include <QSslCertificate>
#include <QTimeLine>
#include <QVBoxLayout>

#include <future>
#include <string>

#include <openssl/x509v3.h>

class Signer: public QDialog {
enum {
UserCancel = 0,
Expand All @@ -55,26 +58,39 @@ class Signer: public QDialog {
return {{"result", "invalid_argument"}};
}

std::unique_ptr<PKCS11CardManager> manager;
std::vector<unsigned char> 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("<font color='red'><b>%1%2 %3</b></font>")
Expand All @@ -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<unsigned char> > signature;

if (manager->isPinpad()) {
if (selected.pinpad) {
signature = std::async(std::launch::async, [&](){
std::vector<unsigned char> result;
try {
result = manager->sign(fromHex(hash), nullptr);
result = PKCS11CardManager::instance(p11.path)->sign(selected, fromHex(hash), nullptr);
dialog.accept();
} catch (const AuthenticationError &) {
--retriesLeft;
Expand All @@ -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<unsigned char> result = manager->sign(fromHex(hash),
dialog.pin->text().toUtf8().constData());
if (!selected.pinpad) {
std::vector<unsigned char> result = PKCS11CardManager::instance(p11.path)->sign(
selected, fromHex(hash), dialog.pin->text().toUtf8().constData());
return {{"signature", toHex(result)}};
}
} catch (const AuthenticationBadInput &) {
Expand All @@ -141,16 +157,16 @@ class Signer: public QDialog {
private:
static QByteArray toHex(const std::vector<unsigned char> &data)
{
return QByteArray((const char*)data.data(), data.size()).toHex();
return QByteArray::fromRawData((const char*)data.data(), data.size()).toHex();
}

static std::vector<unsigned char> fromHex(const QString &data)
{
QByteArray bin = QByteArray::fromHex(data.toLatin1());
return std::vector<unsigned char>(bin.constData(), bin.constData() + bin.size());
return std::vector<unsigned char>(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))
Expand All @@ -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();

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion host-linux/chrome-host.cpp
Expand Up @@ -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"}};
Expand Down
2 changes: 1 addition & 1 deletion host-linux/chrome-token-signing.pro
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion host-osx/CertificateSelection.h
Expand Up @@ -20,6 +20,6 @@

@interface CertificateSelection : NSObject

+ (NSDictionary *)show;
+ (NSDictionary *)show:(bool)forSigning;

@end
65 changes: 44 additions & 21 deletions host-osx/CertificateSelection.mm
Expand Up @@ -26,6 +26,7 @@

#import <SecurityInterface/SFCertificatePanel.h>
#import <SecurityInterface/SFCertificateView.h>
#include <Security/Security.h>

#define _L(KEY) @(Labels::l10n.get(KEY).c_str())

Expand All @@ -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;
Expand Down Expand Up @@ -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"};
}
Expand Down

0 comments on commit f108f1f

Please sign in to comment.