tor-browser

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

p12dec.c (19806B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "pkcs12.h"
      6 #include "plarena.h"
      7 #include "secpkcs7.h"
      8 #include "p12local.h"
      9 #include "secoid.h"
     10 #include "secitem.h"
     11 #include "secport.h"
     12 #include "secasn1.h"
     13 #include "secder.h"
     14 #include "secerr.h"
     15 #include "cert.h"
     16 #include "certdb.h"
     17 #include "p12plcy.h"
     18 #include "p12.h"
     19 #include "secpkcs5.h"
     20 
     21 /* PFX extraction and validation routines */
     22 
     23 /* decode the DER encoded PFX item.  if unable to decode, check to see if it
     24 * is an older PFX item.  If that fails, assume the file was not a valid
     25 * pfx file.
     26 * the returned pfx structure should be destroyed using SEC_PKCS12DestroyPFX
     27 */
     28 static SEC_PKCS12PFXItem *
     29 sec_pkcs12_decode_pfx(SECItem *der_pfx)
     30 {
     31    SEC_PKCS12PFXItem *pfx;
     32    SECStatus rv;
     33 
     34    if (der_pfx == NULL) {
     35        return NULL;
     36    }
     37 
     38    /* allocate the space for a new PFX item */
     39    pfx = sec_pkcs12_new_pfx();
     40    if (pfx == NULL) {
     41        return NULL;
     42    }
     43 
     44    rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate,
     45                            der_pfx);
     46 
     47    /* if a failure occurred, check for older version...
     48     * we also get rid of the old pfx structure, because we don't
     49     * know where it failed and what data in may contain
     50     */
     51    if (rv != SECSuccess) {
     52        SEC_PKCS12DestroyPFX(pfx);
     53        pfx = sec_pkcs12_new_pfx();
     54        if (pfx == NULL) {
     55            return NULL;
     56        }
     57        rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate_OLD,
     58                                der_pfx);
     59        if (rv != SECSuccess) {
     60            PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX);
     61            PORT_FreeArena(pfx->poolp, PR_TRUE);
     62            return NULL;
     63        }
     64        pfx->old = PR_TRUE;
     65        rv = SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac);
     66        if (rv != SECSuccess) {
     67            PORT_SetError(SEC_ERROR_NO_MEMORY);
     68            PORT_FreeArena(pfx->poolp, PR_TRUE);
     69            return NULL;
     70        }
     71        rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt);
     72        if (rv != SECSuccess) {
     73            PORT_SetError(SEC_ERROR_NO_MEMORY);
     74            PORT_FreeArena(pfx->poolp, PR_TRUE);
     75            return NULL;
     76        }
     77    } else {
     78        pfx->old = PR_FALSE;
     79    }
     80 
     81    /* convert bit string from bits to bytes */
     82    pfx->macData.macSalt.len /= 8;
     83 
     84    return pfx;
     85 }
     86 
     87 /* validate the integrity MAC used in the PFX.  The MAC is generated
     88 * per the PKCS 12 document.  If the MAC is incorrect, it is most likely
     89 * due to an invalid password.
     90 * pwitem is the integrity password
     91 * pfx is the decoded pfx item
     92 */
     93 static PRBool
     94 sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem *pfx,
     95                         SECItem *pwitem)
     96 {
     97    SECItem *key = NULL, *mac = NULL, *data = NULL;
     98    SECItem *vpwd = NULL;
     99    SECOidTag algorithm;
    100    PRBool ret = PR_FALSE;
    101 
    102    if (pfx == NULL) {
    103        return PR_FALSE;
    104    }
    105 
    106    algorithm = SECOID_GetAlgorithmTag(&pfx->macData.safeMac.digestAlgorithm);
    107    switch (algorithm) {
    108        /* only SHA1 hashing supported as a MACing algorithm */
    109        case SEC_OID_SHA1:
    110            if (pfx->old == PR_FALSE) {
    111                pfx->swapUnicode = PR_FALSE;
    112            }
    113 
    114        recheckUnicodePassword:
    115            vpwd = sec_pkcs12_create_virtual_password(pwitem,
    116                                                      &pfx->macData.macSalt,
    117                                                      pfx->swapUnicode);
    118            if (vpwd == NULL) {
    119                return PR_FALSE;
    120            }
    121 
    122            key = sec_pkcs12_generate_key_from_password(algorithm,
    123                                                        &pfx->macData.macSalt,
    124                                                        (pfx->old ? pwitem : vpwd));
    125            /* free vpwd only for newer PFX */
    126            if (vpwd) {
    127                SECITEM_ZfreeItem(vpwd, PR_TRUE);
    128            }
    129            if (key == NULL) {
    130                return PR_FALSE;
    131            }
    132 
    133            data = SEC_PKCS7GetContent(&pfx->authSafe);
    134            if (data == NULL) {
    135                break;
    136            }
    137 
    138            /* check MAC */
    139            mac = sec_pkcs12_generate_mac(key, data, pfx->old);
    140            ret = PR_TRUE;
    141            if (mac) {
    142                SECItem *safeMac = &pfx->macData.safeMac.digest;
    143                if (SECITEM_CompareItem(mac, safeMac) != SECEqual) {
    144 
    145                    /* if we encounter an invalid mac, lets invert the
    146                     * password in case of unicode changes
    147                     */
    148                    if (((!pfx->old) && pfx->swapUnicode) || (pfx->old)) {
    149                        PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
    150                        ret = PR_FALSE;
    151                    } else {
    152                        SECITEM_ZfreeItem(mac, PR_TRUE);
    153                        pfx->swapUnicode = PR_TRUE;
    154                        goto recheckUnicodePassword;
    155                    }
    156                }
    157                SECITEM_ZfreeItem(mac, PR_TRUE);
    158            } else {
    159                ret = PR_FALSE;
    160            }
    161            break;
    162        default:
    163            PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM);
    164            ret = PR_FALSE;
    165            break;
    166    }
    167 
    168    /* let success fall through */
    169    if (key != NULL)
    170        SECITEM_ZfreeItem(key, PR_TRUE);
    171 
    172    return ret;
    173 }
    174 
    175 /* check the validity of the pfx structure.  we currently only support
    176 * password integrity mode, so we check the MAC.
    177 */
    178 static PRBool
    179 sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem *pfx,
    180                        SECItem *pwitem)
    181 {
    182    SECOidTag contentType;
    183 
    184    contentType = SEC_PKCS7ContentType(&pfx->authSafe);
    185    switch (contentType) {
    186        case SEC_OID_PKCS7_DATA:
    187            return sec_pkcs12_check_pfx_mac(pfx, pwitem);
    188            break;
    189        case SEC_OID_PKCS7_SIGNED_DATA:
    190        default:
    191            PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
    192            break;
    193    }
    194 
    195    return PR_FALSE;
    196 }
    197 
    198 /* decode and return the valid PFX.  if the PFX item is not valid,
    199 * NULL is returned.
    200 */
    201 static SEC_PKCS12PFXItem *
    202 sec_pkcs12_get_pfx(SECItem *pfx_data,
    203                   SECItem *pwitem)
    204 {
    205    SEC_PKCS12PFXItem *pfx;
    206    PRBool valid_pfx;
    207 
    208    if ((pfx_data == NULL) || (pwitem == NULL)) {
    209        return NULL;
    210    }
    211 
    212    pfx = sec_pkcs12_decode_pfx(pfx_data);
    213    if (pfx == NULL) {
    214        return NULL;
    215    }
    216 
    217    valid_pfx = sec_pkcs12_validate_pfx(pfx, pwitem);
    218    if (valid_pfx != PR_TRUE) {
    219        SEC_PKCS12DestroyPFX(pfx);
    220        pfx = NULL;
    221    }
    222 
    223    return pfx;
    224 }
    225 
    226 /* authenticated safe decoding, validation, and access routines
    227 */
    228 
    229 /* convert dogbert beta 3 authenticated safe structure to a post
    230 * beta three structure, so that we don't have to change more routines.
    231 */
    232 static SECStatus
    233 sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
    234 {
    235    SEC_PKCS12Baggage *baggage;
    236    SEC_PKCS12BaggageItem *bag;
    237    SECStatus rv = SECSuccess;
    238 
    239    if (asafe->old_baggage.espvks == NULL) {
    240        /* XXX should the ASN1 engine produce a single NULL element list
    241         * rather than setting the pointer to NULL?
    242         * There is no need to return an error -- assume that the list
    243         * was empty.
    244         */
    245        return SECSuccess;
    246    }
    247 
    248    baggage = sec_pkcs12_create_baggage(asafe->poolp);
    249    if (!baggage) {
    250        return SECFailure;
    251    }
    252    bag = sec_pkcs12_create_external_bag(baggage);
    253    if (!bag) {
    254        return SECFailure;
    255    }
    256 
    257    PORT_Memcpy(&asafe->baggage, baggage, sizeof(SEC_PKCS12Baggage));
    258 
    259    /* if there are shrouded keys, append them to the bag */
    260    rv = SECSuccess;
    261    if (asafe->old_baggage.espvks[0] != NULL) {
    262        int nEspvk = 0;
    263        rv = SECSuccess;
    264        while ((asafe->old_baggage.espvks[nEspvk] != NULL) &&
    265               (rv == SECSuccess)) {
    266            rv = sec_pkcs12_append_shrouded_key(bag,
    267                                                asafe->old_baggage.espvks[nEspvk]);
    268            nEspvk++;
    269        }
    270    }
    271 
    272    return rv;
    273 }
    274 
    275 /* decodes the authenticated safe item.  a return of NULL indicates
    276 * an error.  however, the error will have occurred either in memory
    277 * allocation or in decoding the authenticated safe.
    278 *
    279 * if an old PFX item has been found, we want to convert the
    280 * old authenticated safe to the new one.
    281 */
    282 static SEC_PKCS12AuthenticatedSafe *
    283 sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem *pfx)
    284 {
    285    SECItem *der_asafe = NULL;
    286    SEC_PKCS12AuthenticatedSafe *asafe = NULL;
    287    SECStatus rv;
    288 
    289    if (pfx == NULL) {
    290        return NULL;
    291    }
    292 
    293    der_asafe = SEC_PKCS7GetContent(&pfx->authSafe);
    294    if (der_asafe == NULL) {
    295        /* XXX set error ? */
    296        goto loser;
    297    }
    298 
    299    asafe = sec_pkcs12_new_asafe(pfx->poolp);
    300    if (asafe == NULL) {
    301        goto loser;
    302    }
    303 
    304    if (pfx->old == PR_FALSE) {
    305        rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
    306                                SEC_PKCS12AuthenticatedSafeTemplate,
    307                                der_asafe);
    308        asafe->old = PR_FALSE;
    309        asafe->swapUnicode = pfx->swapUnicode;
    310    } else {
    311        /* handle beta exported files */
    312        rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
    313                                SEC_PKCS12AuthenticatedSafeTemplate_OLD,
    314                                der_asafe);
    315        asafe->safe = &(asafe->old_safe);
    316        rv = sec_pkcs12_convert_old_auth_safe(asafe);
    317        asafe->old = PR_TRUE;
    318    }
    319 
    320    if (rv != SECSuccess) {
    321        goto loser;
    322    }
    323 
    324    asafe->poolp = pfx->poolp;
    325 
    326    return asafe;
    327 
    328 loser:
    329    return NULL;
    330 }
    331 
    332 /* validates the safe within the authenticated safe item.
    333 * in order to be valid:
    334 *  1.  the privacy salt must be present
    335 *  2.  the encryption algorithm must be supported (including
    336 *      export policy)
    337 * PR_FALSE indicates an error, PR_TRUE indicates a valid safe
    338 */
    339 static PRBool
    340 sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe *asafe)
    341 {
    342    PRBool valid = PR_FALSE;
    343    SECAlgorithmID *algid;
    344 
    345    if (asafe == NULL) {
    346        return PR_FALSE;
    347    }
    348 
    349    /* if mode is password privacy, then privacySalt is assumed
    350     * to be non-zero.
    351     */
    352    if (asafe->privacySalt.len != 0) {
    353        valid = PR_TRUE;
    354        asafe->privacySalt.len /= 8;
    355    } else {
    356        PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
    357        return PR_FALSE;
    358    }
    359 
    360    /* until spec changes, content will have between 2 and 8 bytes depending
    361     * upon the algorithm used if certs are unencrypted...
    362     * also want to support case where content is empty -- which we produce
    363     */
    364    if (SEC_PKCS7IsContentEmpty(asafe->safe, 8) == PR_TRUE) {
    365        asafe->emptySafe = PR_TRUE;
    366        return PR_TRUE;
    367    }
    368 
    369    asafe->emptySafe = PR_FALSE;
    370 
    371    /* make sure that a pbe algorithm is being used */
    372    algid = SEC_PKCS7GetEncryptionAlgorithm(asafe->safe);
    373    if (algid != NULL) {
    374        if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
    375            valid = SEC_PKCS12DecryptionAllowed(algid);
    376 
    377            if (valid == PR_FALSE) {
    378                PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    379            }
    380        } else {
    381            PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
    382            valid = PR_FALSE;
    383        }
    384    } else {
    385        valid = PR_FALSE;
    386        PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
    387    }
    388 
    389    return valid;
    390 }
    391 
    392 /* validates authenticates safe:
    393 *  1.  checks that the version is supported
    394 *  2.  checks that only password privacy mode is used (currently)
    395 *  3.  further, makes sure safe has appropriate policies per above function
    396 * PR_FALSE indicates failure.
    397 */
    398 static PRBool
    399 sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
    400 {
    401    PRBool valid = PR_TRUE;
    402    SECOidTag safe_type;
    403    int version;
    404 
    405    if (asafe == NULL) {
    406        return PR_FALSE;
    407    }
    408 
    409    /* check version, since it is default it may not be present.
    410     * therefore, assume ok
    411     */
    412    if ((asafe->version.len > 0) && (asafe->old == PR_FALSE)) {
    413        version = DER_GetInteger(&asafe->version);
    414        if (version > SEC_PKCS12_PFX_VERSION) {
    415            PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION);
    416            return PR_FALSE;
    417        }
    418    }
    419 
    420    /* validate password mode is being used */
    421    safe_type = SEC_PKCS7ContentType(asafe->safe);
    422    switch (safe_type) {
    423        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    424            valid = sec_pkcs12_validate_encrypted_safe(asafe);
    425            break;
    426        case SEC_OID_PKCS7_ENVELOPED_DATA:
    427        default:
    428            PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
    429            valid = PR_FALSE;
    430            break;
    431    }
    432 
    433    return valid;
    434 }
    435 
    436 /* retrieves the authenticated safe item from the PFX item
    437 *  before returning the authenticated safe, the validity of the
    438 *  authenticated safe is checked and if valid, returned.
    439 * a return of NULL indicates that an error occurred.
    440 */
    441 static SEC_PKCS12AuthenticatedSafe *
    442 sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem *pfx)
    443 {
    444    SEC_PKCS12AuthenticatedSafe *asafe;
    445    PRBool valid_safe;
    446 
    447    if (pfx == NULL) {
    448        return NULL;
    449    }
    450 
    451    asafe = sec_pkcs12_decode_authenticated_safe(pfx);
    452    if (asafe == NULL) {
    453        return NULL;
    454    }
    455 
    456    valid_safe = sec_pkcs12_validate_auth_safe(asafe);
    457    if (valid_safe != PR_TRUE) {
    458        asafe = NULL;
    459    } else if (asafe) {
    460        asafe->baggage.poolp = asafe->poolp;
    461    }
    462 
    463    return asafe;
    464 }
    465 
    466 /* decrypts the authenticated safe.
    467 * a return of anything but SECSuccess indicates an error.  the
    468 * password is not known to be valid until the call to the
    469 * function sec_pkcs12_get_safe_contents.  If decoding the safe
    470 * fails, it is assumed the password was incorrect and the error
    471 * is set then.  any failure here is assumed to be due to
    472 * internal problems in SEC_PKCS7DecryptContents or below.
    473 */
    474 static SECStatus
    475 sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe,
    476                             SECItem *pwitem,
    477                             void *wincx)
    478 {
    479    SECStatus rv = SECFailure;
    480    SECItem *vpwd = NULL;
    481 
    482    if ((asafe == NULL) || (pwitem == NULL)) {
    483        return SECFailure;
    484    }
    485 
    486    if (asafe->old == PR_FALSE) {
    487        vpwd = sec_pkcs12_create_virtual_password(pwitem, &asafe->privacySalt,
    488                                                  asafe->swapUnicode);
    489        if (vpwd == NULL) {
    490            return SECFailure;
    491        }
    492    }
    493 
    494    rv = SEC_PKCS7DecryptContents(asafe->poolp, asafe->safe,
    495                                  (asafe->old ? pwitem : vpwd), wincx);
    496 
    497    if (asafe->old == PR_FALSE) {
    498        SECITEM_ZfreeItem(vpwd, PR_TRUE);
    499    }
    500 
    501    return rv;
    502 }
    503 
    504 /* extract the safe from the authenticated safe.
    505 *  if we are unable to decode the safe, then it is likely that the
    506 *  safe has not been decrypted or the password used to decrypt
    507 *  the safe was invalid.  we assume that the password was invalid and
    508 *  set an error accordingly.
    509 * a return of NULL indicates that an error occurred.
    510 */
    511 static SEC_PKCS12SafeContents *
    512 sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe *asafe)
    513 {
    514    SECItem *src = NULL;
    515    SEC_PKCS12SafeContents *safe = NULL;
    516    SECStatus rv = SECFailure;
    517 
    518    if (asafe == NULL) {
    519        return NULL;
    520    }
    521 
    522    safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(asafe->poolp,
    523                                                      sizeof(SEC_PKCS12SafeContents));
    524    if (safe == NULL) {
    525        return NULL;
    526    }
    527    safe->poolp = asafe->poolp;
    528    safe->old = asafe->old;
    529    safe->swapUnicode = asafe->swapUnicode;
    530 
    531    src = SEC_PKCS7GetContent(asafe->safe);
    532    if (src != NULL) {
    533        const SEC_ASN1Template *theTemplate;
    534        if (asafe->old != PR_TRUE) {
    535            theTemplate = SEC_PKCS12SafeContentsTemplate;
    536        } else {
    537            theTemplate = SEC_PKCS12SafeContentsTemplate_OLD;
    538        }
    539 
    540        rv = SEC_ASN1DecodeItem(asafe->poolp, safe, theTemplate, src);
    541 
    542        /* if we could not decode the item, password was probably invalid */
    543        if (rv != SECSuccess) {
    544            safe = NULL;
    545            PORT_SetError(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT);
    546        }
    547    } else {
    548        PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
    549        rv = SECFailure;
    550    }
    551 
    552    return safe;
    553 }
    554 
    555 /* import PFX item
    556 * der_pfx is the der encoded pfx structure
    557 * pbef and pbearg are the integrity/encryption password call back
    558 * ncCall is the nickname collision calllback
    559 * slot is the destination token
    560 * wincx window handler
    561 *
    562 * on error, error code set and SECFailure returned
    563 */
    564 SECStatus
    565 SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem,
    566                 SEC_PKCS12NicknameCollisionCallback ncCall,
    567                 PK11SlotInfo *slot,
    568                 void *wincx)
    569 {
    570    SEC_PKCS12PFXItem *pfx;
    571    SEC_PKCS12AuthenticatedSafe *asafe;
    572    SEC_PKCS12SafeContents *safe_contents = NULL;
    573    SECStatus rv;
    574 
    575    if (!der_pfx || !pwitem || !slot) {
    576        return SECFailure;
    577    }
    578 
    579    /* decode and validate each section */
    580    rv = SECFailure;
    581 
    582    pfx = sec_pkcs12_get_pfx(der_pfx, pwitem);
    583    if (pfx != NULL) {
    584        asafe = sec_pkcs12_get_auth_safe(pfx);
    585        if (asafe != NULL) {
    586 
    587            /* decrypt safe -- only if not empty */
    588            if (asafe->emptySafe != PR_TRUE) {
    589                rv = sec_pkcs12_decrypt_auth_safe(asafe, pwitem, wincx);
    590                if (rv == SECSuccess) {
    591                    safe_contents = sec_pkcs12_get_safe_contents(asafe);
    592                    if (safe_contents == NULL) {
    593                        rv = SECFailure;
    594                    }
    595                }
    596            } else {
    597                safe_contents = sec_pkcs12_create_safe_contents(asafe->poolp);
    598                if (safe_contents == NULL) {
    599                    rv = SECFailure;
    600                } else {
    601                    safe_contents->swapUnicode = pfx->swapUnicode;
    602                    rv = SECSuccess;
    603                }
    604            }
    605 
    606            /* get safe contents and begin import */
    607            if (rv == SECSuccess) {
    608                SEC_PKCS12DecoderContext *p12dcx;
    609 
    610                p12dcx = sec_PKCS12ConvertOldSafeToNew(pfx->poolp, slot,
    611                                                       pfx->swapUnicode,
    612                                                       pwitem, wincx, safe_contents,
    613                                                       &asafe->baggage);
    614                if (!p12dcx) {
    615                    rv = SECFailure;
    616                    goto loser;
    617                }
    618 
    619                if (SEC_PKCS12DecoderValidateBags(p12dcx, ncCall) != SECSuccess) {
    620                    rv = SECFailure;
    621                    goto loser;
    622                }
    623 
    624                rv = SEC_PKCS12DecoderImportBags(p12dcx);
    625            }
    626        }
    627    }
    628 
    629 loser:
    630 
    631    if (pfx) {
    632        SEC_PKCS12DestroyPFX(pfx);
    633    }
    634 
    635    return rv;
    636 }
    637 
    638 PRBool
    639 SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength)
    640 {
    641    int lengthLength;
    642 
    643    PRBool valid = PR_FALSE;
    644 
    645    if (buf == NULL) {
    646        return PR_FALSE;
    647    }
    648 
    649    /* check for constructed sequence identifier tag */
    650    if (*buf == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) {
    651        totalLength--; /* header byte taken care of */
    652        buf++;
    653 
    654        lengthLength = (long int)SEC_ASN1LengthLength(totalLength - 1);
    655        if (totalLength > 0x7f) {
    656            lengthLength--;
    657            *buf &= 0x7f; /* remove bit 8 indicator */
    658            if ((*buf - (char)lengthLength) == 0) {
    659                valid = PR_TRUE;
    660            }
    661        } else {
    662            lengthLength--;
    663            if ((*buf - (char)lengthLength) == 0) {
    664                valid = PR_TRUE;
    665            }
    666        }
    667    }
    668 
    669    return valid;
    670 }