| /* -*- 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 "secitem.h" |
| #include "pk11func.h" |
| #include "secder.h" |
| #include "sechash.h" |
| |
| CMMFPOPODecKeyChallContent * |
| CMMF_CreatePOPODecKeyChallContentFromDER(const char *buf, long len) |
| { |
| PLArenaPool *poolp; |
| CMMFPOPODecKeyChallContent *challContent; |
| SECStatus rv; |
| |
| poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
| if (poolp == NULL) { |
| return NULL; |
| } |
| challContent = PORT_ArenaZNew(poolp, CMMFPOPODecKeyChallContent); |
| if (challContent == NULL) { |
| goto loser; |
| } |
| challContent->poolp = poolp; |
| rv = SEC_ASN1Decode(poolp, challContent, |
| CMMFPOPODecKeyChallContentTemplate, buf, len); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (challContent->challenges) { |
| while (challContent->challenges[challContent->numChallenges] != NULL) { |
| challContent->numChallenges++; |
| } |
| challContent->numAllocated = challContent->numChallenges; |
| } |
| return challContent; |
| loser: |
| if (poolp != NULL) { |
| PORT_FreeArena(poolp, PR_FALSE); |
| } |
| return NULL; |
| } |
| |
| int |
| CMMF_POPODecKeyChallContentGetNumChallenges(CMMFPOPODecKeyChallContent *inKeyChallCont) |
| { |
| PORT_Assert(inKeyChallCont != NULL); |
| if (inKeyChallCont == NULL) { |
| return 0; |
| } |
| return inKeyChallCont->numChallenges; |
| } |
| |
| SECItem * |
| CMMF_POPODecKeyChallContentGetPublicValue(CMMFPOPODecKeyChallContent *inKeyChallCont, |
| int inIndex) |
| { |
| PORT_Assert(inKeyChallCont != NULL); |
| if (inKeyChallCont == NULL || (inIndex > inKeyChallCont->numChallenges - 1) || |
| inIndex < 0) { |
| return NULL; |
| } |
| return SECITEM_DupItem(&inKeyChallCont->challenges[inIndex]->key); |
| } |
| |
| static SECAlgorithmID * |
| cmmf_get_owf(CMMFPOPODecKeyChallContent *inChalCont, |
| int inIndex) |
| { |
| int i; |
| |
| for (i = inIndex; i >= 0; i--) { |
| if (inChalCont->challenges[i]->owf != NULL) { |
| return inChalCont->challenges[i]->owf; |
| } |
| } |
| return NULL; |
| } |
| |
| SECStatus |
| CMMF_POPODecKeyChallContDecryptChallenge(CMMFPOPODecKeyChallContent *inChalCont, |
| int inIndex, |
| SECKEYPrivateKey *inPrivKey) |
| { |
| CMMFChallenge *challenge; |
| SECItem *decryptedRand = NULL; |
| PLArenaPool *poolp = NULL; |
| SECAlgorithmID *owf; |
| SECStatus rv = SECFailure; |
| SECOidTag tag; |
| CMMFRand randStr; |
| SECItem hashItem; |
| unsigned char hash[HASH_LENGTH_MAX]; |
| |
| PORT_Assert(inChalCont != NULL && inPrivKey != NULL); |
| if (inChalCont == NULL || inIndex < 0 || inIndex > inChalCont->numChallenges || |
| inPrivKey == NULL) { |
| return SECFailure; |
| } |
| |
| poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
| if (poolp == NULL) { |
| goto loser; |
| } |
| |
| challenge = inChalCont->challenges[inIndex]; |
| decryptedRand = SECITEM_AllocItem(poolp, NULL, challenge->challenge.len); |
| if (decryptedRand == NULL) { |
| goto loser; |
| } |
| rv = PK11_PrivDecryptPKCS1(inPrivKey, decryptedRand->data, |
| &decryptedRand->len, decryptedRand->len, |
| challenge->challenge.data, challenge->challenge.len); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| |
| rv = SEC_ASN1DecodeItem(poolp, &randStr, CMMFRandTemplate, |
| decryptedRand); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| rv = SECFailure; /* Just so that when we do go to loser, |
| * I won't have to set it again. |
| */ |
| owf = cmmf_get_owf(inChalCont, inIndex); |
| if (owf == NULL) { |
| /* No hashing algorithm came with the challenges. Can't verify */ |
| goto loser; |
| } |
| /* Verify the hashes in the challenge */ |
| tag = SECOID_FindOIDTag(&owf->algorithm); |
| hashItem.len = HASH_ResultLenByOidTag(tag); |
| if (!hashItem.len) |
| goto loser; /* error code has been set */ |
| |
| rv = PK11_HashBuf(tag, hash, randStr.integer.data, randStr.integer.len); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| hashItem.data = hash; |
| if (SECITEM_CompareItem(&hashItem, &challenge->witness) != SECEqual) { |
| /* The hash for the data we decrypted doesn't match the hash provided |
| * in the challenge. Bail out. |
| */ |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| rv = SECFailure; |
| goto loser; |
| } |
| rv = PK11_HashBuf(tag, hash, challenge->senderDER.data, |
| challenge->senderDER.len); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (SECITEM_CompareItem(&hashItem, &randStr.senderHash) != SECEqual) { |
| /* The hash for the data we decrypted doesn't match the hash provided |
| * in the challenge. Bail out. |
| */ |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| rv = SECFailure; |
| goto loser; |
| } |
| /* All of the hashes have verified, so we can now store the integer away.*/ |
| rv = SECITEM_CopyItem(inChalCont->poolp, &challenge->randomNumber, |
| &randStr.integer); |
| loser: |
| if (poolp) { |
| PORT_FreeArena(poolp, PR_FALSE); |
| } |
| return rv; |
| } |
| |
| SECStatus |
| CMMF_POPODecKeyChallContentGetRandomNumber(CMMFPOPODecKeyChallContent *inKeyChallCont, |
| int inIndex, |
| long *inDest) |
| { |
| CMMFChallenge *challenge; |
| |
| PORT_Assert(inKeyChallCont != NULL); |
| if (inKeyChallCont == NULL || inIndex > 0 || inIndex >= inKeyChallCont->numChallenges) { |
| return SECFailure; |
| } |
| challenge = inKeyChallCont->challenges[inIndex]; |
| if (challenge->randomNumber.data == NULL) { |
| /* There is no random number here, nothing to see. */ |
| return SECFailure; |
| } |
| *inDest = DER_GetInteger(&challenge->randomNumber); |
| return (*inDest == -1) ? SECFailure : SECSuccess; |
| } |
| |
| SECStatus |
| CMMF_EncodePOPODecKeyRespContent(long *inDecodedRand, |
| int inNumRand, |
| CRMFEncoderOutputCallback inCallback, |
| void *inArg) |
| { |
| PLArenaPool *poolp; |
| CMMFPOPODecKeyRespContent *response; |
| SECItem *currItem; |
| SECStatus rv = SECFailure; |
| int i; |
| |
| poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
| if (poolp == NULL) { |
| return SECFailure; |
| } |
| response = PORT_ArenaZNew(poolp, CMMFPOPODecKeyRespContent); |
| if (response == NULL) { |
| goto loser; |
| } |
| response->responses = PORT_ArenaZNewArray(poolp, SECItem *, inNumRand + 1); |
| if (response->responses == NULL) { |
| goto loser; |
| } |
| for (i = 0; i < inNumRand; i++) { |
| currItem = response->responses[i] = PORT_ArenaZNew(poolp, SECItem); |
| if (currItem == NULL) { |
| goto loser; |
| } |
| currItem = SEC_ASN1EncodeInteger(poolp, currItem, inDecodedRand[i]); |
| if (currItem == NULL) { |
| goto loser; |
| } |
| } |
| rv = cmmf_user_encode(response, inCallback, inArg, |
| CMMFPOPODecKeyRespContentTemplate); |
| loser: |
| if (poolp != NULL) { |
| PORT_FreeArena(poolp, PR_FALSE); |
| } |
| return rv; |
| } |