more Smart card

- add support to store a certificate on the card
   (did not work with my TCOS card, but in theory....)
 - init PIN via PUK
 - be more robust regarding card changes
 - improve PIN handling
This commit is contained in:
Christian Hohnstaedt 2009-11-03 19:51:37 +01:00 committed by Christian Hohnstaedt
parent 7cf3862467
commit f41411902f
13 changed files with 368 additions and 98 deletions

View File

@ -197,6 +197,10 @@ void db_key::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
if (key->isScard()) {
menu->addAction(tr("Change PIN"), this,
SLOT(changePin()));
menu->addAction(tr("Init PIN with SO PIN (PUK)"), this,
SLOT(initPin()));
menu->addAction(tr("Change SO PIN (PUK)"), this,
SLOT(changeSoPin()));
}
}
menu->exec(e->globalPos());
@ -295,10 +299,43 @@ void db_key::changePin()
scard = static_cast<pki_scard*>(currentIdx.internalPointer());
try {
if (!scard->isScard()) {
throw errorEx(tr("Tried to change password of a smart card"));
throw errorEx(tr("Tried to change PIN of a key"));
}
scard->changePin();
} catch (errorEx &err) {
mainwin->Error(err);
}
}
void db_key::initPin()
{
pki_scard *scard;
if (!currentIdx.isValid())
return;
scard = static_cast<pki_scard*>(currentIdx.internalPointer());
try {
if (!scard->isScard()) {
throw errorEx(tr("Tried to init PIN of a key"));
}
scard->initPin();
} catch (errorEx &err) {
mainwin->Error(err);
}
}
void db_key::changeSoPin()
{
pki_scard *scard;
if (!currentIdx.isValid())
return;
scard = static_cast<pki_scard*>(currentIdx.internalPointer());
try {
if (!scard->isScard()) {
throw errorEx(tr("Tried to change SO PIN of a key"));
}
scard->changeSoPin();
} catch (errorEx &err) {
mainwin->Error(err);
}
}

View File

@ -42,6 +42,8 @@ class db_key: public db_base
void setOwnPass();
void resetOwnPass();
void changePin();
void initPin();
void changeSoPin();
signals:
void delKey(pki_key *delkey);

View File

@ -549,10 +549,10 @@ void db_x509::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
{
QMenu *menu = new QMenu(mainwin);
QMenu *subExport, *subCa;
QAction *itemReq, *itemRevoke, *itemExtend,
*itemTrust;
bool parentCanSign, canSign, hasTemplates, hasPrivkey;
QAction *itemReq, *itemRevoke, *itemExtend, *itemTrust, *itemScard;
bool parentCanSign, canSign, hasTemplates;
currentIdx = index;
pki_key *privkey;
pki_x509 *cert = static_cast<pki_x509*>(index.internalPointer());
@ -567,6 +567,8 @@ void db_x509::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
subExport->addAction(tr("File"), this, SLOT(store()));
itemReq = subExport->addAction(tr("Request"),
this, SLOT(toRequest()));
itemScard = subExport->addAction(tr("Smart Card"),
this, SLOT(toScard()));
menu->addAction(tr("Delete"), this, SLOT(delete_ask()));
itemTrust = menu->addAction(tr("Trust"), this, SLOT(setTrust()));
@ -594,13 +596,14 @@ void db_x509::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
&& (cert->getSigner() != cert));
canSign = cert->canSign();
hasTemplates = mainwin->temps->getDesc().count() > 0 ;
hasPrivkey = cert->getRefKey();
privkey = cert->getRefKey();
itemRevoke->setEnabled(parentCanSign);
itemExtend->setEnabled(parentCanSign);
subCa->setEnabled(canSign);
itemReq->setEnabled(hasPrivkey);
itemReq->setEnabled(privkey);
itemScard->setEnabled(privkey && privkey->isScard());
#if 0
subP7->setEnabled(hasPrivkey);
subP7->setEnabled(privkey);
#endif
}
menu->exec(e->globalPos());
@ -1013,6 +1016,18 @@ void db_x509::toRequest()
}
}
void db_x509::toScard()
{
pki_x509 *cert = static_cast<pki_x509*>(currentIdx.internalPointer());
if (!cert)
return;
try {
cert->store_token();
} catch (errorEx &err) {
mainwin->Error(err);
}
}
void db_x509::caProperties()
{
Ui::CaProperties ui;

View File

@ -66,6 +66,7 @@ class db_x509: public db_x509super
void genCrl();
void caProperties();
void toRequest();
void toScard();
void newCert(pki_temp *);
void newCert(pki_x509req *);
void loadPKCS12();

View File

@ -37,3 +37,22 @@ void pk11_attr_data::load(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj)
pkcs11::pk11error("C_GetAttributeValue(data)", rv); \
}
void pk11_attr_data::setValue(const void *ptr, unsigned long len)
{
if (attr.pValue)
free(attr.pValue);
attr.pValue = malloc(len);
if (!attr.pValue)
throw errorEx("Out of Memory");
memcpy(attr.pValue, ptr, len);
attr.ulValueLen = len;
}
void pk11_attribute::store(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj)
{
CK_RV rv;
rv = pkcs11::p11->C_SetAttributeValue(sess, obj, &attr, 1);
if (rv != CKR_OK)
pkcs11::pk11error("C_SetAttributeValue", rv);
}

