// --- BEGIN COPYRIGHT BLOCK ---
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301 USA
//
// Copyright (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
/**
* RA_Enroll_Processor handles initialization and enrollment of the token
*/
/* variable naming convention:
* a_ passed as an 'in' argument to a method
* o_ passed as an 'out' argument to a method
* m_ member variable
*/
#include <string.h>
#include "pkcs11.h"
// for public key processing
#include "secder.h"
#include "pk11func.h"
#include "cryptohi.h"
#include "keyhi.h"
#include "base64.h"
#include "nssb64.h"
#include "prlock.h"
#include "cert.h"
#include "main/RA_Session.h"
#include "main/RA_Msg.h"
#include "main/Buffer.h"
#include "main/Util.h"
#include "main/PKCS11Obj.h"
#include "engine/RA.h"
#include "channel/Secure_Channel.h"
#include "msg/RA_SecureId_Request_Msg.h"
#include "msg/RA_SecureId_Response_Msg.h"
#include "msg/RA_New_Pin_Request_Msg.h"
#include "msg/RA_New_Pin_Response_Msg.h"
#include "processor/RA_Processor.h"
#include "processor/RA_Enroll_Processor.h"
#include "tus/tus_db.h"
#include "cms/CertEnroll.h"
#include "httpClient/httpc/response.h"
#include "main/Memory.h"
#define OP_PREFIX "op.enroll"
#ifdef XP_WIN32
#define TPS_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define TPS_PUBLIC
#endif /* !XP_WIN32 */
SECStatus PK11_GenerateRandom(unsigned char *,int);
// This parameter is read from the config file. It is the
// applet build ID which the administrator wants to set as
// the 'latest applet' to upgrade to.
static const char *g_applet_target_version = NULL;
/**
* this function returns a new allocated string
* @param cuid a 20 character string. Usually this is 20 hex
* digits representing a token CUID.
* @returns a new string which is basically a copy of the input, but
* with extra colons. The caller is responsible for freeing the
* returned string with PR_Free().
*/
static char *GetPrettyPrintCUID(const char *cuid)
{
int i,j;
char *ret = NULL;
if (cuid == NULL)
return NULL;
if (strlen(cuid) != 20)
return NULL;
ret = (char *)PR_Malloc(20+4+1);
j = 0;
for (i = 0; i < 24; i++) {
if (i == 4 || i == 9 || i == 14 || i == 19) {
ret[i] = '-';
} else {
ret[i] = cuid[j];
j++;
}
}
ret[24] = '\0';
return ret;
}
static SECItem *
PK11_GetPubIndexKeyID(CERTCertificate *cert) {
SECKEYPublicKey *pubk;
SECItem *newItem = NULL;
pubk = CERT_ExtractPublicKey(cert);
if (pubk == NULL) return NULL;
switch (pubk->keyType) {
case rsaKey:
newItem = SECITEM_DupItem(&pubk->u.rsa.modulus);
break;
case dsaKey:
newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue);
break;
case dhKey:
newItem = SECITEM_DupItem(&pubk->u.dh.publicValue);
break;
case ecKey:
newItem = SECITEM_DupItem(&pubk->u.ec.publicValue);
break;
case fortezzaKey:
default:
newItem = NULL; /* Fortezza Fix later... */
}
SECKEY_DestroyPublicKey(pubk);
/* make hash of it */
return newItem;
}
/**
* Constructs a processor for handling enrollment operation.
*/
TPS_PUBLIC RA_Enroll_Processor::RA_Enroll_Processor ()
{
}
/**
* Destructs enrollment processor.
*/
TPS_PUBLIC RA_Enroll_Processor::~RA_Enroll_Processor ()
{
}
RA_Status RA_Enroll_Processor::DoEnrollment(AuthParams *login, RA_Session *session,
CERTCertificate **certificates,
char **origins,
char **ktypes,
int pkcs11obj_enable,
PKCS11Obj *pkcs_objx,
NameValueSet *extensions,
int index, int keyTypeNum,
int start_progress,
int end_progress,
Secure_Channel *channel, Buffer *wrapped_challenge,
const char *tokenType,
const char *keyType,
Buffer *key_check,
Buffer *plaintext_challenge,
const char *cuid,
const char *msn,
const char *khex,
TokenKeyType key_type,
const char *profileId,
const char *userid,
const char *cert_id,
const char *publisher_id,
const char *cert_attr_id,
const char *pri_attr_id,
const char *pub_attr_id,
BYTE se_p1, BYTE se_p2, int keysize, const char *connid, const char *keyTypePrefix,char * applet_version)
{
RA_Status status = STATUS_NO_ERROR;
int rc = -1;
int len = 0;
int publish_result = -1;
Buffer *public_key = NULL;
SECItem si_mod;
Buffer *modulus=NULL;
SECItem *si_kid = NULL;
Buffer *keyid=NULL;
SECItem si_exp;
Buffer *exponent=NULL;
CertEnroll *certEnroll = NULL;
Buffer *cert = NULL;
Buffer CUID = channel->GetKeyDiversificationData();
const char *label = NULL;
const char *cuid_label = NULL;
const char *pattern;
char configname[256];
NameValueSet nv;
const char *pretty_cuid = NULL;
const char *FN="RA_Enroll_Processor::DoEnrollment";
char *cert_string = NULL;
SECItem* encodedPublicKeyInfo = NULL;
SECItem **ppEncodedPublicKeyInfo = NULL;
CERTSubjectPublicKeyInfo* spkix = NULL;
char *pKey = NULL;
char *ivParam = NULL;
char *wrappedPrivKey = NULL;
const char *drmconnid = NULL;
bool serverKeygen = false;
SECKEYPublicKey *pk_p = NULL;
float progress_block_size = (float) (end_progress - start_progress) / keyTypeNum;
RA::Debug(LL_PER_CONNECTION,FN,
"Start of keygen/certificate enrollment");
// check if we need to do key generation (by default, overwrite everything)
PR_snprintf((char *)configname, 256, "%s.%s.keyGen.%s.overwrite",
OP_PREFIX, tokenType, keyType);
RA::Debug(LL_PER_CONNECTION,FN,
"looking for config %s", configname);
if (RA::GetConfigStore()->GetConfigAsBool(configname, 1)) {
// do nothing
RA::Debug(LL_PER_CONNECTION,FN,
"do overwrite");
} else {
RA::Debug(LL_PER_CONNECTION,FN,
"do not overwrite, if %s exists", cert_id);
int num_objs = pkcs_objx->PKCS11Obj::GetObjectSpecCount();
char b[3];
bool foundObj = false;
for (int i = 0; i< num_objs; i++) {
ObjectSpec* os = pkcs_objx->GetObjectSpec(i);
unsigned long oid = os->GetObjectID();
b[0] = (char)((oid >> 24) & 0xff);
b[1] = (char)((oid >> 16) & 0xff);
b[2] = '\0';
/*
RA::Debug(LL_PER_PDU, "RA_Enroll_Processor::Process",
"object id =%c:%c b=%s",b[0], b[1], b);
*/
if (PL_strcasecmp(cert_id, b) == 0) {
foundObj = true;
break;
}
}
if (foundObj) {
// we already have a certificate there, skip enrollment
RA::Debug(LL_PER_CONNECTION,FN,
"Found certficate. Will not overwrite. Skipped enrollment");
return status;
} else {
RA::Debug(LL_PER_CONNECTION,FN,
"Certficate not found. Continuing with enrollment");
}
}
StatusUpdate(session, extensions,
start_progress + (index * progress_block_size) +
(progress_block_size * 15/100) /* progress */,
"PROGRESS_KEY_GENERATION");
if (key_type == KEY_TYPE_ENCRYPTION) {// do serverSide keygen?
PR_snprintf((char *)configname, 256, "%s.serverKeygen.enable", keyTypePrefix);
RA::Debug(LL_PER_CONNECTION,FN,
"looking for config %s", configname);
serverKeygen = RA::GetConfigStore()->GetConfigAsBool(configname, false);
}
certEnroll = new CertEnroll();
if (serverKeygen) {
RA::Debug(LL_PER_CONNECTION,FN,
"Private key is to be generated on server");
PR_snprintf((char *)configname, 256, "%s.serverKeygen.drm.conn", keyTypePrefix);
RA::Debug(LL_PER_CONNECTION,FN,
"looking for config %s", configname);
drmconnid = RA::GetConfigStore()->GetConfigAsString(configname);
PR_snprintf((char *)configname, 256, "%s.serverKeygen.archive", keyTypePrefix);
bool archive = RA::GetConfigStore()->GetConfigAsBool(configname, true);
RA::Debug(LL_PER_CONNECTION,FN,
"calling ServerSideKeyGen with userid =%s, archive=%s", userid, archive? "true":"false");
RA::ServerSideKeyGen(session, cuid, userid,
channel->getDrmWrappedDESKey(), &pKey,
&wrappedPrivKey, &ivParam, drmconnid,
archive, keysize);
if (pKey == NULL) {
RA::Error(LL_PER_CONNECTION,FN,
"Failed to generate key on server. Please check DRM.");
RA::Debug(LL_PER_CONNECTION,FN,
"ServerSideKeyGen called, pKey is NULL");
status = STATUS_ERROR_MAC_ENROLL_PDU;
goto loser;
} else
RA::Debug(LL_PER_CONNECTION,FN,
"key value = %s", pKey);
if (wrappedPrivKey == NULL) {
RA::Debug(LL_PER_CONNECTION,FN,
"ServerSideKeyGen called, wrappedPrivKey is NULL");
status = STATUS_ERROR_MAC_ENROLL_PDU;
goto loser;
} else
RA::Debug(LL_PER_CONNECTION,FN,
"wrappedPrivKey = %s", wrappedPrivKey);
if (ivParam == NULL) {
RA::Debug(LL_PER_CONNECTION,FN,
"ServerSideKeyGen called, ivParam is NULL");
status = STATUS_ERROR_MAC_ENROLL_PDU;
goto loser;
} else
RA::Debug(LL_PER_CONNECTION,FN,
"ivParam = %s", ivParam);
/*
* the following code converts b64-encoded public key info into SECKEYPublicKey
*/
SECStatus rv;
SECItem der;
CERTSubjectPublicKeyInfo* spki = NULL;
der.type = (SECItemType) 0; /* initialize it, since convertAsciiToItem does not set it */
rv = ATOB_ConvertAsciiToItem (&der, pKey);
if (rv != SECSuccess){
RA::Debug(LL_PER_CONNECTION,FN,
"failed to convert b64 private key to binary");
SECITEM_FreeItem(&der, PR_FALSE);
status = STATUS_ERROR_MAC_ENROLL_PDU;
goto loser;
}else {
RA::Debug(LL_PER_CONNECTION,FN,
"decoded private key as: secitem (len=%d)",der.len);
spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der);
if (spki != NULL) {
RA::Debug(LL_PER_CONNECTION,FN,
"Successfully decoded DER SubjectPublicKeyInfo structure");
pk_p = SECKEY_ExtractPublicKey(spki);
if (pk_p != NULL)
RA::Debug(LL_PER_CONNECTION,FN,
"Successfully extracted public key from SPKI structure");
else
RA::Debug(LL_PER_CONNECTION,FN,
|