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