View File

@ -32,6 +32,7 @@ public:
{
return &attr;
}
void store(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj);
};
class pk11_attr_ulong: public pk11_attribute
@ -45,7 +46,6 @@ public:
attr.pValue = &value;
attr.ulValueLen = sizeof(value);
}
unsigned long getValue() const
{
return value;
@ -66,13 +66,11 @@ public:
attr.pValue = NULL;
attr.ulValueLen = 0;
}
unsigned long getValue(const unsigned char **ptr)
{
*ptr = (unsigned char*)attr.pValue;
return attr.ulValueLen;
}
~pk11_attr_data()
{
if (attr.pValue)
@ -84,11 +82,11 @@ public:
}
BIGNUM *getBignum() const
{
printf("attr.ulValueLen %lu\n", attr.ulValueLen);
return BN_bin2bn((unsigned char*)attr.pValue,
attr.ulValueLen, NULL);
}
void load(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj);
void setValue(const void *ptr, unsigned long len);
};
#endif

View File

@ -21,17 +21,6 @@ pkcs11::pkcs11()
session = CK_INVALID_HANDLE;
object = CK_INVALID_HANDLE;
slot_id = 0;
init_pkcs11();
}
void pkcs11::init_pkcs11()
{
if (p11) {
CK_RV rv = p11->C_Initialize(NULL);
if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)
pk11error("C_Initialize", rv);
}
}
pkcs11::~pkcs11()
@ -59,27 +48,69 @@ void pkcs11::startSession(unsigned long slot, bool rw)
CK_SLOT_ID *pkcs11::getSlotList(unsigned long *num_slots)
{
CK_RV rv;
CK_SLOT_ID *p11_slots;
CK_SLOT_ID *p11_slots = NULL;
rv = p11->C_GetSlotList(CK_TRUE, NULL, num_slots);
if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL)
pk11error("C_GetSlotList", rv);
p11_slots = (CK_SLOT_ID *)malloc(*num_slots *sizeof(CK_SLOT_ID));
//oom_check(p11_slots);
rv = p11->C_GetSlotList(CK_TRUE, p11_slots, num_slots);
if (rv != CKR_OK)
pk11error("C_GetSlotList", rv);
p11->C_GetSlotList(CK_TRUE, p11_slots, num_slots);
do {
rv = p11->C_GetSlotList(CK_TRUE, p11_slots, num_slots);
if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL)
pk11error("C_GetSlotList", rv);
printf("*num_slots = %d\n", *num_slots);
if (*num_slots == 0)
break;
p11_slots = (CK_SLOT_ID *)realloc(p11_slots,
*num_slots *sizeof(CK_SLOT_ID));
if (!p11_slots)
throw errorEx("C_GetSlotList(Out of Memory)");
} while (rv == CKR_BUFFER_TOO_SMALL);
return p11_slots;
}
void pkcs11::login(unsigned long slot,
unsigned char *pin, unsigned long pinlen, bool so)
void pkcs11::logout()
{
CK_RV rv;
rv = p11->C_Logout(session);
if (rv != CKR_OK)
pk11error("C_Logout", rv);
}
bool pkcs11::needsLogin(bool so)
{
CK_SESSION_INFO sinfo;
CK_RV rv;
rv = p11->C_GetSessionInfo(session, &sinfo);
if (rv != CKR_OK)
pk11error("C_GetSessionInfo", rv);
switch (sinfo.state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RW_PUBLIC_SESSION:
return true;
case CKS_RW_SO_FUNCTIONS:
if (so) {
return false;
} else {
logout();
return true;
}
case CKS_RO_USER_FUNCTIONS:
case CKS_RW_USER_FUNCTIONS:
if (!so) {
return false;
} else {
logout();
return true;
}
}
return true;
}
void pkcs11::login(unsigned char *pin, unsigned long pinlen, bool so)
{
unsigned long user = so ? CKU_SO : CKU_USER;
CK_RV rv;
startSession(slot, true);
rv = p11->C_Login(session, user, pin, pinlen);
if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN)
pk11error("C_Login", rv);
@ -126,6 +157,11 @@ void pkcs11::loadAttribute(pk11_attribute &attribute, CK_OBJECT_HANDLE object)
attribute.load(session, object);
}
void pkcs11::storeAttribute(pk11_attribute &attribute, CK_OBJECT_HANDLE object)
{
attribute.store(session, object);
}
QList<CK_OBJECT_HANDLE> pkcs11::objectList(const pk11_attribute *att)
{
CK_RV rv;

View File

@ -31,9 +31,12 @@ class pkcs11
CK_SLOT_ID *getSlotList(unsigned long *num_slots);
void loadAttribute(pk11_attribute &attribute,
CK_OBJECT_HANDLE object);
void storeAttribute(pk11_attribute &attribute,
CK_OBJECT_HANDLE object);
QList<CK_OBJECT_HANDLE> objectList(const pk11_attribute *att);
void login(unsigned long slot,
unsigned char *pin, unsigned long pinlen, bool so);
void login(unsigned char *pin, unsigned long pinlen, bool so);
void logout();
bool needsLogin(bool so);
void setPin(unsigned char *oldPin, unsigned long oldPinLen,
unsigned char *pin, unsigned long pinLen);
void initPin(unsigned char *pin, unsigned long pinLen);

View File

@ -38,24 +38,26 @@ QPixmap *pki_scard::icon[1] = { NULL };
#define XCA_ENGINE_cmd(e, cmd, value) \
do { \
if (!ENGINE_ctrl_cmd_string(e, cmd, value, 0)) { \
openssl_error(); \
printf("FAILED: '%s' : '%s'\n", cmd, value ? value:"");\
ENGINE_free(e); \
return 0; \
} \
printf("SUCCESS: '%s' : '%s'\n", cmd, value ? value:""); \
} while(0);
ENGINE *pki_scard::p11_engine = NULL;
int pki_scard::init_p11engine(void) const
int pki_scard::init_p11engine(void)
{
ENGINE *e;
if (p11_engine)
return 1;
pkcs11::load_lib("", true);
ENGINE_load_dynamic();
e = ENGINE_by_id("dynamic");
openssl_error();
XCA_ENGINE_cmd(e, "SO_PATH", ENGINE_LIB);
XCA_ENGINE_cmd(e, "ID", "pkcs11");
@ -63,6 +65,7 @@ int pki_scard::init_p11engine(void) const
XCA_ENGINE_cmd(e, "LOAD", NULL);
XCA_ENGINE_cmd(e, "MODULE_PATH", PKCS11_DEFAULT_MODULE_NAME);
ENGINE_init(e);
p11_engine = e;
return 1;
}
@ -137,9 +140,7 @@ int pki_scard::prepare_card() const
CK_SLOT_ID *p11_slots = NULL;
unsigned long i, num_slots;
while (1) {
p11.init_pkcs11();
p11_slots = p11.getSlotList(&num_slots);
for (i=0; i<num_slots; i++) {
pkcs11 myp11;
@ -260,20 +261,62 @@ QString pki_scard::getTypeString(void)
return tr("SmartCard") + " " + pki_key::getTypeString();
}
QString pki_scard::scardLogin(pkcs11 &p11, bool so, bool force) const
{
char pin[256];
int pinlen;
bool need_login;
QString text = so ?
tr("Please enter the SO PIN (PUK) of the token: "):
tr("Please enter the PIN of the token: ");
pass_info p(XCA_TITLE, text + getIntName());
p.setPin();
need_login = p11.needsLogin(so);
if (force || need_login) {
if (!need_login)
p11.logout();
pinlen = MainWindow::passRead(pin, 256, 0, &p);
if (pinlen == -1)
return QString();
p11.login((unsigned char*)pin, pinlen, so);
} else {
return QString("");
}
return QString::fromLocal8Bit(pin, pinlen);
}
EVP_PKEY *pki_scard::decryptKey() const
{
int slot_id = prepare_card();
int slot_id;
QString pin;
struct {
const void *password;
const char *prompt_info;
} cb_data = { NULL, NULL };
pass_info p(XCA_TITLE,
pki_scard::tr("Please enter the PIN of the token: ") +
getIntName());
p.setPin();
slot_id = prepare_card();
if (slot_id == -1)
return NULL;
QString key_id = QString("%1:").arg(slot_id) + object_id;
printf("SLOT:ID = '%s'\n", CCHAR(key_id));
init_p11engine();
ENGINE_init(p11_engine);
ign_openssl_error();
pkcs11 p11;
p11.startSession(slot_id, true);
pin = scardLogin(p11, false);
if (pin.isNull())
return NULL;
cb_data.password = CCHAR(pin);
EVP_PKEY *pkey = ENGINE_load_private_key(p11_engine, CCHAR(key_id),
NULL, NULL);
NULL, &cb_data);
openssl_error();
return pkey;
}
@ -282,8 +325,72 @@ void pki_scard::changePin()
char oldPin[256], newPin[256];
int slot;
pass_info p(XCA_TITLE, qApp->translate("MainWindow",
"Please enter the PIN of the token: ")+ getIntName());
pass_info p(XCA_TITLE,
pki_scard::tr("Please enter the PIN of the token: ") +
getIntName());
p.setPin();
slot = prepare_card();
if (slot == -1)
return;
int oldPinLen = MainWindow::passRead(oldPin, 256, 0, &p);
if (oldPinLen == -1)
return;
pkcs11 p11;
p11.startSession(slot, true);
p11.logout();
p11.login((unsigned char*)oldPin, oldPinLen, false);
p.setDescription(qApp->translate("MainWindow",
"Please enter the new Pin for the token: ") +getIntName());
int newPinLen = MainWindow::passWrite(newPin, 256, 0, &p);
if (newPinLen != -1) {
p11.setPin((unsigned char*)oldPin, oldPinLen,
(unsigned char*)newPin, newPinLen);
}
}
void pki_scard::initPin()
{
char soPin[256], newPin[256];
int slot;
pass_info p(XCA_TITLE,
pki_scard::tr("Please enter the SO PIN (PUK) of the token: ") +
getIntName());
p.setPin();
slot = prepare_card();
if (slot == -1)
return;
pkcs11 p11;
p11.startSession(slot, true);
if (p11.needsLogin(true)) {
int soPinLen = MainWindow::passRead(soPin, 256, 0, &p);
if (soPinLen == -1)
return;
p11.login((unsigned char*)soPin, soPinLen, true);
}
p.setDescription(qApp->translate("MainWindow",
"Please enter the new Pin for the token: ") +getIntName());
int newPinLen = MainWindow::passWrite(newPin, 256, 0, &p);
if (newPinLen != -1) {
p11.initPin((unsigned char*)newPin, newPinLen);
}
p11.logout();
}
void pki_scard::changeSoPin()
{
char oldPin[256], newPin[256];
int slot;
pass_info p(XCA_TITLE,
pki_scard::tr("Please enter the SO PIN (PUK) of the token: ") +
getIntName());
p.setPin();
slot = prepare_card();
@ -293,48 +400,19 @@ void pki_scard::changePin()
if (oldPinLen == -1)
return;
pkcs11 p11;
p11.init_pkcs11();
p11.login(slot, (unsigned char*)oldPin, oldPinLen, false);
p11.startSession(slot, true);
p11.logout();
p11.login((unsigned char*)oldPin, oldPinLen, true);
p.setDescription(qApp->translate("MainWindow",
"Please enter the new Pin for the token: ") +getIntName());
"Please enter the new SO Pin for the token: ") +getIntName());
int newPinLen = MainWindow::passWrite(newPin, 256, 0, &p);
if (newPinLen == -1)
return;
p11.setPin((unsigned char*)oldPin, oldPinLen,
(unsigned char*)newPin, newPinLen);
}
void pki_scard::initPin()
{
char soPin[256], newPin[256];
int slot;
pass_info p(XCA_TITLE, qApp->translate("MainWindow",
"Please enter the SO PIN (PUK) of the token: ") +
getIntName());
p.setPin();
slot = prepare_card();
if (slot == -1)
return;
int soPinLen = MainWindow::passRead(soPin, 256, 0, &p);
if (soPinLen == -1)
return;
pkcs11 p11;
p11.init_pkcs11();
p11.login(slot, (unsigned char*)soPin, soPinLen, true);
p.setDescription(qApp->translate("MainWindow",
"Please enter the new Pin for the token: ") +getIntName());
int newPinLen = MainWindow::passWrite(newPin, 256, 0, &p);
if (newPinLen == -1)
return;
p11.initPin((unsigned char*)newPin, newPinLen);
if (newPinLen == -1) {
p11.setPin((unsigned char*)oldPin, oldPinLen,
(unsigned char*)newPin, newPinLen);
}
p11.logout();
}
int pki_scard::verify()

