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.
403 lines
9.1 KiB
403 lines
9.1 KiB
15 years ago
|
|
||
|
#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;
|
||
|
}
|
||
|
|
||
|
|