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 *
sec_pkcs12_decode_pfx(SECItem * der_pfx)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
sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem * pfx,SECItem * pwitem)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
sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem * pfx,SECItem * pwitem)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 *
sec_pkcs12_get_pfx(SECItem * pfx_data,SECItem * pwitem)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
sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe * asafe)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 *
sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem * pfx)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
sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe * asafe)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
sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe * asafe)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 *
sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem * pfx)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
sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe * asafe,SECItem * pwitem,void * wincx)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 *
sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe * asafe)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
SEC_PKCS12PutPFX(SECItem * der_pfx,SECItem * pwitem,SEC_PKCS12NicknameCollisionCallback ncCall,PK11SlotInfo * slot,void * wincx)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
SEC_PKCS12ValidData(char * buf,int bufLen,long int totalLength)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 }
671