/* vi: set sw=4 ts=4: * * Copyright (C) 2009 Christian Hohnstaedt. * * All rights reserved. */ #include "pki_scard.h" #include "pass_info.h" #include "pk11_attribute.h" #include "exception.h" #include "db_base.h" #include "pkcs11.h" #include "x509name.h" #include "func.h" #include "db.h" #include #include #include #include #include #include #include #include #include #if defined(_WIN32) || defined(USE_CYGWIN) #define PKCS11_DEFAULT_MODULE_NAME "opensc-pkcs11.dll" #define ENGINE_LIB "engine_pkcs11.dll" #else #define PKCS11_DEFAULT_MODULE_NAME "/usr/lib/opensc-pkcs11.so" #define ENGINE_LIB "/usr/lib/engines/engine_pkcs11.so" #endif QPixmap *pki_scard::icon[1] = { NULL }; #define XCA_ENGINE_cmd(cmd, value) \ do { \ QString msg = QString(" '%1' : '%2'\n" \ ).arg(cmd).arg(value ? value:""); \ if (!ENGINE_ctrl_cmd_string(e, cmd, value, 0)) { \ ENGINE_free(e); \ if (!silent) { \ log += tr("FAILED:") + msg; \ openssl_error(log); \ } \ ign_openssl_error(); \ return false; \ } \ log += tr("SUCCESS:") +msg; \ } while(0); ENGINE *pki_scard::p11_engine = NULL; bool pki_scard::init_p11engine(QString file, bool silent) { ENGINE *e; const char *engine_path; QString log; #ifdef WIN32 static bool loaded = false; if (loaded) return true; loaded = true; #endif if (file.isEmpty()) file = PKCS11_DEFAULT_MODULE_NAME; if (p11_engine) { log += "Unloading old OpenSSL PKCS#11 engine\n"; ENGINE_finish(p11_engine); ENGINE_free(p11_engine); p11_engine = NULL; if (!silent) { openssl_error(log); } else { ign_openssl_error(); } } if (!pkcs11::load_lib(file, silent)) return false; log += "Successfully loaded PKCS#11 library: " +file +"\n"; #ifdef WIN32 engine_path = ".\\" ENGINE_LIB; #else engine_path = ENGINE_LIB; #endif ENGINE_load_dynamic(); e = ENGINE_by_id("dynamic"); XCA_ENGINE_cmd("SO_PATH", engine_path); XCA_ENGINE_cmd("ID", "pkcs11"); XCA_ENGINE_cmd("LIST_ADD", "1"); XCA_ENGINE_cmd("LOAD", NULL); XCA_ENGINE_cmd("MODULE_PATH", QString2filename(file)); // XCA_ENGINE_cmd("VERBOSE", NULL); ENGINE_init(e); if (!silent) { openssl_error(log); } else { if (ign_openssl_error()) return false; } p11_engine = e; return true; } void pki_scard::init(void) { class_name = "pki_scard"; ownPass = ptPin; dataVersion = 1; pkiType = smartCard; cols = 5; card_serial = card_manufacturer = card_label = ""; bit_length = slot_label = ""; } pki_scard::pki_scard(const QString name) :pki_key(name) { init(); } EVP_PKEY *pki_scard::load_pubkey(pkcs11 &p11, CK_OBJECT_HANDLE object) const { const unsigned char *p; unsigned long s, keytype; EVP_PKEY *pkey = NULL; pk11_attr_ulong type(CKA_KEY_TYPE); p11.loadAttribute(type, object); keytype = type.getValue(); switch (keytype) { case CKK_RSA: { RSA *rsa = RSA_new(); pk11_attr_data n(CKA_MODULUS); p11.loadAttribute(n, object); rsa->n = n.getBignum(); pk11_attr_data e(CKA_PUBLIC_EXPONENT); p11.loadAttribute(e, object); rsa->e = e.getBignum(); pkey = EVP_PKEY_new(); EVP_PKEY_set1_RSA(pkey, rsa); break; } case CKK_DSA: { DSA *dsa = DSA_new(); pk11_attr_data p(CKA_PRIME); p11.loadAttribute(p, object); dsa->p = p.getBignum(); pk11_attr_data q(CKA_SUBPRIME); p11.loadAttribute(q, object); dsa->q = q.getBignum(); pk11_attr_data g(CKA_BASE); p11.loadAttribute(g, object); dsa->g = g.getBignum(); pk11_attr_data pub(CKA_VALUE); p11.loadAttribute(pub, object); dsa->pub_key = pub.getBignum(); pkey = EVP_PKEY_new(); EVP_PKEY_set1_DSA(pkey, dsa); break; } case CKK_EC: { EC_KEY *ec = EC_KEY_new(); pk11_attr_data grp(CKA_EC_PARAMS); p11.loadAttribute(grp, object); s = grp.getValue(&p); EC_GROUP *group = d2i_ECPKParameters(NULL, &p, s); EC_GROUP_set_asn1_flag(group, 1); EC_KEY_set_group(ec, group); openssl_error(); pk11_attr_data pt(CKA_EC_POINT); p11.loadAttribute(pt, object); BIGNUM *bn = pt.getBignum(); BN_CTX *ctx = BN_CTX_new(); EC_POINT *point = EC_POINT_bn2point(group, bn, NULL, ctx); EC_KEY_set_public_key(ec, point); BN_CTX_free(ctx); openssl_error(); pkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(pkey, ec); break; } default: throw errorEx(QString("Unsupported CKA_KEY_TYPE: %1\n").arg(keytype)); } openssl_error(); return pkey; } void pki_scard::load_token(pkcs11 &p11, CK_OBJECT_HANDLE object) { BIGNUM *cka_id; QStringList sl = p11.tokenInfo(); card_label = sl[0]; card_manufacturer = sl[1]; card_serial = sl[2]; pk11_attr_ulong bits(CKA_MODULUS_BITS); p11.loadAttribute(bits, object); bit_length.setNum(bits.getValue()); pk11_attr_data id(CKA_ID); p11.loadAttribute(id, object); cka_id = id.getBignum(); object_id = BNoneLine(cka_id); BN_free(cka_id); try { pk11_attr_data label(CKA_LABEL); p11.loadAttribute(label, object); slot_label = label.getText(); } catch (errorEx &err) { printf("No PubKey Label: %s\n", err.getCString()); // ignore } if (slot_label.isEmpty()) { try{ unsigned long s; const unsigned char *p; x509name xn; pk11_attr_data subj(CKA_SUBJECT); p11.loadAttribute(subj, object); s = subj.getValue(&p); xn.d2i(p, s); slot_label = xn.getMostPopular(); openssl_error(); } catch (errorEx &err) { printf("No Pubkey Subject: %s\n", err.getCString()); // ignore } } EVP_PKEY *pkey = load_pubkey(p11, object); if (pkey) { if (key) EVP_PKEY_free(key); key = pkey; } setIntName(card_label + " (" + slot_label + ")"); openssl_error(); } int pki_scard::getIdBin(unsigned char **to) { int l; BIGNUM *bn = NULL; BN_hex2bn(&bn, CCHAR(object_id)); l = BN_num_bytes(bn); *to = (unsigned char*)malloc(l); BN_bn2bin(bn, *to); return l; } QList pki_scard::possibleHashNids() { QList nids; int i; for (i=0; i< mech_list.count(); i++) { switch (mech_list[i]) { case CKM_MD5_RSA_PKCS: nids << NID_md5; break; case CKM_DSA_SHA1: case CKM_ECDSA_SHA1: case CKM_SHA1_RSA_PKCS: nids << NID_sha1; break; case CKM_SHA256_RSA_PKCS: nids << NID_sha256; break; case CKM_SHA384_RSA_PKCS: nids << NID_sha384; break; case CKM_SHA512_RSA_PKCS: nids << NID_sha512; break; case CKM_RIPEMD160_RSA_PKCS: nids << NID_ripemd160; break; } } if (nids.count() == 0) { nids << NID_md5 << NID_sha1 << NID_sha256 << NID_sha384 << NID_sha512 << NID_ripemd160; } return nids; } const EVP_MD *pki_scard::getDefaultMD() { if (mech_list.contains(CKM_SHA1_RSA_PKCS)) return EVP_sha1(); if (mech_list.contains(CKM_DSA_SHA1)) return EVP_dss1(); if (mech_list.contains(CKM_ECDSA_SHA1)) return EVP_ecdsa(); if (mech_list.contains(CKM_SHA512_RSA_PKCS)) return EVP_sha512(); if (mech_list.contains(CKM_SHA384_RSA_PKCS)) return EVP_sha384(); if (mech_list.contains(CKM_SHA256_RSA_PKCS)) return EVP_sha256(); if (mech_list.contains(CKM_RIPEMD160_RSA_PKCS)) return EVP_ripemd160(); if (mech_list.contains(CKM_MD5_RSA_PKCS)) return EVP_md5(); /* Last resort */ return EVP_sha1(); } /* Assures the correct card is inserted and * returns the slot ID or -1 on error or abort */ int pki_scard::prepare_card() const { pkcs11 p11; QList p11_slots; int i; if (!pkcs11::loaded()) return -1; while (1) { p11_slots = p11.getSlotList(); for (i=0; i objects; for (i=0; ilen - sizeof(db_header_t); version = head->version; card_serial = db::stringFromData(&p1); card_manufacturer = db::stringFromData(&p1); card_label = db::stringFromData(&p1); slot_label = db::stringFromData(&p1); bit_length = db::stringFromData(&p1); object_id = db::stringFromData(&p1); count = db::intFromData(&p1); mech_list.clear(); for (i=0; itranslate("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(); 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, true); p.setDescription(qApp->translate("MainWindow", "Please enter the new SO 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); } p11.logout(); } int pki_scard::verify() { return true; } bool pki_scard::isScard() { return true; } QString pki_scard::length() { return bit_length + " bit"; } QVariant pki_scard::getIcon(int column) { return column == 0 ? QVariant(*icon[0]) : QVariant(); }