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
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; |
|
} |
|
|
|
|
|
|