A simple Qt based browser with no bullshit that supports PKCS#11 tokens (such as the SuisseID).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

402 lines
9.1 KiB

#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 <actIToken.h>
#include <actITokenKey.h>
#include <actITokenPIN.h>
#include <actMode.h>
#include <actState.h>
#include <actUtility.h>
#include <actDebug.h>
#include <actSlotManager.h>
#include <actAlgorithm.h>
#include <actISlot.h>
// Provide thread synchronization
#include <SyncObject.h>
#include <cstring>
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<CardKey> 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<CardKey> 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;
}