tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }