]>
Commit | Line | Data |
---|---|---|
df0e469f A |
1 | /* |
2 | * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | /* | |
19 | * clNssUtils.cpp - support for libnssasn1-based ASN1 encode/decode | |
20 | */ | |
21 | ||
22 | #include "clNssUtils.h" | |
23 | #include "clNameUtils.h" | |
24 | #include "CSPAttacher.h" | |
25 | #include <SecurityNssAsn1/secasn1.h> | |
26 | #include <SecurityNssAsn1/SecNssCoder.h> | |
27 | #include <SecurityNssAsn1/nssUtils.h> | |
28 | #include <SecurityNssAsn1/keyTemplates.h> | |
29 | #include <SecurityNssAsn1/certExtensionTemplates.h> | |
30 | #include <Security/oidsalg.h> | |
31 | #include <Security/cssmapple.h> | |
32 | #include <string.h> | |
33 | ||
34 | #pragma mark ----- ArenaAllocator ----- | |
35 | ||
36 | /* | |
37 | * Avoid inlining this for debuggability | |
38 | */ | |
39 | void *ArenaAllocator::malloc(size_t len) throw(std::bad_alloc) | |
40 | { | |
41 | try { | |
42 | return mCoder.malloc(len); | |
43 | } | |
44 | catch (...) { | |
45 | throw std::bad_alloc(); | |
46 | } | |
47 | } | |
48 | ||
49 | /* intentionally not implemented, should never be called */ | |
50 | void ArenaAllocator::free(void *p) throw() | |
51 | { | |
52 | throw std::bad_alloc(); | |
53 | } | |
54 | ||
55 | void *ArenaAllocator::realloc(void *p, size_t len) throw(std::bad_alloc) | |
56 | { | |
57 | throw std::bad_alloc(); | |
58 | } | |
59 | ||
60 | #pragma mark ----- Malloc/Copy/Compare CSSM_DATA ----- | |
61 | ||
62 | /* | |
63 | * Misc. alloc/copy with arbitrary CssmAllocator | |
64 | */ | |
65 | /* malloc d.Data, set d.Length */ | |
66 | void clAllocData( | |
67 | CssmAllocator &alloc, | |
68 | CSSM_DATA &dst, | |
69 | size_t len) | |
70 | { | |
71 | if(len == 0) { | |
72 | dst.Data = NULL; | |
73 | } | |
74 | else { | |
75 | dst.Data = (uint8 *)alloc.malloc(len); | |
76 | } | |
77 | dst.Length = len; | |
78 | } | |
79 | ||
80 | /* malloc and copy */ | |
81 | void clAllocCopyData( | |
82 | CssmAllocator &alloc, | |
83 | const CSSM_DATA &src, | |
84 | CSSM_DATA &dst) | |
85 | { | |
86 | clAllocData(alloc, dst, src.Length); | |
87 | if(dst.Length != 0) { | |
88 | memmove(dst.Data, src.Data, src.Length); | |
89 | } | |
90 | } | |
91 | ||
92 | /* | |
93 | * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical. | |
94 | */ | |
95 | bool clCompareCssmData( | |
96 | const CSSM_DATA *data1, | |
97 | const CSSM_DATA *data2) | |
98 | { | |
99 | if((data1 == NULL) || (data1->Data == NULL) || | |
100 | (data2 == NULL) || (data2->Data == NULL) || | |
101 | (data1->Length != data2->Length)) { | |
102 | return false; | |
103 | } | |
104 | if(data1->Length != data2->Length) { | |
105 | return false; | |
106 | } | |
107 | if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { | |
108 | return true; | |
109 | } | |
110 | else { | |
111 | return false; | |
112 | } | |
113 | } | |
114 | ||
115 | #pragma mark ----- CSSM_DATA <--> uint32 ----- | |
116 | ||
117 | uint32 clDataToInt( | |
118 | const CSSM_DATA &cdata, | |
119 | CSSM_RETURN toThrow) /* = CSSMERR_CL_INVALID_CERT_POINTER */ | |
120 | { | |
121 | if((cdata.Length == 0) || (cdata.Data == NULL)) { | |
122 | return 0; | |
123 | } | |
124 | uint32 len = cdata.Length; | |
125 | if(len > sizeof(uint32)) { | |
126 | CssmError::throwMe(toThrow); | |
127 | } | |
128 | ||
129 | uint32 rtn = 0; | |
130 | uint8 *cp = cdata.Data; | |
131 | for(uint32 i=0; i<len; i++) { | |
132 | rtn = (rtn << 8) | *cp++; | |
133 | } | |
134 | return rtn; | |
135 | } | |
136 | ||
137 | void clIntToData( | |
138 | uint32 num, | |
139 | CSSM_DATA &cdata, | |
140 | CssmAllocator &alloc) | |
141 | { | |
142 | uint32 len = 0; | |
143 | ||
144 | if(num < 0x100) { | |
145 | len = 1; | |
146 | } | |
147 | else if(num < 0x10000) { | |
148 | len = 2; | |
149 | } | |
150 | else if(num < 0x1000000) { | |
151 | len = 3; | |
152 | } | |
153 | else { | |
154 | len = 4; | |
155 | } | |
156 | clAllocData(alloc, cdata, len); | |
157 | uint8 *cp = &cdata.Data[len - 1]; | |
158 | for(unsigned i=0; i<len; i++) { | |
159 | *cp-- = num & 0xff; | |
160 | num >>= 8; | |
161 | } | |
162 | } | |
163 | ||
164 | #pragma mark ----- CSSM_BOOL <--> CSSM_DATA ----- | |
165 | /* | |
166 | * A Bool is encoded as one byte of either 0 or 0xff | |
167 | * Default of NSS boolean not present is false | |
168 | */ | |
169 | CSSM_BOOL clNssBoolToCssm( | |
170 | const CSSM_DATA &nssBool) | |
171 | { | |
172 | if((nssBool.Data != NULL) && (nssBool.Data[0] == 0xff)) { | |
173 | return CSSM_TRUE; | |
174 | } | |
175 | else { | |
176 | return CSSM_FALSE; | |
177 | } | |
178 | } | |
179 | ||
180 | void clCssmBoolToNss( | |
181 | CSSM_BOOL cBool, | |
182 | CSSM_DATA &nssBool, | |
183 | CssmAllocator &alloc) | |
184 | { | |
185 | uint32 num = cBool ? 0xff : 0; | |
186 | clIntToData(num, nssBool, alloc); | |
187 | } | |
188 | ||
189 | #pragma mark ----- Bit String manipulation ----- | |
190 | ||
191 | /* | |
192 | * Adjust the length of a CSSM_DATA representing a pre-encoded | |
193 | * bit string. On entry the length field is the number of bytes | |
194 | * of data; en exit, the number if bits. Trailing zero bits | |
195 | * are counted as unused (which is how KeyUsage and NetscapeCertType | |
196 | * extensions are encoded). | |
197 | */ | |
198 | void clCssmBitStringToNss( | |
199 | CSSM_DATA &b) | |
200 | { | |
201 | int numBits = b.Length * 8; | |
202 | ||
203 | /* start at end of bit array, scanning backwards looking | |
204 | * for the first set bit */ | |
205 | bool foundSet = false; | |
206 | for(int dex=b.Length-1; dex>=0; dex--) { | |
207 | unsigned bitMask = 0x01; | |
208 | uint8 byte = b.Data[dex]; | |
209 | for(unsigned bdex=0; bdex<8; bdex++) { | |
210 | if(byte & bitMask) { | |
211 | foundSet = true; | |
212 | break; | |
213 | } | |
214 | else { | |
215 | bitMask <<= 1; | |
216 | numBits--; | |
217 | } | |
218 | } | |
219 | if(foundSet) { | |
220 | break; | |
221 | } | |
222 | } | |
223 | /* !foundSet --> numBits = 0 */ | |
224 | assert(((numBits > 0) & foundSet) || ((numBits == 0) && !foundSet)); | |
225 | b.Length = (uint32)numBits; | |
226 | } | |
227 | ||
228 | /* | |
229 | * On entry, Length is bit count; on exit, a byte count. | |
230 | * The job here is to ensure that bits marked as "unused" in the | |
231 | * BER encoding are cleared. Encoding rules say they are undefined in | |
232 | * the actual encoding. | |
233 | */ | |
234 | void clNssBitStringToCssm( | |
235 | CSSM_DATA &b) | |
236 | { | |
237 | uint32 byteCount = (b.Length + 7) / 8; | |
238 | unsigned partialBits = b.Length & 0x7; | |
239 | b.Length = byteCount; | |
240 | if(partialBits == 0) { | |
241 | return; | |
242 | } | |
243 | ||
244 | /* mask off unused bits */ | |
245 | unsigned unusedBits = 8 - partialBits; | |
246 | uint8 *bp = b.Data + b.Length - 1; | |
247 | /* mask = (2 ** unusedBits) - 1 */ | |
248 | unsigned mask = (1 << unusedBits) - 1; | |
249 | *bp &= ~mask; | |
250 | } | |
251 | ||
252 | #pragma mark ----- NSS array manipulation ----- | |
253 | /* | |
254 | * How many items in a NULL-terminated array of pointers? | |
255 | */ | |
256 | unsigned clNssArraySize( | |
257 | const void **array) | |
258 | { | |
259 | unsigned count = 0; | |
260 | if (array) { | |
261 | while (*array++) { | |
262 | count++; | |
263 | } | |
264 | } | |
265 | return count; | |
266 | } | |
267 | ||
268 | /* malloc a NULL-ed array of pointers of size num+1 */ | |
269 | void **clNssNullArray( | |
270 | uint32 num, | |
271 | SecNssCoder &coder) | |
272 | { | |
273 | unsigned len = (num + 1) * sizeof(void *); | |
274 | void **p = (void **)coder.malloc(len); | |
275 | memset(p, 0, len); | |
276 | return p; | |
277 | } | |
278 | ||
279 | /* | |
280 | * GIven a CSSM_DATA containing a decoded BIT_STRING, | |
281 | * convert to a KeyUsage. | |
282 | */ | |
283 | CE_KeyUsage clBitStringToKeyUsage( | |
284 | const CSSM_DATA &cdata) | |
285 | { | |
286 | unsigned toCopy = (cdata.Length + 7) / 8; | |
287 | if(toCopy > 2) { | |
288 | /* I hope I never see this... */ | |
289 | clErrorLog("clBitStringToKeyUsage: KeyUsage larger than 2 bytes!"); | |
290 | toCopy = 2; | |
291 | } | |
292 | unsigned char bits[2] = {0, 0}; | |
293 | memmove(bits, cdata.Data, toCopy); | |
294 | CE_KeyUsage usage = (((unsigned)bits[0]) << 8) | bits[1]; | |
295 | return usage; | |
296 | } | |
297 | ||
298 | CSSM_ALGORITHMS CL_oidToAlg( | |
299 | const CSSM_OID &oid) | |
300 | { | |
301 | CSSM_ALGORITHMS alg; | |
302 | bool found = cssmOidToAlg(&oid, &alg); | |
303 | if(!found) { | |
304 | clErrorLog("CL_oidToAlg: unknown alg\n"); | |
305 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
306 | } | |
307 | return alg; | |
308 | } | |
309 | ||
310 | #pragma mark ----- copy CSSM_X509_ALGORITHM_IDENTIFIER ----- | |
311 | ||
312 | /* | |
313 | * Copy CSSM_X509_ALGORITHM_IDENTIFIER, same format (NSS and CSSM). | |
314 | */ | |
315 | void CL_copyAlgId( | |
316 | const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId, | |
317 | CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId, | |
318 | CssmAllocator &alloc) | |
319 | { | |
320 | clAllocCopyData(alloc, srcAlgId.algorithm, dstAlgId.algorithm); | |
321 | clAllocCopyData(alloc, srcAlgId.parameters, dstAlgId.parameters); | |
322 | } | |
323 | ||
324 | void CL_freeCssmAlgId( | |
325 | CSSM_X509_ALGORITHM_IDENTIFIER *cdsaObj, // optional | |
326 | CssmAllocator &alloc) | |
327 | { | |
328 | if(cdsaObj == NULL) { | |
329 | return; | |
330 | } | |
331 | alloc.free(cdsaObj->algorithm.Data); | |
332 | alloc.free(cdsaObj->parameters.Data); | |
333 | memset(cdsaObj, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); | |
334 | } | |
335 | ||
336 | ||
337 | #pragma mark ----- CSSM_X509_TIME <--> NSS format ----- | |
338 | ||
339 | /* | |
340 | * Map the tag associated with a choice of DirectoryString elements to | |
341 | * a template array for encoding/decoding that string type. | |
342 | * Contrary to RFC2459, we allow the IA5String type, which is actually | |
343 | * used in the real world (cf. the email address in Thawte's serverbasic | |
344 | * cert). | |
345 | */ | |
346 | ||
347 | /* The template chooser does the work here */ | |
348 | ||
349 | bool CL_nssTimeToCssm( | |
350 | const NSS_TaggedItem &nssTime, | |
351 | CSSM_X509_TIME &cssmObj, | |
352 | CssmAllocator &alloc) | |
353 | { | |
354 | cssmObj.timeType = nssTime.tag; | |
355 | clAllocCopyData(alloc, nssTime.item, cssmObj.time); | |
356 | return true; | |
357 | } | |
358 | ||
359 | /* | |
360 | * CSSM time to NSS time. | |
361 | */ | |
362 | void CL_cssmTimeToNss( | |
363 | const CSSM_X509_TIME &cssmTime, | |
364 | NSS_TaggedItem &nssTime, | |
365 | SecNssCoder &coder) | |
366 | { | |
367 | nssTime.tag = cssmTime.timeType; | |
368 | coder.allocCopyItem(cssmTime.time, nssTime.item); | |
369 | } | |
370 | ||
371 | void CL_freeCssmTime( | |
372 | CSSM_X509_TIME *cssmTime, | |
373 | CssmAllocator &alloc) | |
374 | { | |
375 | if(cssmTime == NULL) { | |
376 | return; | |
377 | } | |
378 | if(cssmTime->time.Data) { | |
379 | alloc.free(cssmTime->time.Data); | |
380 | } | |
381 | memset(cssmTime, 0, sizeof(CSSM_X509_TIME)); | |
382 | } | |
383 | ||
384 | ||
385 | #pragma mark ----- CSSM_X509_SUBJECT_PUBLIC_KEY_INFO <--> CSSM_KEY ----- | |
386 | ||
387 | /* | |
388 | * Copy a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO. | |
389 | * | |
390 | * Same format (NSS and CSSM), EXCEPT: | |
391 | * | |
392 | * Objects which have just been NSS decoded or are about to be | |
393 | * NSS encoded have the subjectPublicKey.Length field in BITS | |
394 | * since this field is wrapped in a BIT STRING upon encoding. | |
395 | * | |
396 | * Caller tells us which format (bits or bytes) | |
397 | * to use for each of {src, dst}. | |
398 | */ | |
399 | void CL_copySubjPubKeyInfo( | |
400 | const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &srcInfo, | |
401 | bool srcInBits, | |
402 | CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &dstInfo, | |
403 | bool dstInBits, | |
404 | CssmAllocator &alloc) | |
405 | { | |
406 | CL_copyAlgId(srcInfo.algorithm, dstInfo.algorithm, alloc); | |
407 | ||
408 | CSSM_DATA srcKey = srcInfo.subjectPublicKey; | |
409 | if(srcInBits) { | |
410 | srcKey.Length = (srcKey.Length + 7) / 8; | |
411 | } | |
412 | clAllocCopyData(alloc, srcKey, dstInfo.subjectPublicKey); | |
413 | if(dstInBits) { | |
414 | dstInfo.subjectPublicKey.Length *= 8; | |
415 | } | |
416 | } | |
417 | ||
418 | /* | |
419 | * Obtain a CSSM_KEY from a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO, | |
420 | * inferring as much as we can from required fields | |
421 | * (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO) and extensions (for | |
422 | * KeyUse, obtained from the optional DecodedCert). | |
423 | */ | |
424 | CSSM_KEY_PTR CL_extractCSSMKeyNSS( | |
425 | const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &keyInfo, | |
426 | CssmAllocator &alloc, | |
427 | const DecodedCert *decodedCert) // optional | |
428 | { | |
429 | CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR) alloc.malloc(sizeof(CSSM_KEY)); | |
430 | memset(cssmKey, 0, sizeof(CSSM_KEY)); | |
431 | CSSM_KEYHEADER &hdr = cssmKey->KeyHeader; | |
432 | CssmRemoteData keyData(alloc, cssmKey->KeyData); | |
433 | try { | |
434 | hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; | |
435 | /* CspId blank */ | |
436 | hdr.BlobType = CSSM_KEYBLOB_RAW; | |
437 | hdr.AlgorithmId = CL_oidToAlg(keyInfo.algorithm.algorithm); | |
438 | hdr.KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE; | |
439 | ||
440 | /* | |
441 | * Format inferred from AlgorithmId. I have never seen these defined | |
442 | * anywhere, e.g., what's the format of an RSA public key in a cert? | |
443 | * X509 certainly doesn't say. However. the following two cases are | |
444 | * known to be correct. | |
445 | */ | |
446 | switch(hdr.AlgorithmId) { | |
447 | case CSSM_ALGID_RSA: | |
448 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | |
449 | break; | |
450 | case CSSM_ALGID_DSA: | |
451 | case CSSM_ALGID_DH: | |
452 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_X509; | |
453 | break; | |
454 | case CSSM_ALGID_FEE: | |
455 | /* CSSM_KEYBLOB_RAW_FORMAT_NONE --> DER encoded */ | |
456 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
457 | break; | |
458 | default: | |
459 | /* punt */ | |
460 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
461 | } | |
462 | hdr.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; | |
463 | ||
464 | /* KeyUsage inferred from extensions */ | |
465 | if(decodedCert) { | |
466 | hdr.KeyUsage = decodedCert->inferKeyUsage(); | |
467 | } | |
468 | else { | |
469 | hdr.KeyUsage = CSSM_KEYUSE_ANY; | |
470 | } | |
471 | ||
472 | /* start/end date unknown, leave zero */ | |
473 | hdr.WrapAlgorithmId = CSSM_ALGID_NONE; | |
474 | hdr.WrapMode = CSSM_ALGMODE_NONE; | |
475 | ||
476 | switch(hdr.AlgorithmId) { | |
477 | case CSSM_ALGID_DSA: | |
478 | case CSSM_ALGID_DH: | |
479 | { | |
480 | /* | |
481 | * Just encode the whole subject public key info blob. | |
482 | * NOTE we're assuming that the keyInfo.subjectPublicKey | |
483 | * field is in the NSS_native BITSTRING format, i.e., | |
484 | * its Length field is in bits and we don't have to adjust. | |
485 | */ | |
486 | PRErrorCode prtn = SecNssEncodeItemOdata(&keyInfo, | |
487 | NSS_SubjectPublicKeyInfoTemplate, keyData); | |
488 | if(prtn) { | |
489 | clErrorLog("extractCSSMKey: error on reencode\n"); | |
490 | CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); | |
491 | } | |
492 | break; | |
493 | } | |
494 | default: | |
495 | /* | |
496 | * RSA, FEE for now. | |
497 | * keyInfo.subjectPublicKey (in BITS) ==> KeyData | |
498 | */ | |
499 | keyData.copy(keyInfo.subjectPublicKey.Data, | |
500 | (keyInfo.subjectPublicKey.Length + 7) / 8); | |
501 | } | |
502 | keyData.release(); | |
503 | ||
504 | /* | |
505 | * LogicalKeySizeInBits - ask the CSP | |
506 | */ | |
507 | CSSM_CSP_HANDLE cspHand = getGlobalCspHand(true); | |
508 | CSSM_KEY_SIZE keySize; | |
509 | CSSM_RETURN crtn; | |
510 | crtn = CSSM_QueryKeySizeInBits(cspHand, CSSM_INVALID_HANDLE, cssmKey, | |
511 | &keySize); | |
512 | switch(crtn) { | |
513 | default: | |
514 | CssmError::throwMe(crtn); | |
515 | case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: | |
516 | /* | |
517 | * This is how the CSP indicates a "partial" public key, | |
518 | * with a valid public key value but no alg-specific | |
519 | * parameters (currently, DSA only). | |
520 | */ | |
521 | hdr.KeyAttr |= CSSM_KEYATTR_PARTIAL; | |
522 | /* and drop thru */ | |
523 | case CSSM_OK: | |
524 | cssmKey->KeyHeader.LogicalKeySizeInBits = | |
525 | keySize.LogicalKeySizeInBits; | |
526 | break; | |
527 | } | |
528 | } | |
529 | catch (...) { | |
530 | alloc.free(cssmKey); | |
531 | throw; | |
532 | } | |
533 | return cssmKey; | |
534 | } | |
535 | ||
536 | /* | |
537 | * Set up a encoded NULL for CSSM_X509_ALGORITHM_IDENTIFIER.parameters. | |
538 | */ | |
539 | void CL_nullAlgParams( | |
540 | CSSM_X509_ALGORITHM_IDENTIFIER &algId) | |
541 | { | |
542 | static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 }; | |
543 | CSSM_DATA encNullData; | |
544 | encNullData.Data = (uint8 *)encNull; | |
545 | encNullData.Length = 2; | |
546 | ||
547 | algId.parameters = encNullData; | |
548 | } | |
549 | ||
550 | /* | |
551 | * Convert a CSSM_KEY to a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO. The | |
552 | * CSSM key must be in raw format and with a specific blob format. | |
553 | * -- RSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_PKCS1 | |
554 | * -- DSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_X509 | |
555 | */ | |
556 | void CL_CSSMKeyToSubjPubKeyInfoNSS( | |
557 | const CSSM_KEY &cssmKey, | |
558 | CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &nssKeyInfo, | |
559 | SecNssCoder &coder) | |
560 | { | |
561 | const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader; | |
562 | if(hdr.BlobType != CSSM_KEYBLOB_RAW) { | |
563 | clErrorLog("CL SetField: must specify RAW key blob\n"); | |
564 | CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); | |
565 | } | |
566 | memset(&nssKeyInfo, 0, sizeof(nssKeyInfo)); | |
567 | ||
568 | /* algorithm and format dependent from here... */ | |
569 | switch(hdr.AlgorithmId) { | |
570 | case CSSM_ALGID_RSA: | |
571 | if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_PKCS1) { | |
572 | clErrorLog("CL SetField: RSA key must be in PKCS1 format\n"); | |
573 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); | |
574 | } | |
575 | /* and fall thru */ | |
576 | default: | |
577 | { | |
578 | /* Key header's algorithm --> OID */ | |
579 | const CSSM_OID *oid = cssmAlgToOid(hdr.AlgorithmId); | |
580 | if(oid == NULL) { | |
581 | clErrorLog("CL SetField: Unknown key algorithm\n"); | |
582 | CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); | |
583 | } | |
584 | CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssKeyInfo.algorithm; | |
585 | coder.allocCopyItem(*oid, algId.algorithm); | |
586 | ||
587 | /* NULL algorithm parameters, always in this case */ | |
588 | CL_nullAlgParams(algId); | |
589 | ||
590 | /* Copy key bits, destination is a BIT STRING */ | |
591 | coder.allocCopyItem(cssmKey.KeyData, nssKeyInfo.subjectPublicKey); | |
592 | nssKeyInfo.subjectPublicKey.Length *= 8; | |
593 | break; | |
594 | } | |
595 | case CSSM_ALGID_DSA: | |
596 | if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_X509) { | |
597 | clErrorLog("CL SetField: DSA key must be in X509 format\n"); | |
598 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); | |
599 | } | |
600 | ||
601 | /* | |
602 | * All we do is decode the whole key blob into the | |
603 | * SubjectPublicKeyInfo. | |
604 | */ | |
605 | if(coder.decodeItem(cssmKey.KeyData, | |
606 | NSS_SubjectPublicKeyInfoTemplate, | |
607 | &nssKeyInfo)) { | |
608 | clErrorLog("CL SetField: Error decoding DSA public key\n"); | |
609 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); | |
610 | } | |
611 | break; | |
612 | } | |
613 | } | |
614 | ||
615 | void CL_freeCSSMKey( | |
616 | CSSM_KEY_PTR cssmKey, | |
617 | CssmAllocator &alloc, | |
618 | bool freeTop) | |
619 | { | |
620 | if(cssmKey == NULL) { | |
621 | return; | |
622 | } | |
623 | alloc.free(cssmKey->KeyData.Data); | |
624 | memset(cssmKey, 0, sizeof(CSSM_KEY)); | |
625 | if(freeTop) { | |
626 | alloc.free(cssmKey); | |
627 | } | |
628 | } | |
629 | ||
630 | #pragma mark ----- CE_AuthorityKeyID <--> NSS_AuthorityKeyId ----- | |
631 | ||
632 | void CL_cssmAuthorityKeyIdToNss( | |
633 | const CE_AuthorityKeyID &cdsaObj, | |
634 | NSS_AuthorityKeyId &nssObj, | |
635 | SecNssCoder &coder) | |
636 | { | |
637 | memset(&nssObj, 0, sizeof(nssObj)); | |
638 | if(cdsaObj.keyIdentifierPresent) { | |
639 | nssObj.keyIdentifier = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA)); | |
640 | coder.allocCopyItem(cdsaObj.keyIdentifier, *nssObj.keyIdentifier); | |
641 | } | |
642 | if(cdsaObj.generalNamesPresent ) { | |
643 | /* GeneralNames, the hard one */ | |
644 | CL_cssmGeneralNamesToNss(*cdsaObj.generalNames, | |
645 | nssObj.genNames, coder); | |
646 | } | |
647 | if(cdsaObj.serialNumberPresent) { | |
648 | coder.allocCopyItem(cdsaObj.serialNumber,nssObj.serialNumber); | |
649 | } | |
650 | } | |
651 | ||
652 | void CL_nssAuthorityKeyIdToCssm( | |
653 | const NSS_AuthorityKeyId &nssObj, | |
654 | CE_AuthorityKeyID &cdsaObj, | |
655 | SecNssCoder &coder, // for temp decoding | |
656 | CssmAllocator &alloc) | |
657 | { | |
658 | if(nssObj.keyIdentifier != NULL) { | |
659 | cdsaObj.keyIdentifierPresent = CSSM_TRUE; | |
660 | clAllocCopyData(alloc, *nssObj.keyIdentifier, cdsaObj.keyIdentifier); | |
661 | } | |
662 | if(nssObj.genNames.names != NULL) { | |
663 | /* GeneralNames, the hard one */ | |
664 | cdsaObj.generalNamesPresent = CSSM_TRUE; | |
665 | cdsaObj.generalNames = | |
666 | (CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames)); | |
667 | CL_nssGeneralNamesToCssm(nssObj.genNames, | |
668 | *cdsaObj.generalNames, | |
669 | coder, | |
670 | alloc); | |
671 | } | |
672 | if(nssObj.serialNumber.Data != NULL) { | |
673 | cdsaObj.serialNumberPresent = CSSM_TRUE; | |
674 | clAllocCopyData(alloc, nssObj.serialNumber, cdsaObj.serialNumber); | |
675 | } | |
676 | } | |
677 | ||
678 | #pragma mark ----- decode/encode CE_DistributionPointName ----- | |
679 | ||
680 | /* This is always a DER-encoded blob at the NSS level */ | |
681 | void CL_decodeDistributionPointName( | |
682 | const CSSM_DATA &nssBlob, | |
683 | CE_DistributionPointName &cssmDpn, | |
684 | SecNssCoder &coder, | |
685 | CssmAllocator &alloc) | |
686 | { | |
687 | memset(&cssmDpn, 0, sizeof(CE_DistributionPointName)); | |
688 | if(nssBlob.Length == 0) { | |
689 | clErrorLog("***CL_decodeDistributionPointName: bad PointName\n"); | |
690 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
691 | } | |
692 | unsigned char tag = nssBlob.Data[0] & SEC_ASN1_TAGNUM_MASK; | |
693 | switch(tag) { | |
694 | case NSS_DIST_POINT_FULL_NAME_TAG: | |
695 | { | |
696 | /* decode to temp coder memory */ | |
697 | NSS_GeneralNames gnames; | |
698 | gnames.names = NULL; | |
699 | if(coder.decodeItem(nssBlob, NSS_DistPointFullNameTemplate, | |
700 | &gnames)) { | |
701 | clErrorLog("***Error decoding DistPointFullName\n"); | |
702 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
703 | } | |
704 | ||
705 | cssmDpn.nameType = CE_CDNT_FullName; | |
706 | cssmDpn.fullName = (CE_GeneralNames *)alloc.malloc( | |
707 | sizeof(CE_GeneralNames)); | |
708 | ||
709 | /* copy out to caller */ | |
710 | CL_nssGeneralNamesToCssm(gnames, | |
711 | *cssmDpn.fullName, coder, alloc); | |
712 | break; | |
713 | } | |
714 | case NSS_DIST_POINT_RDN_TAG: | |
715 | { | |
716 | /* decode to temp coder memory */ | |
717 | NSS_RDN rdn; | |
718 | memset(&rdn, 0, sizeof(rdn)); | |
719 | if(coder.decodeItem(nssBlob, NSS_DistPointRDNTemplate, | |
720 | &rdn)) { | |
721 | clErrorLog("***Error decoding DistPointRDN\n"); | |
722 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
723 | } | |
724 | ||
725 | cssmDpn.nameType = CE_CDNT_NameRelativeToCrlIssuer; | |
726 | cssmDpn.rdn = (CSSM_X509_RDN_PTR)alloc.malloc( | |
727 | sizeof(CSSM_X509_RDN)); | |
728 | ||
729 | /* copy out to caller */ | |
730 | CL_nssRdnToCssm(rdn, *cssmDpn.rdn, alloc, coder); | |
731 | break; | |
732 | } | |
733 | default: | |
734 | clErrorLog("***Bad CE_DistributionPointName tag\n"); | |
735 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
736 | } | |
737 | } | |
738 | ||
739 | void CL_encodeDistributionPointName( | |
740 | CE_DistributionPointName &cpoint, | |
741 | CSSM_DATA &npoint, | |
742 | SecNssCoder &coder) | |
743 | { | |
744 | const SEC_ASN1Template *templ = NULL; | |
745 | NSS_GeneralNames gnames; | |
746 | NSS_RDN rdn; | |
747 | void *encodeSrc = NULL; | |
748 | ||
749 | /* | |
750 | * Our job is to convert one of two incoming aggregate types | |
751 | * into NSS format, then encode the result into npoint. | |
752 | */ | |
753 | switch(cpoint.nameType) { | |
754 | case CE_CDNT_FullName: | |
755 | CL_cssmGeneralNamesToNss(*cpoint.fullName, | |
756 | gnames, coder); | |
757 | encodeSrc = &gnames; | |
758 | templ = NSS_DistPointFullNameTemplate; | |
759 | break; | |
760 | ||
761 | case CE_CDNT_NameRelativeToCrlIssuer: | |
762 | CL_cssmRdnToNss(*cpoint.rdn, rdn, coder); | |
763 | encodeSrc = &rdn; | |
764 | templ = NSS_DistPointRDNTemplate; | |
765 | break; | |
766 | default: | |
767 | clErrorLog("CL_encodeDistributionPointName: bad nameType\n"); | |
768 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); | |
769 | } | |
770 | if(coder.encodeItem(encodeSrc, templ, npoint)) { | |
771 | clErrorLog("CL_encodeDistributionPointName: encode error\n"); | |
772 | CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); | |
773 | } | |
774 | } | |
775 | ||
776 | ||
777 | #pragma mark --- CE_CRLDistPointsSyntax <--> NSS_CRLDistributionPoints --- | |
778 | ||
779 | void CL_cssmDistPointsToNss( | |
780 | const CE_CRLDistPointsSyntax &cdsaObj, | |
781 | NSS_CRLDistributionPoints &nssObj, | |
782 | SecNssCoder &coder) | |
783 | { | |
784 | memset(&nssObj, 0, sizeof(nssObj)); | |
785 | unsigned numPoints = cdsaObj.numDistPoints; | |
786 | if(numPoints == 0) { | |
787 | return; | |
788 | } | |
789 | nssObj.distPoints = | |
790 | (NSS_DistributionPoint **)clNssNullArray(numPoints, coder); | |
791 | for(unsigned dex=0; dex<numPoints; dex++) { | |
792 | nssObj.distPoints[dex] = (NSS_DistributionPoint *) | |
793 | coder.malloc(sizeof(NSS_DistributionPoint)); | |
794 | NSS_DistributionPoint *npoint = nssObj.distPoints[dex]; | |
795 | memset(npoint, 0, sizeof(NSS_DistributionPoint)); | |
796 | CE_CRLDistributionPoint *cpoint = &cdsaObj.distPoints[dex]; | |
797 | ||
798 | /* all fields are optional */ | |
799 | if(cpoint->distPointName) { | |
800 | /* encode and drop into ASN_ANY slot */ | |
801 | npoint->distPointName = (CSSM_DATA *) | |
802 | coder.malloc(sizeof(CSSM_DATA)); | |
803 | CL_encodeDistributionPointName(*cpoint->distPointName, | |
804 | *npoint->distPointName, coder); | |
805 | ||
806 | } | |
807 | ||
808 | if(cpoint->reasonsPresent) { | |
809 | /* bit string, presumed max length 8 bits */ | |
810 | coder.allocItem(npoint->reasons, 1); | |
811 | npoint->reasons.Data[0] = cpoint->reasons; | |
812 | /* adjust for bit string length */ | |
813 | npoint->reasons.Length = 8; | |
814 | } | |
815 | ||
816 | if(cpoint->crlIssuer) { | |
817 | CL_cssmGeneralNamesToNss(*cpoint->crlIssuer, | |
818 | npoint->crlIssuer, coder); | |
819 | } | |
820 | } | |
821 | } | |
822 | ||
823 | void CL_nssDistPointsToCssm( | |
824 | const NSS_CRLDistributionPoints &nssObj, | |
825 | CE_CRLDistPointsSyntax &cdsaObj, | |
826 | SecNssCoder &coder, // for temp decoding | |
827 | CssmAllocator &alloc) | |
828 | { | |
829 | memset(&cdsaObj, 0, sizeof(cdsaObj)); | |
830 | unsigned numPoints = clNssArraySize((const void **)nssObj.distPoints); | |
831 | if(numPoints == 0) { | |
832 | return; | |
833 | } | |
834 | ||
835 | unsigned len = sizeof(CE_CRLDistributionPoint) * numPoints; | |
836 | cdsaObj.distPoints = (CE_CRLDistributionPoint *)alloc.malloc(len); | |
837 | memset(cdsaObj.distPoints, 0, len); | |
838 | cdsaObj.numDistPoints = numPoints; | |
839 | ||
840 | for(unsigned dex=0; dex<numPoints; dex++) { | |
841 | CE_CRLDistributionPoint &cpoint = cdsaObj.distPoints[dex]; | |
842 | NSS_DistributionPoint &npoint = *(nssObj.distPoints[dex]); | |
843 | ||
844 | /* All three fields are optional */ | |
845 | if(npoint.distPointName != NULL) { | |
846 | /* Drop in a CE_DistributionPointName */ | |
847 | CE_DistributionPointName *cname = | |
848 | (CE_DistributionPointName *)alloc.malloc( | |
849 | sizeof(CE_DistributionPointName)); | |
850 | memset(cname, 0, sizeof(*cname)); | |
851 | cpoint.distPointName = cname; | |
852 | ||
853 | /* | |
854 | * This one is currently still encoded; we have to peek | |
855 | * at its tag and decode accordingly. | |
856 | */ | |
857 | CL_decodeDistributionPointName(*npoint.distPointName, | |
858 | *cname, coder, alloc); | |
859 | } | |
860 | ||
861 | if(npoint.reasons.Data != NULL) { | |
862 | /* careful, it's a bit string */ | |
863 | if(npoint.reasons.Length > 8) { | |
864 | clErrorLog("***CL_nssDistPointsToCssm: Malformed reasons\n"); | |
865 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
866 | } | |
867 | cpoint.reasonsPresent = CSSM_TRUE; | |
868 | if(npoint.reasons.Length != 0) { | |
869 | cpoint.reasons = npoint.reasons.Data[0]; | |
870 | } | |
871 | } | |
872 | ||
873 | if(npoint.crlIssuer.names != NULL) { | |
874 | /* Cook up a new CE_GeneralNames */ | |
875 | cpoint.crlIssuer = | |
876 | (CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames)); | |
877 | CL_nssGeneralNamesToCssm(npoint.crlIssuer, *cpoint.crlIssuer, | |
878 | coder, alloc); | |
879 | } | |
880 | } | |
881 | } | |
882 | ||
883 | #pragma mark ----- IssuingDistributionPoint ----- | |
884 | ||
885 | void CL_nssIssuingDistPointToCssm( | |
886 | NSS_IssuingDistributionPoint *nssIdp, | |
887 | CE_IssuingDistributionPoint *cssmIdp, | |
888 | SecNssCoder &coder, | |
889 | CssmAllocator &alloc) | |
890 | { | |
891 | /* All fields optional */ | |
892 | memset(cssmIdp, 0, sizeof(*cssmIdp)); | |
893 | if(nssIdp->distPointName) { | |
894 | CE_DistributionPointName *cssmDp = (CE_DistributionPointName *) | |
895 | alloc.malloc(sizeof(CE_DistributionPointName)); | |
896 | ||
897 | /* | |
898 | * This one is currently still encoded; we have to peek | |
899 | * at its tag and decode accordingly. | |
900 | */ | |
901 | CL_decodeDistributionPointName(*nssIdp->distPointName, | |
902 | *cssmDp, coder, alloc); | |
903 | cssmIdp->distPointName = cssmDp; | |
904 | } | |
905 | if(nssIdp->onlyUserCerts) { | |
906 | cssmIdp->onlyUserCertsPresent = CSSM_TRUE; | |
907 | cssmIdp->onlyUserCerts = clNssBoolToCssm(*nssIdp->onlyUserCerts); | |
908 | } | |
909 | if(nssIdp->onlyCACerts) { | |
910 | cssmIdp->onlyCACertsPresent = CSSM_TRUE; | |
911 | cssmIdp->onlyCACerts = clNssBoolToCssm(*nssIdp->onlyCACerts); | |
912 | } | |
913 | if(nssIdp->onlySomeReasons) { | |
914 | cssmIdp->onlySomeReasonsPresent = CSSM_TRUE; | |
915 | if(nssIdp->onlySomeReasons->Length > 0) { | |
916 | cssmIdp->onlySomeReasons = *nssIdp->onlySomeReasons->Data; | |
917 | } | |
918 | else { | |
919 | cssmIdp->onlySomeReasons = 0; | |
920 | } | |
921 | } | |
922 | if(nssIdp->indirectCRL) { | |
923 | cssmIdp->indirectCrlPresent = CSSM_TRUE; | |
924 | cssmIdp->indirectCrl = clNssBoolToCssm(*nssIdp->indirectCRL); | |
925 | } | |
926 | } | |
927 | ||
928 | #pragma mark ----- Top-level Cert/CRL encode and decode ----- | |
929 | ||
930 | /* | |
931 | * To ensure a secure means of signing and verifying TBSCert blobs, we | |
932 | * provide these functions to encode and decode just the top-level | |
933 | * elements of a certificate. Unfortunately there is no guarantee | |
934 | * that when you decode and re-encode a TBSCert blob, you get the | |
935 | * same thing you started with (although with DER rules, as opposed | |
936 | * to BER rules, you should). Thus when signing, we sign the TBSCert | |
937 | * and encode the signed cert here without ever decoding the TBSCert (or, | |
938 | * at least, without using the decoded version to get the encoded TBS blob). | |
939 | */ | |
940 | ||
941 | void CL_certCrlDecodeComponents( | |
942 | const CssmData &signedItem, // DER-encoded cert or CRL | |
943 | CssmOwnedData &tbsBlob, // still DER-encoded | |
944 | CssmOwnedData &algId, // ditto | |
945 | CssmOwnedData &rawSig) // raw bits (not an encoded AsnBits) | |
946 | { | |
947 | /* BER-decode into temp memory */ | |
948 | NSS_SignedCertOrCRL nssObj; | |
949 | SecNssCoder coder; | |
950 | PRErrorCode prtn; | |
951 | ||
952 | memset(&nssObj, 0, sizeof(nssObj)); | |
953 | prtn = coder.decode(signedItem.data(), signedItem.length(), | |
954 | NSS_SignedCertOrCRLTemplate, &nssObj); | |
955 | if(prtn) { | |
956 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
957 | } | |
958 | ||
959 | /* tbsBlob and algId are raw ASN_ANY including tags, which we pass | |
960 | * back to caller intact */ | |
961 | tbsBlob.copy(nssObj.tbsBlob.Data, nssObj.tbsBlob.Length); | |
962 | algId.copy(nssObj.signatureAlgorithm.Data, | |
963 | nssObj.signatureAlgorithm.Length); | |
964 | ||
965 | /* signature is a bit string which we do in fact decode */ | |
966 | rawSig.copy(nssObj.signature.Data, | |
967 | (nssObj.signature.Length + 7) / 8); | |
968 | } | |
969 | ||
970 | ||
971 | /* | |
972 | * Given pre-DER-encoded blobs, do the final encode step for a signed cert. | |
973 | */ | |
974 | void | |
975 | CL_certEncodeComponents( | |
976 | const CssmData &TBSCert, // DER-encoded | |
977 | const CssmData &algId, // ditto | |
978 | const CssmData &rawSig, // raw bits, not encoded | |
979 | CssmOwnedData &signedCert) // DER-encoded | |
980 | { | |
981 | NSS_SignedCertOrCRL nssObj; | |
982 | nssObj.tbsBlob.Data = TBSCert.Data; | |
983 | nssObj.tbsBlob.Length = TBSCert.Length; | |
984 | nssObj.signatureAlgorithm.Data = algId.Data; | |
985 | nssObj.signatureAlgorithm.Length = algId.Length; | |
986 | nssObj.signature.Data = rawSig.Data; | |
987 | nssObj.signature.Length = rawSig.Length * 8; // BIT STRING | |
988 | ||
989 | PRErrorCode prtn; | |
990 | ||
991 | prtn = SecNssEncodeItemOdata(&nssObj, | |
992 | NSS_SignedCertOrCRLTemplate,signedCert); | |
993 | if(prtn) { | |
994 | CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); | |
995 | } | |
996 | ||
997 | } |