Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C; tab-width: 8 -*-*/ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "cmmf.h" |
michael@0 | 7 | #include "cmmfi.h" |
michael@0 | 8 | #include "sechash.h" |
michael@0 | 9 | #include "genname.h" |
michael@0 | 10 | #include "pk11func.h" |
michael@0 | 11 | #include "cert.h" |
michael@0 | 12 | #include "secitem.h" |
michael@0 | 13 | #include "secmod.h" |
michael@0 | 14 | #include "keyhi.h" |
michael@0 | 15 | |
michael@0 | 16 | static int |
michael@0 | 17 | cmmf_create_witness_and_challenge(PLArenaPool *poolp, |
michael@0 | 18 | CMMFChallenge *challenge, |
michael@0 | 19 | long inRandom, |
michael@0 | 20 | SECItem *senderDER, |
michael@0 | 21 | SECKEYPublicKey *inPubKey, |
michael@0 | 22 | void *passwdArg) |
michael@0 | 23 | { |
michael@0 | 24 | SECItem *encodedRandNum; |
michael@0 | 25 | SECItem encodedRandStr = {siBuffer, NULL, 0}; |
michael@0 | 26 | SECItem *dummy; |
michael@0 | 27 | unsigned char *randHash, *senderHash, *encChal=NULL; |
michael@0 | 28 | unsigned modulusLen = 0; |
michael@0 | 29 | SECStatus rv = SECFailure; |
michael@0 | 30 | CMMFRand randStr= { {siBuffer, NULL, 0}, {siBuffer, NULL, 0}}; |
michael@0 | 31 | PK11SlotInfo *slot; |
michael@0 | 32 | PK11SymKey *symKey = NULL; |
michael@0 | 33 | CK_OBJECT_HANDLE id; |
michael@0 | 34 | CERTSubjectPublicKeyInfo *spki = NULL; |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | encodedRandNum = SEC_ASN1EncodeInteger(poolp, &challenge->randomNumber, |
michael@0 | 38 | inRandom); |
michael@0 | 39 | encodedRandNum = &challenge->randomNumber; |
michael@0 | 40 | randHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH); |
michael@0 | 41 | senderHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH); |
michael@0 | 42 | if (randHash == NULL) { |
michael@0 | 43 | goto loser; |
michael@0 | 44 | } |
michael@0 | 45 | rv = PK11_HashBuf(SEC_OID_SHA1, randHash, encodedRandNum->data, |
michael@0 | 46 | (PRUint32)encodedRandNum->len); |
michael@0 | 47 | if (rv != SECSuccess) { |
michael@0 | 48 | goto loser; |
michael@0 | 49 | } |
michael@0 | 50 | rv = PK11_HashBuf(SEC_OID_SHA1, senderHash, senderDER->data, |
michael@0 | 51 | (PRUint32)senderDER->len); |
michael@0 | 52 | if (rv != SECSuccess) { |
michael@0 | 53 | goto loser; |
michael@0 | 54 | } |
michael@0 | 55 | challenge->witness.data = randHash; |
michael@0 | 56 | challenge->witness.len = SHA1_LENGTH; |
michael@0 | 57 | |
michael@0 | 58 | randStr.integer = *encodedRandNum; |
michael@0 | 59 | randStr.senderHash.data = senderHash; |
michael@0 | 60 | randStr.senderHash.len = SHA1_LENGTH; |
michael@0 | 61 | dummy = SEC_ASN1EncodeItem(NULL, &encodedRandStr, &randStr, |
michael@0 | 62 | CMMFRandTemplate); |
michael@0 | 63 | if (dummy != &encodedRandStr) { |
michael@0 | 64 | rv = SECFailure; |
michael@0 | 65 | goto loser; |
michael@0 | 66 | } |
michael@0 | 67 | /* XXXX Now I have to encrypt encodedRandStr and stash it away. */ |
michael@0 | 68 | modulusLen = SECKEY_PublicKeyStrength(inPubKey); |
michael@0 | 69 | encChal = PORT_ArenaNewArray(poolp, unsigned char, modulusLen); |
michael@0 | 70 | if (encChal == NULL) { |
michael@0 | 71 | rv = SECFailure; |
michael@0 | 72 | goto loser; |
michael@0 | 73 | } |
michael@0 | 74 | slot =PK11_GetBestSlotWithAttributes(CKM_RSA_PKCS, CKF_WRAP, 0, passwdArg); |
michael@0 | 75 | if (slot == NULL) { |
michael@0 | 76 | rv = SECFailure; |
michael@0 | 77 | goto loser; |
michael@0 | 78 | } |
michael@0 | 79 | id = PK11_ImportPublicKey(slot, inPubKey, PR_FALSE); |
michael@0 | 80 | /* In order to properly encrypt the data, we import as a symmetric |
michael@0 | 81 | * key, and then wrap that key. That in essence encrypts the data. |
michael@0 | 82 | * This is the method recommended in the PK11 world in order |
michael@0 | 83 | * to prevent threading issues as well as breaking any other semantics |
michael@0 | 84 | * the PK11 libraries depend on. |
michael@0 | 85 | */ |
michael@0 | 86 | symKey = PK11_ImportSymKey(slot, CKM_RSA_PKCS, PK11_OriginGenerated, |
michael@0 | 87 | CKA_VALUE, &encodedRandStr, passwdArg); |
michael@0 | 88 | if (symKey == NULL) { |
michael@0 | 89 | rv = SECFailure; |
michael@0 | 90 | goto loser; |
michael@0 | 91 | } |
michael@0 | 92 | challenge->challenge.data = encChal; |
michael@0 | 93 | challenge->challenge.len = modulusLen; |
michael@0 | 94 | rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, inPubKey, symKey, |
michael@0 | 95 | &challenge->challenge); |
michael@0 | 96 | PK11_FreeSlot(slot); |
michael@0 | 97 | if (rv != SECSuccess) { |
michael@0 | 98 | goto loser; |
michael@0 | 99 | } |
michael@0 | 100 | rv = SECITEM_CopyItem(poolp, &challenge->senderDER, senderDER); |
michael@0 | 101 | crmf_get_public_value(inPubKey, &challenge->key); |
michael@0 | 102 | /* Fall through */ |
michael@0 | 103 | loser: |
michael@0 | 104 | if (spki != NULL) { |
michael@0 | 105 | SECKEY_DestroySubjectPublicKeyInfo(spki); |
michael@0 | 106 | } |
michael@0 | 107 | if (encodedRandStr.data != NULL) { |
michael@0 | 108 | PORT_Free(encodedRandStr.data); |
michael@0 | 109 | } |
michael@0 | 110 | if (encodedRandNum != NULL) { |
michael@0 | 111 | SECITEM_FreeItem(encodedRandNum, PR_TRUE); |
michael@0 | 112 | } |
michael@0 | 113 | if (symKey != NULL) { |
michael@0 | 114 | PK11_FreeSymKey(symKey); |
michael@0 | 115 | } |
michael@0 | 116 | return rv; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | static SECStatus |
michael@0 | 120 | cmmf_create_first_challenge(CMMFPOPODecKeyChallContent *challContent, |
michael@0 | 121 | long inRandom, |
michael@0 | 122 | SECItem *senderDER, |
michael@0 | 123 | SECKEYPublicKey *inPubKey, |
michael@0 | 124 | void *passwdArg) |
michael@0 | 125 | { |
michael@0 | 126 | SECOidData *oidData; |
michael@0 | 127 | CMMFChallenge *challenge; |
michael@0 | 128 | SECAlgorithmID *algId; |
michael@0 | 129 | PLArenaPool *poolp; |
michael@0 | 130 | SECStatus rv; |
michael@0 | 131 | |
michael@0 | 132 | oidData = SECOID_FindOIDByTag(SEC_OID_SHA1); |
michael@0 | 133 | if (oidData == NULL) { |
michael@0 | 134 | return SECFailure; |
michael@0 | 135 | } |
michael@0 | 136 | poolp = challContent->poolp; |
michael@0 | 137 | challenge = PORT_ArenaZNew(poolp, CMMFChallenge); |
michael@0 | 138 | if (challenge == NULL) { |
michael@0 | 139 | return SECFailure; |
michael@0 | 140 | } |
michael@0 | 141 | algId = challenge->owf = PORT_ArenaZNew(poolp, SECAlgorithmID); |
michael@0 | 142 | if (algId == NULL) { |
michael@0 | 143 | return SECFailure; |
michael@0 | 144 | } |
michael@0 | 145 | rv = SECITEM_CopyItem(poolp, &algId->algorithm, &oidData->oid); |
michael@0 | 146 | if (rv != SECSuccess) { |
michael@0 | 147 | return SECFailure; |
michael@0 | 148 | } |
michael@0 | 149 | rv = cmmf_create_witness_and_challenge(poolp, challenge, inRandom, |
michael@0 | 150 | senderDER, inPubKey, passwdArg); |
michael@0 | 151 | challContent->challenges[0] = (rv == SECSuccess) ? challenge : NULL; |
michael@0 | 152 | challContent->numChallenges++; |
michael@0 | 153 | return rv ; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | CMMFPOPODecKeyChallContent* |
michael@0 | 157 | CMMF_CreatePOPODecKeyChallContent (void) |
michael@0 | 158 | { |
michael@0 | 159 | PLArenaPool *poolp; |
michael@0 | 160 | CMMFPOPODecKeyChallContent *challContent; |
michael@0 | 161 | |
michael@0 | 162 | poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
michael@0 | 163 | if (poolp == NULL) { |
michael@0 | 164 | return NULL; |
michael@0 | 165 | } |
michael@0 | 166 | challContent = PORT_ArenaZNew(poolp, CMMFPOPODecKeyChallContent); |
michael@0 | 167 | if (challContent == NULL) { |
michael@0 | 168 | PORT_FreeArena(poolp, PR_FALSE); |
michael@0 | 169 | return NULL; |
michael@0 | 170 | } |
michael@0 | 171 | challContent->poolp = poolp; |
michael@0 | 172 | return challContent; |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | SECStatus |
michael@0 | 176 | CMMF_POPODecKeyChallContentSetNextChallenge |
michael@0 | 177 | (CMMFPOPODecKeyChallContent *inDecKeyChall, |
michael@0 | 178 | long inRandom, |
michael@0 | 179 | CERTGeneralName *inSender, |
michael@0 | 180 | SECKEYPublicKey *inPubKey, |
michael@0 | 181 | void *passwdArg) |
michael@0 | 182 | { |
michael@0 | 183 | CMMFChallenge *curChallenge; |
michael@0 | 184 | PLArenaPool *genNamePool = NULL, *poolp; |
michael@0 | 185 | SECStatus rv; |
michael@0 | 186 | SECItem *genNameDER; |
michael@0 | 187 | void *mark; |
michael@0 | 188 | |
michael@0 | 189 | PORT_Assert (inDecKeyChall != NULL && |
michael@0 | 190 | inSender != NULL && |
michael@0 | 191 | inPubKey != NULL); |
michael@0 | 192 | |
michael@0 | 193 | if (inDecKeyChall == NULL || |
michael@0 | 194 | inSender == NULL || inPubKey == NULL) { |
michael@0 | 195 | return SECFailure; |
michael@0 | 196 | } |
michael@0 | 197 | poolp = inDecKeyChall->poolp; |
michael@0 | 198 | mark = PORT_ArenaMark(poolp); |
michael@0 | 199 | |
michael@0 | 200 | genNamePool = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
michael@0 | 201 | genNameDER = CERT_EncodeGeneralName(inSender, NULL, genNamePool); |
michael@0 | 202 | if (genNameDER == NULL) { |
michael@0 | 203 | rv = SECFailure; |
michael@0 | 204 | goto loser; |
michael@0 | 205 | } |
michael@0 | 206 | if (inDecKeyChall->challenges == NULL) { |
michael@0 | 207 | inDecKeyChall->challenges = |
michael@0 | 208 | PORT_ArenaZNewArray(poolp, CMMFChallenge*,(CMMF_MAX_CHALLENGES+1)); |
michael@0 | 209 | inDecKeyChall->numAllocated = CMMF_MAX_CHALLENGES; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | if (inDecKeyChall->numChallenges >= inDecKeyChall->numAllocated) { |
michael@0 | 213 | rv = SECFailure; |
michael@0 | 214 | goto loser; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | if (inDecKeyChall->numChallenges == 0) { |
michael@0 | 218 | rv = cmmf_create_first_challenge(inDecKeyChall, inRandom, |
michael@0 | 219 | genNameDER, inPubKey, passwdArg); |
michael@0 | 220 | } else { |
michael@0 | 221 | curChallenge = PORT_ArenaZNew(poolp, CMMFChallenge); |
michael@0 | 222 | if (curChallenge == NULL) { |
michael@0 | 223 | rv = SECFailure; |
michael@0 | 224 | goto loser; |
michael@0 | 225 | } |
michael@0 | 226 | rv = cmmf_create_witness_and_challenge(poolp, curChallenge, inRandom, |
michael@0 | 227 | genNameDER, inPubKey, |
michael@0 | 228 | passwdArg); |
michael@0 | 229 | if (rv == SECSuccess) { |
michael@0 | 230 | inDecKeyChall->challenges[inDecKeyChall->numChallenges] = |
michael@0 | 231 | curChallenge; |
michael@0 | 232 | inDecKeyChall->numChallenges++; |
michael@0 | 233 | } |
michael@0 | 234 | } |
michael@0 | 235 | if (rv != SECSuccess) { |
michael@0 | 236 | goto loser; |
michael@0 | 237 | } |
michael@0 | 238 | PORT_ArenaUnmark(poolp, mark); |
michael@0 | 239 | PORT_FreeArena(genNamePool, PR_FALSE); |
michael@0 | 240 | return SECSuccess; |
michael@0 | 241 | |
michael@0 | 242 | loser: |
michael@0 | 243 | PORT_ArenaRelease(poolp, mark); |
michael@0 | 244 | if (genNamePool != NULL) { |
michael@0 | 245 | PORT_FreeArena(genNamePool, PR_FALSE); |
michael@0 | 246 | } |
michael@0 | 247 | PORT_Assert(rv != SECSuccess); |
michael@0 | 248 | return rv; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | SECStatus |
michael@0 | 252 | CMMF_DestroyPOPODecKeyRespContent(CMMFPOPODecKeyRespContent *inDecKeyResp) |
michael@0 | 253 | { |
michael@0 | 254 | PORT_Assert(inDecKeyResp != NULL); |
michael@0 | 255 | if (inDecKeyResp != NULL && inDecKeyResp->poolp != NULL) { |
michael@0 | 256 | PORT_FreeArena(inDecKeyResp->poolp, PR_FALSE); |
michael@0 | 257 | } |
michael@0 | 258 | return SECSuccess; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | int |
michael@0 | 262 | CMMF_POPODecKeyRespContentGetNumResponses(CMMFPOPODecKeyRespContent *inRespCont) |
michael@0 | 263 | { |
michael@0 | 264 | int numResponses = 0; |
michael@0 | 265 | |
michael@0 | 266 | PORT_Assert(inRespCont != NULL); |
michael@0 | 267 | if (inRespCont == NULL) { |
michael@0 | 268 | return 0; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | while (inRespCont->responses[numResponses] != NULL) { |
michael@0 | 272 | numResponses ++; |
michael@0 | 273 | } |
michael@0 | 274 | return numResponses; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | SECStatus |
michael@0 | 278 | CMMF_POPODecKeyRespContentGetResponse (CMMFPOPODecKeyRespContent *inRespCont, |
michael@0 | 279 | int inIndex, |
michael@0 | 280 | long *inDest) |
michael@0 | 281 | { |
michael@0 | 282 | PORT_Assert(inRespCont != NULL); |
michael@0 | 283 | |
michael@0 | 284 | if (inRespCont == NULL || inIndex < 0 || |
michael@0 | 285 | inIndex >= CMMF_POPODecKeyRespContentGetNumResponses(inRespCont)) { |
michael@0 | 286 | return SECFailure; |
michael@0 | 287 | } |
michael@0 | 288 | *inDest = DER_GetInteger(inRespCont->responses[inIndex]); |
michael@0 | 289 | return (*inDest == -1) ? SECFailure : SECSuccess; |
michael@0 | 290 | } |