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