#include "engine_sct.h" #include "engine_sct_internal.h" #include "Init_Engine.h" #include "CardObject.h" #include "CardKey.h" #include "SlotList.h" #include "CertificateList.h" #include "SecOpGuard.h" #include #include #include #include #include #include #include #include #include #include // Provide thread synchronization #include #include EVP_PKEY *SecureTokenEngine::encapsule_CardKey(CardKey *ck) { EVP_PKEY* pk(EVP_PKEY_new()); RSA* rsa(RSA_new()); if(pk == NULL || rsa == NULL) return NULL; // Initialize the RSA structure { RSA_set_method(rsa, RSA_get_sct_method()); RSA_set_app_data(rsa, ck); // We need (at least) the public exponent and the modulus for OpenSSL itself because // it calculates the I/O buffer sizes out of it. act::ITokenKey* key = ck->getKey(); act::Blob publicExponent; act::Blob modulus; key->GetParam(act::PUBLICKEY, publicExponent); key->GetParam(act::MODULO, modulus); act::I2OSP(modulus); act::I2OSP(publicExponent); rsa->n = BN_bin2bn(&modulus[0], modulus.size(), rsa->n); rsa->e = BN_bin2bn(&publicExponent[0], publicExponent.size(), rsa->e); // Meh. Set it here. If we don't, OpenSSL tries to emulate sign/verify with // private_encrypt and public_decrypt which we can't do. rsa->flags |= RSA_FLAG_SIGN_VER; } EVP_PKEY_set1_RSA(pk, rsa); // Decreases refcount by one, i.e. transfers sole ownership to EVP_PKEY struct RSA_free(rsa); return pk; } int SecureTokenEngine::setPin(char *pin) { int plen = strlen(pin); act::Blob(pin, pin+plen).swap(m_pin); std::fill(pin, pin+plen, '*'); return 1; } int SecureTokenEngine::incVerbosity() { return 0; } int SecureTokenEngine::setInitArgs(const char *args) { return 0; } int SecureTokenEngine::init() { act::Init(true); act::SlotManager slot_manager; try { slot_manager.Install(act::SubsystemReg::CreateSubsystem("PCSC", 0)); slot_manager.Refresh(); } catch(act::Exception& ACT_DEBUG_PARAM(e)) { ACT_TRACE(e, "SecureTokenEngine::Init"); return 0; } int slots = slot_manager.GetSlotNumber(); for(int i = 0; i < slots; i++) { act::ISlot* slot(slot_manager.GetSlot(i)->Clone()); m_slot_list.addSlot(slot); } return 1; } int SecureTokenEngine::finish() { m_slot_list.clear(); return 1; } int SecureTokenEngine::rsa_finish(RSA *rsa) { // This function is only called on freeing of 'our' own RSA keys which have been generated // by encapsule_CardKey // NB: If you use something like // // EVP_PKEY* pk_pub = ENGINE_load_public_key(e, key_id, NULL, NULL); // RSA* pubkey = EVP_PKEY_get1_RSA(pk_pub); // // in the user code, you have to use both EVP_PKEY_free() and RSA_free() until the last traces are gone. CardKey* ck(EXTRACT_CARD_KEY(rsa)); ACT_ASSERT(ck != 0); delete ck; return 1; } int SecureTokenEngine::loadCertCtrl(ENGINE *e, load_cert_params *p) { CardObject co(&m_slot_list); ACT_ASSERT(p != NULL); ACT_ASSERT(p->s_token_cert_id != NULL); if(p == NULL || p->s_token_cert_id == NULL) return NULL; if(!co.searchFor(act::CERTIFICATE, p->s_token_cert_id)) return NULL; const act::Blob& certblob = co.getCertBlob(); const unsigned char *cbp = &certblob[0]; p->cert = d2i_X509(NULL, &cbp, certblob.size()); return p->cert != NULL ? 1 : 0; } EVP_PKEY *SecureTokenEngine::load_privkey(const char *s_key_id, UI_METHOD *ui_method, void *callback_data) { CardObject co(&m_slot_list); if(!co.searchFor(act::PRIVATEKEY, s_key_id)) return NULL; std::auto_ptr ck(new CardKey(co.getKey())); // Ask for the PIN if not already passed into the module if(m_pin.empty()) ck->setPin(ui_method, callback_data); else ck->setPin(m_pin); // Try to authenticate with the provided PIN. If we fail, don't return the key at all. act::IToken* token = ck->getKey()->GetToken(); SecOpGuard guard(token); act::Synchronize lock(*token); if(!ck->Authenticate(guard)) return NULL; EVP_PKEY* pk = encapsule_CardKey(ck.get()); if(pk != NULL) { ck.release(); return pk; } return NULL; } EVP_PKEY *SecureTokenEngine::load_pubkey(const char *s_key_id, UI_METHOD *ui_method, void *callback_data) { // Really. Same as above, but without asking/setting a PIN. Simply because one can use an instance of // act::ITokenKey for both ways since private and public keys are interlinked internally. CardObject co(&m_slot_list); if(!co.searchFor(act::PUBLICKEY, s_key_id)) return NULL; std::auto_ptr ck(new CardKey(co.getKey())); EVP_PKEY* pk = encapsule_CardKey(ck.get()); if(pk != NULL) { ck.release(); return pk; } return NULL; } int SecureTokenEngine::rsa_encrypt(int flen, const unsigned char *from, unsigned char *to, const CardKey* ck, int padding) { if(padding != RSA_PKCS1_PADDING) { ACT_TRACE("SecureTokenEngine::rsa_encrypt: Only RSA_PKCS1_PADDING allowed so far\n"); return -1; } if(ck == NULL) { ACT_TRACE("SecureTokenEngine::rsa_encrypt: No CardKey given\n"); return -1; } act::Blob _plaintext(from, from+flen); act::Blob _ciphertext; int retlen = -1; act::IToken* token = ck->getKey()->GetToken(); SecOpGuard guard(token); act::Synchronize lock(*token); try { act::Algorithm alg(ck->getKey(), act::ENCRYPT); alg << _plaintext << act::final >> _ciphertext; retlen = _ciphertext.size(); // OpenSSL uses these operations for just one round and determines the buffer size from the // key length. Still I'm not happy with it. std::copy(_ciphertext.begin(), _ciphertext.end(), to); } catch(act::Exception& ACT_DEBUG_PARAM(e)) { ACT_TRACE(e, "SecureTokenEngine::rsa_encrypt"); } return retlen; } int SecureTokenEngine::rsa_decrypt(int flen, const unsigned char *from, unsigned char *to, CardKey* ck, int padding) { if(padding != RSA_PKCS1_PADDING) { ACT_TRACE("SecureTokenEngine::rsa_decrypt: Only RSA_PKCS1_PADDING allowed so far\n"); return -1; } if(ck == NULL) { ACT_TRACE("SecureTokenEngine::rsa_decrypt: No CardKey given\n"); return -1; } act::IToken* token = ck->getKey()->GetToken(); SecOpGuard guard(token); act::Synchronize lock(*token); if(!ck->Authenticate(guard)) return -1; act::Blob _ciphertext(from, from+flen); act::Blob _plaintext; int retlen = -1; try { act::Algorithm alg(ck->getKey(), act::DECRYPT); alg << _ciphertext << act::final >> _plaintext; retlen = _plaintext.size(); // Same as with rsa_encrypt std::copy(_plaintext.begin(), _plaintext.end(), to); } catch(act::Exception& ACT_DEBUG_PARAM(e)) { ACT_TRACE(e, "SecureTokenEngine::rsa_decrypt"); } return retlen; } int SecureTokenEngine::rsa_sign(int type, const unsigned char *msg, unsigned int msglen, unsigned char *sigret, unsigned int *siglen, CardKey* ck) { if(ck == NULL) { ACT_TRACE("SecureTokenEngine::rsa_sign: No CardKey given\n"); return 0; } act::IToken* token = ck->getKey()->GetToken(); SecOpGuard guard(ck->getKey()->GetToken()); act::Synchronize lock(*token); if(!ck->Authenticate(guard)) return 0; act::Blob _plaintext(msg, msg+msglen); act::Blob _signature; unsigned int _siglen = 0; // In that case the hashing is already done and we just need the lowlevel key operation bool ssl = (type == NID_md5_sha1); act::ITokenKey* key = ck->getKey(); act::Blob oldhash; key->GetParam(act::HASH, oldhash); if(ssl) key->SetParam(act::HASH, "DummyHash"); try { act::Algorithm alg(ck->getKey(), act::SIGN); alg << _plaintext << act::final >> _signature; _siglen = _signature.size(); // I won't repeat myself here.... std::copy(_signature.begin(), _signature.end(), sigret); } catch(act::Exception& ACT_DEBUG_PARAM(e)) { ACT_TRACE(e, "SecureTokenEngine::rsa_sign"); } key->SetParam(act::HASH, oldhash); *siglen = _siglen; return _siglen > 0 ? 1 : 0; } int SecureTokenEngine::rsa_verify(int type, const unsigned char *msg, unsigned int msglen, unsigned char *signature, unsigned int siglen, const CardKey* ck) { if(ck == NULL) { ACT_TRACE("SecureTokenEngine::rsa_verify: No CardKey given\n"); return 0; } act::IToken* token = ck->getKey()->GetToken(); SecOpGuard guard(token); act::Synchronize lock(*token); act::Blob _plaintext(msg, msg+msglen); act::Blob _signature(signature, signature+siglen); bool sig_ok = false; try { act::Algorithm alg(ck->getKey(), act::VERIFY, _signature); alg << _plaintext << act::final; sig_ok = alg.GetStatus() == act::SIGNATURE_OK; } catch(act::Exception& ACT_DEBUG_PARAM(e)) { ACT_TRACE(e, "SecureTokenEngine::rsa_verify"); } if(!sig_ok) RSAerr(RSA_F_RSA_VERIFY, RSA_R_BAD_SIGNATURE); return sig_ok ? 1 : 0; } int SecureTokenEngine::enumerate_certs(ENGINE *e, enum_certs_s **p) { if(p == NULL) return 0; // Drop and free old instance (if present) and create a fresh one. m_cert_list.reset(new CertificateList()); m_cert_list->init(m_slot_list); *p = m_cert_list->getEnumList(); // Only way to return 0 would be that we ran out of memory. If there are no certificates to be found // we would return successful, but with an empty list (num_certs == 0) return *p ? 1 : 0; }