crmfpop.c (18415B)
1 /* -*- Mode: C; tab-width: 8 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "crmf.h" 7 #include "crmfi.h" 8 #include "secasn1.h" 9 #include "keyhi.h" 10 #include "cryptohi.h" 11 12 #define CRMF_DEFAULT_ALLOC_SIZE 1024U 13 14 SECStatus 15 crmf_init_encoder_callback_arg(struct crmfEncoderArg *encoderArg, 16 SECItem *derDest) 17 { 18 derDest->data = PORT_ZNewArray(unsigned char, CRMF_DEFAULT_ALLOC_SIZE); 19 if (derDest->data == NULL) { 20 return SECFailure; 21 } 22 derDest->len = 0; 23 encoderArg->allocatedLen = CRMF_DEFAULT_ALLOC_SIZE; 24 encoderArg->buffer = derDest; 25 return SECSuccess; 26 } 27 28 /* Caller should release or unmark the pool, instead of doing it here. 29 ** But there are NO callers of this function at present... 30 */ 31 SECStatus 32 CRMF_CertReqMsgSetRAVerifiedPOP(CRMFCertReqMsg *inCertReqMsg) 33 { 34 CRMFProofOfPossession *pop; 35 PLArenaPool *poolp; 36 void *mark; 37 38 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL); 39 poolp = inCertReqMsg->poolp; 40 mark = PORT_ArenaMark(poolp); 41 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) { 42 return SECFailure; 43 } 44 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); 45 if (pop == NULL) { 46 goto loser; 47 } 48 pop->popUsed = crmfRAVerified; 49 pop->popChoice.raVerified.data = NULL; 50 pop->popChoice.raVerified.len = 0; 51 inCertReqMsg->pop = pop; 52 (void)SEC_ASN1EncodeItem(poolp, &(inCertReqMsg->derPOP), 53 &(pop->popChoice.raVerified), 54 CRMFRAVerifiedTemplate); 55 return SECSuccess; 56 loser: 57 PORT_ArenaRelease(poolp, mark); 58 return SECFailure; 59 } 60 61 static SECOidTag 62 crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey) 63 { 64 /* maintain backward compatibility with older 65 * implementations */ 66 if (inPubKey->keyType == rsaKey) { 67 return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; 68 } 69 return SEC_GetSignatureAlgorithmOidTagByKey(NULL, inPubKey, SEC_OID_UNKNOWN); 70 } 71 72 static SECAlgorithmID * 73 crmf_create_poposignkey_algid(PLArenaPool *poolp, 74 SECKEYPublicKey *inPubKey) 75 { 76 SECAlgorithmID *algID; 77 SECOidTag tag; 78 SECStatus rv; 79 void *mark; 80 81 mark = PORT_ArenaMark(poolp); 82 algID = PORT_ArenaZNew(poolp, SECAlgorithmID); 83 if (algID == NULL) { 84 goto loser; 85 } 86 tag = crmf_get_key_sign_tag(inPubKey); 87 if (tag == SEC_OID_UNKNOWN) { 88 goto loser; 89 } 90 rv = SECOID_SetAlgorithmID(poolp, algID, tag, NULL); 91 if (rv != SECSuccess) { 92 goto loser; 93 } 94 PORT_ArenaUnmark(poolp, mark); 95 return algID; 96 loser: 97 PORT_ArenaRelease(poolp, mark); 98 return NULL; 99 } 100 101 static CRMFPOPOSigningKeyInput * 102 crmf_create_poposigningkeyinput(PLArenaPool *poolp, CERTCertificate *inCert, 103 CRMFMACPasswordCallback fn, void *arg) 104 { 105 /* PSM isn't going to do this, so we'll fail here for now.*/ 106 return NULL; 107 } 108 109 void 110 crmf_generic_encoder_callback(void *arg, const char *buf, unsigned long len, 111 int depth, SEC_ASN1EncodingPart data_kind) 112 { 113 struct crmfEncoderArg *encoderArg = (struct crmfEncoderArg *)arg; 114 unsigned char *cursor; 115 116 if (encoderArg->buffer->len + len > encoderArg->allocatedLen) { 117 int newSize = encoderArg->buffer->len + CRMF_DEFAULT_ALLOC_SIZE; 118 void *dummy = PORT_Realloc(encoderArg->buffer->data, newSize); 119 if (dummy == NULL) { 120 /* I really want to return an error code here */ 121 PORT_Assert(0); 122 return; 123 } 124 encoderArg->buffer->data = dummy; 125 encoderArg->allocatedLen = newSize; 126 } 127 cursor = &(encoderArg->buffer->data[encoderArg->buffer->len]); 128 if (len) { 129 PORT_Memcpy(cursor, buf, len); 130 } 131 encoderArg->buffer->len += len; 132 } 133 134 static SECStatus 135 crmf_encode_certreq(CRMFCertRequest *inCertReq, SECItem *derDest) 136 { 137 struct crmfEncoderArg encoderArg; 138 SECStatus rv; 139 140 rv = crmf_init_encoder_callback_arg(&encoderArg, derDest); 141 if (rv != SECSuccess) { 142 return SECFailure; 143 } 144 return SEC_ASN1Encode(inCertReq, CRMFCertRequestTemplate, 145 crmf_generic_encoder_callback, &encoderArg); 146 } 147 148 static SECStatus 149 crmf_sign_certreq(PLArenaPool *poolp, 150 CRMFPOPOSigningKey *crmfSignKey, 151 CRMFCertRequest *certReq, 152 SECKEYPrivateKey *inKey, 153 SECAlgorithmID *inAlgId) 154 { 155 SECItem derCertReq = { siBuffer, NULL, 0 }; 156 SECItem certReqSig = { siBuffer, NULL, 0 }; 157 SECStatus rv = SECSuccess; 158 159 rv = crmf_encode_certreq(certReq, &derCertReq); 160 if (rv != SECSuccess) { 161 goto loser; 162 } 163 rv = SEC_SignData(&certReqSig, derCertReq.data, derCertReq.len, 164 inKey, SECOID_GetAlgorithmTag(inAlgId)); 165 if (rv != SECSuccess) { 166 goto loser; 167 } 168 169 /* Now make it a part of the POPOSigningKey */ 170 rv = SECITEM_CopyItem(poolp, &(crmfSignKey->signature), &certReqSig); 171 /* Convert this length to number of bits */ 172 crmfSignKey->signature.len <<= 3; 173 174 loser: 175 if (derCertReq.data != NULL) { 176 PORT_Free(derCertReq.data); 177 } 178 if (certReqSig.data != NULL) { 179 PORT_Free(certReqSig.data); 180 } 181 return rv; 182 } 183 184 static SECStatus 185 crmf_create_poposignkey(PLArenaPool *poolp, 186 CRMFCertReqMsg *inCertReqMsg, 187 CRMFPOPOSigningKeyInput *signKeyInput, 188 SECKEYPrivateKey *inPrivKey, 189 SECAlgorithmID *inAlgID, 190 CRMFPOPOSigningKey *signKey) 191 { 192 CRMFCertRequest *certReq; 193 void *mark; 194 PRBool useSignKeyInput; 195 SECStatus rv; 196 197 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL); 198 mark = PORT_ArenaMark(poolp); 199 if (signKey == NULL) { 200 goto loser; 201 } 202 certReq = inCertReqMsg->certReq; 203 useSignKeyInput = !(CRMF_DoesRequestHaveField(certReq, crmfSubject) && 204 CRMF_DoesRequestHaveField(certReq, crmfPublicKey)); 205 206 if (useSignKeyInput) { 207 goto loser; 208 } else { 209 rv = crmf_sign_certreq(poolp, signKey, certReq, inPrivKey, inAlgID); 210 if (rv != SECSuccess) { 211 goto loser; 212 } 213 } 214 PORT_ArenaUnmark(poolp, mark); 215 return SECSuccess; 216 loser: 217 PORT_ArenaRelease(poolp, mark); 218 return SECFailure; 219 } 220 221 SECStatus 222 CRMF_CertReqMsgSetSignaturePOP(CRMFCertReqMsg *inCertReqMsg, 223 SECKEYPrivateKey *inPrivKey, 224 SECKEYPublicKey *inPubKey, 225 CERTCertificate *inCertForInput, 226 CRMFMACPasswordCallback fn, 227 void *arg) 228 { 229 SECAlgorithmID *algID; 230 PLArenaPool *poolp; 231 SECItem derTemp = { siBuffer, NULL, 0 }; 232 void *mark; 233 SECStatus rv; 234 CRMFPOPOSigningKeyInput *signKeyInput = NULL; 235 CRMFCertRequest *certReq; 236 CRMFProofOfPossession *pop; 237 struct crmfEncoderArg encoderArg; 238 239 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL && 240 inCertReqMsg->pop == NULL); 241 certReq = inCertReqMsg->certReq; 242 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice || 243 !CRMF_DoesRequestHaveField(certReq, crmfPublicKey)) { 244 return SECFailure; 245 } 246 poolp = inCertReqMsg->poolp; 247 mark = PORT_ArenaMark(poolp); 248 algID = crmf_create_poposignkey_algid(poolp, inPubKey); 249 250 if (!CRMF_DoesRequestHaveField(certReq, crmfSubject)) { 251 signKeyInput = crmf_create_poposigningkeyinput(poolp, inCertForInput, 252 fn, arg); 253 if (signKeyInput == NULL) { 254 goto loser; 255 } 256 } 257 258 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); 259 if (pop == NULL) { 260 goto loser; 261 } 262 263 rv = crmf_create_poposignkey(poolp, inCertReqMsg, 264 signKeyInput, inPrivKey, algID, 265 &(pop->popChoice.signature)); 266 if (rv != SECSuccess) { 267 goto loser; 268 } 269 270 pop->popUsed = crmfSignature; 271 pop->popChoice.signature.algorithmIdentifier = algID; 272 inCertReqMsg->pop = pop; 273 274 rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp); 275 if (rv != SECSuccess) { 276 goto loser; 277 } 278 rv = SEC_ASN1Encode(&pop->popChoice.signature, 279 CRMFPOPOSigningKeyTemplate, 280 crmf_generic_encoder_callback, &encoderArg); 281 if (rv != SECSuccess) { 282 goto loser; 283 } 284 rv = SECITEM_CopyItem(poolp, &(inCertReqMsg->derPOP), &derTemp); 285 if (rv != SECSuccess) { 286 goto loser; 287 } 288 PORT_Free(derTemp.data); 289 PORT_ArenaUnmark(poolp, mark); 290 return SECSuccess; 291 292 loser: 293 PORT_ArenaRelease(poolp, mark); 294 if (derTemp.data != NULL) { 295 PORT_Free(derTemp.data); 296 } 297 return SECFailure; 298 } 299 300 static const SEC_ASN1Template * 301 crmf_get_popoprivkey_subtemplate(CRMFPOPOPrivKey *inPrivKey) 302 { 303 const SEC_ASN1Template *retTemplate = NULL; 304 305 switch (inPrivKey->messageChoice) { 306 case crmfThisMessage: 307 retTemplate = CRMFThisMessageTemplate; 308 break; 309 case crmfSubsequentMessage: 310 retTemplate = CRMFSubsequentMessageTemplate; 311 break; 312 case crmfDHMAC: 313 retTemplate = CRMFDHMACTemplate; 314 break; 315 default: 316 retTemplate = NULL; 317 } 318 return retTemplate; 319 } 320 321 static SECStatus 322 crmf_encode_popoprivkey(PLArenaPool *poolp, 323 CRMFCertReqMsg *inCertReqMsg, 324 CRMFPOPOPrivKey *popoPrivKey, 325 const SEC_ASN1Template *privKeyTemplate) 326 { 327 struct crmfEncoderArg encoderArg; 328 SECItem derTemp = { siBuffer, NULL, 0 }; 329 SECStatus rv; 330 void *mark; 331 const SEC_ASN1Template *subDerTemplate; 332 333 mark = PORT_ArenaMark(poolp); 334 rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp); 335 if (rv != SECSuccess) { 336 goto loser; 337 } 338 subDerTemplate = crmf_get_popoprivkey_subtemplate(popoPrivKey); 339 /* We've got a union, so a pointer to one item is a pointer to 340 * all the items in the union. 341 */ 342 rv = SEC_ASN1Encode(&popoPrivKey->message.thisMessage, 343 subDerTemplate, 344 crmf_generic_encoder_callback, &encoderArg); 345 if (rv != SECSuccess) { 346 goto loser; 347 } 348 if (encoderArg.allocatedLen > derTemp.len + 2) { 349 void *dummy = PORT_Realloc(derTemp.data, derTemp.len + 2); 350 if (dummy == NULL) { 351 goto loser; 352 } 353 derTemp.data = dummy; 354 } 355 PORT_Memmove(&derTemp.data[2], &derTemp.data[0], derTemp.len); 356 /* I couldn't figure out how to get the ASN1 encoder to implicitly 357 * tag an implicitly tagged der blob. So I'm putting in the outter- 358 * most tag myself. -javi 359 */ 360 derTemp.data[0] = (unsigned char)privKeyTemplate->kind; 361 derTemp.data[1] = (unsigned char)derTemp.len; 362 derTemp.len += 2; 363 rv = SECITEM_CopyItem(poolp, &inCertReqMsg->derPOP, &derTemp); 364 if (rv != SECSuccess) { 365 goto loser; 366 } 367 PORT_Free(derTemp.data); 368 PORT_ArenaUnmark(poolp, mark); 369 return SECSuccess; 370 loser: 371 PORT_ArenaRelease(poolp, mark); 372 if (derTemp.data) { 373 PORT_Free(derTemp.data); 374 } 375 return SECFailure; 376 } 377 378 static const SEC_ASN1Template * 379 crmf_get_template_for_privkey(CRMFPOPChoice inChoice) 380 { 381 switch (inChoice) { 382 case crmfKeyAgreement: 383 return CRMFPOPOKeyAgreementTemplate; 384 case crmfKeyEncipherment: 385 return CRMFPOPOKeyEnciphermentTemplate; 386 default: 387 break; 388 } 389 return NULL; 390 } 391 392 static SECStatus 393 crmf_add_privkey_thismessage(CRMFCertReqMsg *inCertReqMsg, SECItem *encPrivKey, 394 CRMFPOPChoice inChoice) 395 { 396 PLArenaPool *poolp; 397 void *mark; 398 CRMFPOPOPrivKey *popoPrivKey; 399 CRMFProofOfPossession *pop; 400 SECStatus rv; 401 402 PORT_Assert(inCertReqMsg != NULL && encPrivKey != NULL); 403 poolp = inCertReqMsg->poolp; 404 mark = PORT_ArenaMark(poolp); 405 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); 406 if (pop == NULL) { 407 goto loser; 408 } 409 pop->popUsed = inChoice; 410 /* popChoice is a union, so getting a pointer to one 411 * field gives me a pointer to the other fields as 412 * well. This in essence points to both 413 * pop->popChoice.keyEncipherment and 414 * pop->popChoice.keyAgreement 415 */ 416 popoPrivKey = &pop->popChoice.keyEncipherment; 417 418 rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.thisMessage), 419 encPrivKey); 420 if (rv != SECSuccess) { 421 goto loser; 422 } 423 popoPrivKey->message.thisMessage.len <<= 3; 424 popoPrivKey->messageChoice = crmfThisMessage; 425 inCertReqMsg->pop = pop; 426 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey, 427 crmf_get_template_for_privkey(inChoice)); 428 if (rv != SECSuccess) { 429 goto loser; 430 } 431 PORT_ArenaUnmark(poolp, mark); 432 return SECSuccess; 433 434 loser: 435 PORT_ArenaRelease(poolp, mark); 436 return SECFailure; 437 } 438 439 static SECStatus 440 crmf_add_privkey_dhmac(CRMFCertReqMsg *inCertReqMsg, SECItem *dhmac, 441 CRMFPOPChoice inChoice) 442 { 443 PLArenaPool *poolp; 444 void *mark; 445 CRMFPOPOPrivKey *popoPrivKey; 446 CRMFProofOfPossession *pop; 447 SECStatus rv; 448 449 PORT_Assert(inCertReqMsg != NULL && dhmac != NULL); 450 poolp = inCertReqMsg->poolp; 451 mark = PORT_ArenaMark(poolp); 452 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); 453 if (pop == NULL) { 454 goto loser; 455 } 456 pop->popUsed = inChoice; 457 popoPrivKey = &pop->popChoice.keyAgreement; 458 459 rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.dhMAC), 460 dhmac); 461 if (rv != SECSuccess) { 462 goto loser; 463 } 464 popoPrivKey->message.dhMAC.len <<= 3; 465 popoPrivKey->messageChoice = crmfDHMAC; 466 inCertReqMsg->pop = pop; 467 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey, 468 crmf_get_template_for_privkey(inChoice)); 469 if (rv != SECSuccess) { 470 goto loser; 471 } 472 PORT_ArenaUnmark(poolp, mark); 473 return SECSuccess; 474 475 loser: 476 PORT_ArenaRelease(poolp, mark); 477 return SECFailure; 478 } 479 480 static SECStatus 481 crmf_add_privkey_subseqmessage(CRMFCertReqMsg *inCertReqMsg, 482 CRMFSubseqMessOptions subsequentMessage, 483 CRMFPOPChoice inChoice) 484 { 485 void *mark; 486 PLArenaPool *poolp; 487 CRMFProofOfPossession *pop; 488 CRMFPOPOPrivKey *popoPrivKey; 489 SECStatus rv; 490 const SEC_ASN1Template *privKeyTemplate; 491 492 if (subsequentMessage == crmfNoSubseqMess) { 493 return SECFailure; 494 } 495 poolp = inCertReqMsg->poolp; 496 mark = PORT_ArenaMark(poolp); 497 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); 498 if (pop == NULL) { 499 goto loser; 500 } 501 502 pop->popUsed = inChoice; 503 /* 504 * We have a union, so a pointer to one member of the union 505 * is also a member to another member of that same union. 506 */ 507 popoPrivKey = &pop->popChoice.keyEncipherment; 508 509 switch (subsequentMessage) { 510 case crmfEncrCert: 511 rv = crmf_encode_integer(poolp, 512 &(popoPrivKey->message.subsequentMessage), 513 0); 514 break; 515 case crmfChallengeResp: 516 rv = crmf_encode_integer(poolp, 517 &(popoPrivKey->message.subsequentMessage), 518 1); 519 break; 520 default: 521 goto loser; 522 } 523 if (rv != SECSuccess) { 524 goto loser; 525 } 526 popoPrivKey->messageChoice = crmfSubsequentMessage; 527 privKeyTemplate = crmf_get_template_for_privkey(inChoice); 528 inCertReqMsg->pop = pop; 529 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey, 530 privKeyTemplate); 531 532 if (rv != SECSuccess) { 533 goto loser; 534 } 535 PORT_ArenaUnmark(poolp, mark); 536 return SECSuccess; 537 loser: 538 PORT_ArenaRelease(poolp, mark); 539 return SECFailure; 540 } 541 542 SECStatus 543 CRMF_CertReqMsgSetKeyEnciphermentPOP(CRMFCertReqMsg *inCertReqMsg, 544 CRMFPOPOPrivKeyChoice inKeyChoice, 545 CRMFSubseqMessOptions subseqMess, 546 SECItem *encPrivKey) 547 { 548 SECStatus rv; 549 550 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL); 551 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) { 552 return SECFailure; 553 } 554 switch (inKeyChoice) { 555 case crmfThisMessage: 556 rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey, 557 crmfKeyEncipherment); 558 break; 559 case crmfSubsequentMessage: 560 rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess, 561 crmfKeyEncipherment); 562 break; 563 case crmfDHMAC: 564 default: 565 rv = SECFailure; 566 } 567 return rv; 568 } 569 570 SECStatus 571 CRMF_CertReqMsgSetKeyAgreementPOP(CRMFCertReqMsg *inCertReqMsg, 572 CRMFPOPOPrivKeyChoice inKeyChoice, 573 CRMFSubseqMessOptions subseqMess, 574 SECItem *encPrivKey) 575 { 576 SECStatus rv; 577 578 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL); 579 switch (inKeyChoice) { 580 case crmfThisMessage: 581 rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey, 582 crmfKeyAgreement); 583 break; 584 case crmfSubsequentMessage: 585 rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess, 586 crmfKeyAgreement); 587 break; 588 case crmfDHMAC: 589 /* In this case encPrivKey should be the calculated dhMac 590 * as specified in RFC 2511 */ 591 rv = crmf_add_privkey_dhmac(inCertReqMsg, encPrivKey, 592 crmfKeyAgreement); 593 break; 594 default: 595 rv = SECFailure; 596 } 597 return rv; 598 }