Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C; tab-width: 8 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "cmmf.h"
#include "cmmfi.h"
#include "sechash.h"
#include "genname.h"
#include "pk11func.h"
#include "cert.h"
#include "secitem.h"
#include "secmod.h"
#include "keyhi.h"
static int
cmmf_create_witness_and_challenge(PLArenaPool *poolp,
CMMFChallenge *challenge,
long inRandom,
SECItem *senderDER,
SECKEYPublicKey *inPubKey,
void *passwdArg)
{
SECItem *encodedRandNum;
SECItem encodedRandStr = { siBuffer, NULL, 0 };
SECItem *dummy;
unsigned char *randHash, *senderHash, *encChal = NULL;
unsigned modulusLen = 0;
SECStatus rv = SECFailure;
CMMFRand randStr = { { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 } };
PK11SlotInfo *slot;
PK11SymKey *symKey = NULL;
CERTSubjectPublicKeyInfo *spki = NULL;
encodedRandNum = SEC_ASN1EncodeInteger(poolp, &challenge->randomNumber,
inRandom);
if (!encodedRandNum) {
goto loser;
}
randHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH);
senderHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH);
if (randHash == NULL) {
goto loser;
}
rv = PK11_HashBuf(SEC_OID_SHA1, randHash, encodedRandNum->data,
(PRUint32)encodedRandNum->len);
if (rv != SECSuccess) {
goto loser;
}
rv = PK11_HashBuf(SEC_OID_SHA1, senderHash, senderDER->data,
(PRUint32)senderDER->len);
if (rv != SECSuccess) {
goto loser;
}
challenge->witness.data = randHash;
challenge->witness.len = SHA1_LENGTH;
randStr.integer = *encodedRandNum;
randStr.senderHash.data = senderHash;
randStr.senderHash.len = SHA1_LENGTH;
dummy = SEC_ASN1EncodeItem(NULL, &encodedRandStr, &randStr,
CMMFRandTemplate);
if (dummy != &encodedRandStr) {
rv = SECFailure;
goto loser;
}
/* XXXX Now I have to encrypt encodedRandStr and stash it away. */
modulusLen = SECKEY_PublicKeyStrength(inPubKey);
encChal = PORT_ArenaNewArray(poolp, unsigned char, modulusLen);
if (encChal == NULL) {
rv = SECFailure;
goto loser;
}
slot = PK11_GetBestSlotWithAttributes(CKM_RSA_PKCS, CKF_WRAP, 0, passwdArg);
if (slot == NULL) {
rv = SECFailure;
goto loser;
}
(void)PK11_ImportPublicKey(slot, inPubKey, PR_FALSE);
/* In order to properly encrypt the data, we import as a symmetric
* key, and then wrap that key. That in essence encrypts the data.
* This is the method recommended in the PK11 world in order
* to prevent threading issues as well as breaking any other semantics
* the PK11 libraries depend on.
*/
symKey = PK11_ImportSymKey(slot, CKM_RSA_PKCS, PK11_OriginGenerated,
CKA_VALUE, &encodedRandStr, passwdArg);
if (symKey == NULL) {
rv = SECFailure;
goto loser;
}
challenge->challenge.data = encChal;
challenge->challenge.len = modulusLen;
rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, inPubKey, symKey,
&challenge->challenge);
PK11_FreeSlot(slot);
if (rv != SECSuccess) {
goto loser;
}
rv = SECITEM_CopyItem(poolp, &challenge->senderDER, senderDER);
crmf_get_public_value(inPubKey, &challenge->key);
/* Fall through */
loser:
if (spki != NULL) {
SECKEY_DestroySubjectPublicKeyInfo(spki);
}
if (encodedRandStr.data != NULL) {
PORT_Free(encodedRandStr.data);
}
if (encodedRandNum != NULL) {
SECITEM_FreeItem(encodedRandNum, PR_TRUE);
}
if (symKey != NULL) {
PK11_FreeSymKey(symKey);
}
return rv;
}
static SECStatus
cmmf_create_first_challenge(CMMFPOPODecKeyChallContent *challContent,
long inRandom,
SECItem *senderDER,
SECKEYPublicKey *inPubKey,
void *passwdArg)
{
SECOidData *oidData;
CMMFChallenge *challenge;
SECAlgorithmID *algId;
PLArenaPool *poolp;
SECStatus rv;
oidData = SECOID_FindOIDByTag(SEC_OID_SHA1);
if (oidData == NULL) {
return SECFailure;
}
poolp = challContent->poolp;
challenge = PORT_ArenaZNew(poolp, CMMFChallenge);
if (challenge == NULL) {
return SECFailure;
}
algId = challenge->owf = PORT_ArenaZNew(poolp, SECAlgorithmID);
if (algId == NULL) {
return SECFailure;
}
rv = SECITEM_CopyItem(poolp, &algId->algorithm, &oidData->oid);
if (rv != SECSuccess) {
return SECFailure;
}
rv = cmmf_create_witness_and_challenge(poolp, challenge, inRandom,
senderDER, inPubKey, passwdArg);
challContent->challenges[0] = (rv == SECSuccess) ? challenge : NULL;
challContent->numChallenges++;
return rv;
}
CMMFPOPODecKeyChallContent *
CMMF_CreatePOPODecKeyChallContent(void)
{
PLArenaPool *poolp;
CMMFPOPODecKeyChallContent *challContent;
poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
if (poolp == NULL) {
return NULL;
}
challContent = PORT_ArenaZNew(poolp, CMMFPOPODecKeyChallContent);
if (challContent == NULL) {
PORT_FreeArena(poolp, PR_FALSE);
return NULL;
}
challContent->poolp = poolp;
return challContent;
}
SECStatus
CMMF_POPODecKeyChallContentSetNextChallenge(CMMFPOPODecKeyChallContent *inDecKeyChall,
long inRandom,
CERTGeneralName *inSender,
SECKEYPublicKey *inPubKey,
void *passwdArg)
{
CMMFChallenge *curChallenge;
PLArenaPool *genNamePool = NULL, *poolp;
SECStatus rv;
SECItem *genNameDER;
void *mark;
PORT_Assert(inDecKeyChall != NULL &&
inSender != NULL &&
inPubKey != NULL);
if (inDecKeyChall == NULL ||
inSender == NULL || inPubKey == NULL) {
return SECFailure;
}
poolp = inDecKeyChall->poolp;
mark = PORT_ArenaMark(poolp);
genNamePool = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
genNameDER = CERT_EncodeGeneralName(inSender, NULL, genNamePool);
if (genNameDER == NULL) {
rv = SECFailure;
goto loser;
}
if (inDecKeyChall->challenges == NULL) {
inDecKeyChall->challenges =
PORT_ArenaZNewArray(poolp, CMMFChallenge *, (CMMF_MAX_CHALLENGES + 1));
inDecKeyChall->numAllocated = CMMF_MAX_CHALLENGES;
}
if (inDecKeyChall->numChallenges >= inDecKeyChall->numAllocated) {
rv = SECFailure;
goto loser;
}
if (inDecKeyChall->numChallenges == 0) {
rv = cmmf_create_first_challenge(inDecKeyChall, inRandom,
genNameDER, inPubKey, passwdArg);
} else {
curChallenge = PORT_ArenaZNew(poolp, CMMFChallenge);
if (curChallenge == NULL) {
rv = SECFailure;
goto loser;
}
rv = cmmf_create_witness_and_challenge(poolp, curChallenge, inRandom,
genNameDER, inPubKey,
passwdArg);
if (rv == SECSuccess) {
inDecKeyChall->challenges[inDecKeyChall->numChallenges] =
curChallenge;
inDecKeyChall->numChallenges++;
}
}
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
PORT_FreeArena(genNamePool, PR_FALSE);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
if (genNamePool != NULL) {
PORT_FreeArena(genNamePool, PR_FALSE);
}
PORT_Assert(rv != SECSuccess);
return rv;
}
SECStatus
CMMF_DestroyPOPODecKeyRespContent(CMMFPOPODecKeyRespContent *inDecKeyResp)
{
PORT_Assert(inDecKeyResp != NULL);
if (inDecKeyResp != NULL && inDecKeyResp->poolp != NULL) {
PORT_FreeArena(inDecKeyResp->poolp, PR_FALSE);
}
return SECSuccess;
}
int
CMMF_POPODecKeyRespContentGetNumResponses(CMMFPOPODecKeyRespContent *inRespCont)
{
int numResponses = 0;
PORT_Assert(inRespCont != NULL);
if (inRespCont == NULL) {
return 0;
}
while (inRespCont->responses[numResponses] != NULL) {
numResponses++;
}
return numResponses;
}
SECStatus
CMMF_POPODecKeyRespContentGetResponse(CMMFPOPODecKeyRespContent *inRespCont,
int inIndex,
long *inDest)
{
PORT_Assert(inRespCont != NULL);
if (inRespCont == NULL || inIndex < 0 ||
inIndex >= CMMF_POPODecKeyRespContentGetNumResponses(inRespCont)) {
return SECFailure;
}
*inDest = DER_GetInteger(inRespCont->responses[inIndex]);
return (*inDest == -1) ? SECFailure : SECSuccess;
}