View File

@ -32,7 +32,7 @@ class pki_scard: public pki_key
virtual ~pki_scard();
static QPixmap *icon[1];
void load_token(pkcs11 &p11, CK_OBJECT_HANDLE object);
int init_p11engine(void) const;
static int init_p11engine(void);
int prepare_card() const;
void fromData(const unsigned char *p, db_header_t *head);
unsigned char *toData(int *size);
@ -45,8 +45,10 @@ class pki_scard: public pki_key
QString getCardLabel() const { return card_label; }
EVP_PKEY *decryptKey() const;
QString length();
QString scardLogin(pkcs11 &p11, bool so, bool force=false)const;
void changePin();
void initPin();
void changeSoPin();
int verify();
bool isScard();
QVariant getIcon();

View File

@ -8,8 +8,12 @@
#include "pki_x509.h"
#include "pki_evp.h"
#include "pki_scard.h"
#include "func.h"
#include "base.h"
#include "exception.h"
#include "pass_info.h"
#include "widgets/MainWindow.h"
#include <qdir.h>
QPixmap *pki_x509::icon[5] = { NULL, NULL, NULL, NULL, NULL };
@ -172,6 +176,45 @@ void pki_x509::load_token(pkcs11 &p11, CK_OBJECT_HANDLE object)
openssl_error();
}
void pki_x509::store_token()
{
pki_scard *card = (pki_scard *)privkey;
int slot, size;
unsigned char*p, *p1;
QList<CK_OBJECT_HANDLE> objects;
if (!privkey || !privkey->isScard())
throw errorEx(tr("No associated Smart card"));
slot = card->prepare_card();
size = i2d_X509(cert, NULL);
openssl_error();
p = p1 = (unsigned char*)OPENSSL_malloc(size);
i2d_X509(cert, &p1);
openssl_error();
pk11_attr_data x509(CKA_VALUE);
x509.setValue(p, size);
free(p);
pk11_attr_ulong class_att = pk11_attr_ulong(CKA_CLASS);
class_att.setValue(CKO_CERTIFICATE);
pkcs11 p11;
p11.startSession(slot, true);
objects = p11.objectList(&class_att);
if (objects.count() == 0)
throw errorEx(tr("No certificate object found"));
if (objects.count() > 1)
throw errorEx(tr("More than one certificate objects found"));
if (card->scardLogin(p11, false).isNull())
return;
p11.storeAttribute(x509, objects[0]);
openssl_error();
}
bool pki_x509::verifyQASerial(const a1int &secret) const
{
return getQASerial(secret) == getSerial();
@ -329,8 +372,7 @@ unsigned char *pki_x509::toData(int *size)
*size = i2d_X509(cert, NULL) + 11 + caSerial.toHex().length() +
caTemplate.length() + crlExpiry.derSize() + revoked.derSize();
openssl_error();
p = (unsigned char*)OPENSSL_malloc(*size);
p1 = p;
p = p1 = (unsigned char*)OPENSSL_malloc(*size);
i2d_X509(cert, &p1); // cert
db::intToData(&p1, trust); // trust

View File

@ -44,6 +44,7 @@ class pki_x509 : public pki_x509super
void fload(const QString fname);
void load_token(pkcs11 &p11, CK_OBJECT_HANDLE object);
void store_token();
void fromPEM_BIO(BIO *bio, QString name);
void writeDefault(const QString fname);
a1int hashInfo(const EVP_MD *md) const;

View File

@ -151,7 +151,7 @@ MainWindow::MainWindow(QWidget *parent )
homedir = getHomeDir();
init_curves();
pkcs11::load_lib("", true);
pki_scard::init_p11engine();
// FIXME: Change pass isn't functional yet.
BNchangePass->setDisabled(true);
@ -357,6 +357,9 @@ void MainWindow::importScard()
pk11_attr_ulong class_att = pk11_attr_ulong(CKA_CLASS);
p11_slots = p11.getSlotList(&num_slots);
if (num_slots == 0)
QMessageBox::warning(this, XCA_TITLE,
tr("No Smart card found"));
for (i=0; i<num_slots; i++) {
p11.startSession(i);
@ -482,6 +485,25 @@ int MainWindow::initPass()
return 1;
}
static int hex2bin(QString &x, char *buf, int buflen)
{
int len = x.length();
bool ok = false;
if (len % 2)
return -1;
len /= 2;
if (len > buflen)
return -1;
for (int i=0; i<len; i++) {
buf[i] = x.mid(i*2, 2).toInt(&ok, 16);
if (!ok)
return -1;
}
return len;
}
static const QString hexwarn = MainWindow::tr("Hex password must only contain the characters '0' - '9' and 'a' - 'f' and it must consist of an even number of characters");
// Static Password Callback functions
int MainWindow::passRead(char *buf, int size, int, void *userdata)
{
@ -500,15 +522,18 @@ int MainWindow::passRead(char *buf, int size, int, void *userdata)
ui.takeHex->hide();
}
if (dlg->exec()) {
while (dlg->exec()) {
QString x = ui.pass->text();
if (ui.takeHex->isChecked()) {
// PARSE Hex string
abort();
ret = hex2bin(x, buf, size);
if (ret != -1)
break;
} else {
strncpy(buf, x.toAscii(), size);
ret = x.length();
break;
}
QMessageBox::warning(p->getWidget(), XCA_TITLE, hexwarn);
}
delete dlg;
return ret;
@ -539,13 +564,24 @@ int MainWindow::passWrite(char *buf, int size, int, void *userdata)
QString A = ui.passA->text();
QString B = ui.passB->text();
if (A == B) {
strncpy(buf, A.toAscii(), size);
ret = A.length();
break;
if (ui.takeHex->isChecked()) {
ret = hex2bin(A, buf, size);
if (ret != -1)
break;
} else {
strncpy(buf, A.toAscii(), size);
ret = A.length();
break;
}
QMessageBox::warning(p->getWidget(), XCA_TITLE,
hexwarn);
} else {
QMessageBox::warning(p->getWidget(), tr(XCA_TITLE), p->getType() + tr(" missmatch"));
QMessageBox::warning(p->getWidget(), XCA_TITLE,
p->getType() + tr(" missmatch"));
}
}
for (int i=0; i<ret; i++)
printf("NewPass[%d] = 0x%02x\n", i, buf[i]);
delete dlg;
return ret;
}