]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2003-2004,2011,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * pkcs12Decode.h - P12Coder decoding engine. | |
26 | */ | |
27 | ||
28 | #include "pkcs12Coder.h" | |
29 | #include "pkcs12Templates.h" | |
30 | #include "pkcs12Utils.h" | |
31 | #include "pkcs12Debug.h" | |
32 | #include "pkcs12Crypto.h" | |
33 | #include <security_cdsa_utilities/cssmerrors.h> | |
34 | #include <security_asn1/nssUtils.h> | |
35 | ||
36 | /* top-level PKCS12 PFX decoder */ | |
37 | void P12Coder::decode( | |
38 | CFDataRef cdpfx) | |
39 | { | |
40 | SecNssCoder localCdr; | |
41 | NSS_P12_DecodedPFX pfx; | |
42 | ||
43 | p12DecodeLog("decode"); | |
44 | memset(&pfx, 0, sizeof(pfx)); | |
45 | const CSSM_DATA rawBlob = {CFDataGetLength(cdpfx), | |
46 | (uint8 *)CFDataGetBytePtr(cdpfx)}; | |
47 | ||
48 | if(localCdr.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) { | |
49 | p12ErrorLog("Error on top-level decode of NSS_P12_DecodedPFX\n"); | |
50 | P12_THROW_DECODE; | |
51 | } | |
52 | NSS_P7_DecodedContentInfo &dci = pfx.authSafe; | |
53 | if(dci.type != CT_Data) { | |
54 | /* no other types supported yet */ | |
55 | p12ErrorLog("bad top-level contentType\n"); | |
56 | P12_THROW_DECODE; | |
57 | } | |
58 | mIntegrityMode = kSecPkcs12ModePassword; | |
59 | ||
60 | if(pfx.macData == NULL) { | |
61 | /* not present is an error in kSecPkcs12ModePassword */ | |
62 | p12ErrorLog("no MAC in PFX\n"); | |
63 | P12_THROW_DECODE; | |
64 | } | |
65 | macParse(*pfx.macData, localCdr); | |
66 | ||
67 | const CSSM_DATA *macPhrase = getMacPassPhrase(); | |
68 | const CSSM_KEY *macPassKey = getMacPassKey(); | |
69 | if((macPhrase == NULL) && (macPassKey == NULL)) { | |
70 | p12ErrorLog("no passphrase set\n"); | |
71 | CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); | |
72 | } | |
73 | CSSM_RETURN crtn = p12VerifyMac(pfx, mCspHand, macPhrase, | |
74 | macPassKey, localCdr); | |
75 | if(crtn) { | |
76 | p12LogCssmError("p12VerifyMac", crtn); | |
77 | CssmError::throwMe(errSecPkcs12VerifyFailure); | |
78 | } | |
79 | ||
80 | authSafeParse(*dci.content.data, localCdr); | |
81 | ||
82 | /* | |
83 | * On success, if we have a keychain, store certs and CRLs there | |
84 | */ | |
85 | if(mKeychain != NULL) { | |
86 | storeDecodeResults(); | |
87 | } | |
88 | } | |
89 | ||
90 | /* | |
91 | * Decrypt the contents of a NSS_P7_EncryptedData | |
92 | */ | |
93 | void P12Coder::encryptedDataDecrypt( | |
94 | const NSS_P7_EncryptedData &edata, | |
95 | SecNssCoder &localCdr, | |
96 | NSS_P12_PBE_Params *pbep, // preparsed | |
97 | CSSM_DATA &ptext) // result goes here in localCdr space | |
98 | { | |
99 | p12DecodeLog("encryptedDataDecrypt"); | |
100 | ||
101 | /* see if we can grok the encr alg */ | |
102 | CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES | |
103 | CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE | |
104 | CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 | |
105 | uint32 keySizeInBits; | |
106 | uint32 blockSizeInBytes; // for IV, optional | |
107 | CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. | |
108 | CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. | |
109 | PKCS_Which pkcs; | |
110 | ||
111 | bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm, | |
112 | keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, | |
113 | padding, mode, pkcs); | |
114 | if(!found || (pkcs != PW_PKCS12)) { | |
115 | p12ErrorLog("EncryptedData encrAlg not understood\n"); | |
116 | CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); | |
117 | } | |
118 | ||
119 | uint32 iterCount; | |
120 | if(!p12DataToInt(pbep->iterations, iterCount)) { | |
121 | p12ErrorLog("encryptedDataDecrypt: badly formed iterCount\n"); | |
122 | P12_THROW_DECODE; | |
123 | } | |
124 | const CSSM_DATA *pwd = getEncrPassPhrase(); | |
125 | const CSSM_KEY *passKey = getEncrPassKey(); | |
126 | if((pwd == NULL) && (passKey == NULL)) { | |
127 | p12ErrorLog("no passphrase set\n"); | |
128 | CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); | |
129 | } | |
130 | ||
131 | /* go */ | |
132 | CSSM_RETURN crtn = p12Decrypt(mCspHand, | |
133 | edata.contentInfo.encrContent, | |
134 | keyAlg, encrAlg, pbeHashAlg, | |
135 | keySizeInBits, blockSizeInBytes, | |
136 | padding, mode, | |
137 | iterCount, pbep->salt, | |
138 | pwd, | |
139 | passKey, | |
140 | localCdr, | |
141 | ptext); | |
142 | if(crtn) { | |
143 | CssmError::throwMe(crtn); | |
144 | } | |
145 | } | |
146 | ||
147 | ||
148 | /* | |
149 | * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12. | |
150 | * Decode the alg params as a NSS_P12_PBE_Params and parse and | |
151 | * return the result if the pbeParams is non-NULL. | |
152 | */ | |
153 | void P12Coder::algIdParse( | |
154 | const CSSM_X509_ALGORITHM_IDENTIFIER &algId, | |
155 | NSS_P12_PBE_Params *pbeParams, // optional | |
156 | SecNssCoder &localCdr) | |
157 | { | |
158 | p12DecodeLog("algIdParse"); | |
159 | ||
160 | const CSSM_DATA ¶m = algId.parameters; | |
161 | if(pbeParams == NULL) { | |
162 | /* alg params are uninterpreted */ | |
163 | return; | |
164 | } | |
165 | ||
166 | if(param.Length == 0) { | |
167 | p12ErrorLog("algIdParse: no alg parameters\n"); | |
168 | P12_THROW_DECODE; | |
169 | } | |
170 | ||
171 | memset(pbeParams, 0, sizeof(*pbeParams)); | |
172 | if(localCdr.decodeItem(param, | |
173 | NSS_P12_PBE_ParamsTemplate, pbeParams)) { | |
174 | p12ErrorLog("Error decoding NSS_P12_PBE_Params\n"); | |
175 | P12_THROW_DECODE; | |
176 | } | |
177 | } | |
178 | ||
179 | /* | |
180 | * Parse a NSS_P7_EncryptedData - specifically in the context | |
181 | * of a P12 in password privacy mode. (The latter assumption is | |
182 | * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters | |
183 | * format). | |
184 | */ | |
185 | void P12Coder::encryptedDataParse( | |
186 | const NSS_P7_EncryptedData &edata, | |
187 | SecNssCoder &localCdr, | |
188 | NSS_P12_PBE_Params *pbep) // optional, RETURNED | |
189 | { | |
190 | p12DecodeLog("encryptedDataParse"); | |
191 | ||
192 | /* | |
193 | * Parse the alg ID, save PBE params for when we do the decrypt | |
194 | * key unwrap | |
195 | */ | |
196 | const NSS_P7_EncrContentInfo &ci = edata.contentInfo; | |
197 | const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg; | |
198 | algIdParse(algId, pbep, localCdr); | |
199 | } | |
200 | ||
201 | /* | |
202 | * ShroudedKeyBag parser w/decrypt | |
203 | */ | |
204 | void P12Coder::shroudedKeyBagParse( | |
205 | const NSS_P12_SafeBag &safeBag, | |
206 | SecNssCoder &localCdr) | |
207 | { | |
208 | p12DecodeLog("Found shrouded key bag"); | |
209 | if(mPrivKeyImportState == PKIS_NoMore) { | |
210 | CssmError::throwMe(errSecMultiplePrivKeys); | |
211 | } | |
212 | ||
213 | const NSS_P12_ShroudedKeyBag *keyBag = safeBag.bagValue.shroudedKeyBag; | |
214 | const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm; | |
215 | NSS_P12_PBE_Params pbep; | |
216 | algIdParse(algId, &pbep, localCdr); | |
217 | ||
218 | /* | |
219 | * Prepare for decryption | |
220 | */ | |
221 | CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES | |
222 | CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE | |
223 | CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 | |
224 | uint32 keySizeInBits; | |
225 | uint32 blockSizeInBytes; // for IV, optional | |
226 | CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. | |
227 | CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. | |
228 | PKCS_Which pkcs; | |
229 | ||
230 | bool found = pkcsOidToParams(&algId.algorithm, | |
231 | keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, | |
232 | padding, mode, pkcs); | |
233 | if(!found || (pkcs != PW_PKCS12)) { | |
234 | p12ErrorLog("ShroudedKeyBag encrAlg not understood\n"); | |
235 | CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); | |
236 | } | |
237 | ||
238 | uint32 iterCount; | |
239 | if(!p12DataToInt(pbep.iterations, iterCount)) { | |
240 | p12ErrorLog("ShroudedKeyBag: badly formed iterCount\n"); | |
241 | P12_THROW_DECODE; | |
242 | } | |
243 | const CSSM_DATA *encrPhrase = getEncrPassPhrase(); | |
244 | const CSSM_KEY *passKey = getEncrPassKey(); | |
245 | if((encrPhrase == NULL) && (passKey == NULL)) { | |
246 | p12ErrorLog("no passphrase set\n"); | |
247 | CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); | |
248 | } | |
249 | ||
250 | /* We'll own the actual CSSM_KEY memory */ | |
251 | CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)mCoder.malloc(sizeof(CSSM_KEY)); | |
252 | memset(privKey, 0, sizeof(CSSM_KEY)); | |
253 | ||
254 | CSSM_DATA labelData; | |
255 | p12GenLabel(labelData, localCdr); | |
256 | ||
257 | CSSM_RETURN crtn = p12UnwrapKey(mCspHand, | |
258 | mDlDbHand.DLHandle ? &mDlDbHand : NULL, | |
259 | mImportFlags & kSecImportKeys, | |
260 | keyBag->encryptedData, | |
261 | keyAlg, encrAlg, pbeHashAlg, | |
262 | keySizeInBits, blockSizeInBytes, | |
263 | padding, mode, | |
264 | iterCount, pbep.salt, | |
265 | encrPhrase, | |
266 | passKey, | |
267 | localCdr, | |
268 | labelData, | |
269 | mAccess, | |
270 | mNoAcl, | |
271 | mKeyUsage, | |
272 | mKeyAttrs, | |
273 | privKey); | |
274 | if(crtn) { | |
275 | p12ErrorLog("Error unwrapping private key\n"); | |
276 | CssmError::throwMe(crtn); | |
277 | } | |
278 | p12DecodeLog("unwrapped shrouded key bag"); | |
279 | ||
280 | P12KeyBag *p12bag = new P12KeyBag(privKey, mCspHand, | |
281 | safeBag.bagAttrs, labelData, mCoder); | |
282 | addKey(p12bag); | |
283 | ||
284 | if(mPrivKeyImportState == PKIS_AllowOne) { | |
285 | mPrivKeyImportState = PKIS_NoMore; | |
286 | } | |
287 | } | |
288 | ||
289 | /* | |
290 | * (unshrouded) KeyBag parser | |
291 | */ | |
292 | void P12Coder::keyBagParse( | |
293 | const NSS_P12_SafeBag &safeBag, | |
294 | SecNssCoder &localCdr) | |
295 | { | |
296 | if(mPrivKeyImportState == PKIS_NoMore) { | |
297 | CssmError::throwMe(errSecMultiplePrivKeys); | |
298 | } | |
299 | ||
300 | /* FIXME - should be able to parse and handle this.... */ | |
301 | p12DecodeLog("found keyBag"); | |
302 | NSS_P12_KeyBag *keyBag = safeBag.bagValue.keyBag; | |
303 | P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, | |
304 | /* this breaks when NSS_P12_KeyBag is not a CSSM_DATA */ | |
305 | *keyBag, | |
306 | safeBag.bagAttrs, | |
307 | mCoder); | |
308 | addOpaque(p12Bag); | |
309 | } | |
310 | ||
311 | /* | |
312 | * CertBag parser | |
313 | */ | |
314 | void P12Coder::certBagParse( | |
315 | const NSS_P12_SafeBag &safeBag, | |
316 | SecNssCoder &localCdr) | |
317 | { | |
318 | p12DecodeLog("found certBag"); | |
319 | NSS_P12_CertBag *certBag = safeBag.bagValue.certBag; | |
320 | switch(certBag->type) { | |
321 | case CT_X509: | |
322 | case CT_SDSI: | |
323 | break; | |
324 | default: | |
325 | p12ErrorLog("certBagParse: unknown cert type\n"); | |
326 | P12_THROW_DECODE; | |
327 | } | |
328 | P12CertBag *p12Bag = new P12CertBag(certBag->type, | |
329 | certBag->certValue, | |
330 | safeBag.bagAttrs, | |
331 | mCoder); | |
332 | addCert(p12Bag); | |
333 | } | |
334 | ||
335 | /* | |
336 | * CrlBag parser | |
337 | */ | |
338 | void P12Coder::crlBagParse( | |
339 | const NSS_P12_SafeBag &safeBag, | |
340 | SecNssCoder &localCdr) | |
341 | { | |
342 | p12DecodeLog("found crlBag"); | |
343 | NSS_P12_CrlBag *crlBag = safeBag.bagValue.crlBag; | |
344 | switch(crlBag->type) { | |
345 | case CRT_X509: | |
346 | break; | |
347 | default: | |
348 | p12ErrorLog("crlBagParse: unknown CRL type\n"); | |
349 | P12_THROW_DECODE; | |
350 | } | |
351 | P12CrlBag *p12Bag = new P12CrlBag(crlBag->type, | |
352 | crlBag->crlValue, | |
353 | safeBag.bagAttrs, | |
354 | mCoder); | |
355 | addCrl(p12Bag); | |
356 | } | |
357 | ||
358 | /* | |
359 | * SecretBag parser | |
360 | */ | |
361 | void P12Coder::secretBagParse( | |
362 | const NSS_P12_SafeBag &safeBag, | |
363 | SecNssCoder &localCdr) | |
364 | { | |
365 | p12DecodeLog("found secretBag"); | |
366 | NSS_P12_SecretBag *secretBag = safeBag.bagValue.secretBag; | |
367 | P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, | |
368 | /* this breaks when NSS_P12_SecretBag is not a CSSM_DATA */ | |
369 | *secretBag, | |
370 | safeBag.bagAttrs, | |
371 | mCoder); | |
372 | addOpaque(p12Bag); | |
373 | } | |
374 | ||
375 | /* | |
376 | * SafeContentsBag parser | |
377 | */ | |
378 | void P12Coder::safeContentsBagParse( | |
379 | const NSS_P12_SafeBag &safeBag, | |
380 | SecNssCoder &localCdr) | |
381 | { | |
382 | p12DecodeLog("found SafeContents safe bag"); | |
383 | NSS_P12_SafeContentsBag *scBag = safeBag.bagValue.safeContentsBag; | |
384 | P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, | |
385 | /* this breaks when NSS_P12_SafeContentsBag is not a CSSM_DATA */ | |
386 | *scBag, | |
387 | safeBag.bagAttrs, | |
388 | mCoder); | |
389 | addOpaque(p12Bag); | |
390 | } | |
391 | ||
392 | /* | |
393 | * Parse an encoded NSS_P12_SafeContents. This could be either | |
394 | * present as plaintext in an AuthSafe or decrypted. | |
395 | */ | |
396 | void P12Coder::safeContentsParse( | |
397 | const CSSM_DATA &contentsBlob, | |
398 | SecNssCoder &localCdr) | |
399 | { | |
400 | p12DecodeLog("safeContentsParse"); | |
401 | ||
402 | NSS_P12_SafeContents sc; | |
403 | memset(&sc, 0, sizeof(sc)); | |
404 | if(localCdr.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate, | |
405 | &sc)) { | |
406 | p12ErrorLog("Error decoding SafeContents\n"); | |
407 | P12_THROW_DECODE; | |
408 | } | |
409 | unsigned numBags = nssArraySize((const void **)sc.bags); | |
410 | for(unsigned dex=0; dex<numBags; dex++) { | |
411 | NSS_P12_SafeBag *bag = sc.bags[dex]; | |
412 | assert(bag != NULL); | |
413 | ||
414 | /* ensure that *something* is there */ | |
415 | if(bag->bagValue.keyBag == NULL) { | |
416 | p12ErrorLog("safeContentsParse: Empty SafeBag\n"); | |
417 | P12_THROW_DECODE; | |
418 | } | |
419 | ||
420 | /* | |
421 | * Break out to individual bag type | |
422 | */ | |
423 | switch(bag->type) { | |
424 | case BT_KeyBag: | |
425 | keyBagParse(*bag, localCdr); | |
426 | break; | |
427 | case BT_ShroudedKeyBag: | |
428 | shroudedKeyBagParse(*bag, localCdr); | |
429 | break; | |
430 | case BT_CertBag: | |
431 | certBagParse(*bag, localCdr); | |
432 | break; | |
433 | case BT_CrlBag: | |
434 | crlBagParse(*bag, localCdr); | |
435 | break; | |
436 | case BT_SecretBag: | |
437 | secretBagParse(*bag ,localCdr); | |
438 | break; | |
439 | case BT_SafeContentsBag: | |
440 | safeContentsBagParse(*bag, localCdr); | |
441 | break; | |
442 | default: | |
443 | p12ErrorLog("unknown p12 BagType (%u)\n", | |
444 | (unsigned)bag->type); | |
445 | P12_THROW_DECODE; | |
446 | } | |
447 | } | |
448 | } | |
449 | ||
450 | /* | |
451 | * Parse a ContentInfo in the context of (i.e., as an element of) | |
452 | * an AuthenticatedSafe. | |
453 | */ | |
454 | void P12Coder::authSafeElementParse( | |
455 | const NSS_P7_DecodedContentInfo *info, | |
456 | SecNssCoder &localCdr) | |
457 | { | |
458 | p12DecodeLog("authSafeElementParse"); | |
459 | switch(info->type) { | |
460 | case CT_Data: | |
461 | /* unencrypted SafeContents */ | |
462 | safeContentsParse(*info->content.data, localCdr); | |
463 | break; | |
464 | ||
465 | case CT_EncryptedData: | |
466 | { | |
467 | NSS_P12_PBE_Params pbep; | |
468 | encryptedDataParse(*info->content.encryptData, localCdr, &pbep); | |
469 | ||
470 | /* | |
471 | * Decrypt contents to get a SafeContents and | |
472 | * then parse that. | |
473 | */ | |
474 | CSSM_DATA ptext = {0, NULL}; | |
475 | encryptedDataDecrypt(*info->content.encryptData, | |
476 | localCdr, &pbep, ptext); | |
477 | safeContentsParse(ptext, localCdr); | |
478 | break; | |
479 | } | |
480 | default: | |
481 | p12ErrorLog("authSafeElementParse: unknown sage type (%u)\n", | |
482 | (unsigned)info->type); | |
483 | ||
484 | /* well, save it as an opaque bag for now */ | |
485 | P12OpaqueBag *opaque = new P12OpaqueBag( | |
486 | info->contentType, *info->content.data, | |
487 | NULL, // no attrs | |
488 | localCdr); | |
489 | addOpaque(opaque); | |
490 | break; | |
491 | } | |
492 | } | |
493 | ||
494 | /* | |
495 | * Parse an encoded NSS_P12_AuthenticatedSafe | |
496 | */ | |
497 | void P12Coder::authSafeParse( | |
498 | const CSSM_DATA &authSafeBlob, | |
499 | SecNssCoder &localCdr) | |
500 | { | |
501 | p12DecodeLog("authSafeParse"); | |
502 | ||
503 | NSS_P12_AuthenticatedSafe authSafe; | |
504 | ||
505 | memset(&authSafe, 0, sizeof(authSafe)); | |
506 | if(localCdr.decodeItem(authSafeBlob, | |
507 | NSS_P12_AuthenticatedSafeTemplate, | |
508 | &authSafe)) { | |
509 | p12ErrorLog("Error decoding authSafe\n"); | |
510 | P12_THROW_DECODE; | |
511 | } | |
512 | unsigned numInfos = nssArraySize((const void **)authSafe.info); | |
513 | for(unsigned dex=0; dex<numInfos; dex++) { | |
514 | NSS_P7_DecodedContentInfo *info = authSafe.info[dex]; | |
515 | authSafeElementParse(info, localCdr); | |
516 | } | |
517 | } | |
518 | ||
519 | void P12Coder::macParse( | |
520 | const NSS_P12_MacData &macData, | |
521 | SecNssCoder &localCdr) | |
522 | { | |
523 | p12DecodeLog("macParse"); | |
524 | algIdParse(macData.mac.digestAlgorithm, NULL, localCdr); | |
525 | const CSSM_DATA &iter = macData.iterations; | |
526 | if(iter.Length > 4) { | |
527 | p12ErrorLog("malformed iteration length (%u)\n", | |
528 | (unsigned)iter.Length); | |
529 | P12_THROW_DECODE; | |
530 | } | |
531 | } | |
532 |