]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 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 | /* | |
20 | * CertSNACC.cpp - snacc-related cert functions | |
21 | * | |
22 | * Created 9/1/2000 by Doug Mitchell. | |
23 | * Copyright (c) 2000 by Apple Computer. | |
24 | */ | |
25 | ||
26 | #include "SnaccUtils.h" | |
29654253 | 27 | #include "CSPAttacher.h" |
bac41a7b A |
28 | #include "cldebugging.h" |
29 | #include <Security/pkcs1oids.h> | |
30 | #include <Security/cdsaUtils.h> | |
31 | #include <Security/cssmapple.h> | |
32 | #include <Security/appleoids.h> | |
33 | #include <Security/globalizer.h> | |
34 | ||
35 | #define DEBUG_DECODE 0 | |
36 | #if DEBUG_DECODE | |
37 | #define ddprintf(x) printf x | |
38 | #else | |
39 | #define ddprintf(x) | |
40 | #endif | |
41 | ||
42 | /* | |
43 | * AsnOid "constants" which we construct and cache on demand to avoid the | |
44 | * somewhat expensive op of constructing them every time we test for equality | |
45 | * in CL_snaccOidToCssmAlg. | |
46 | */ | |
47 | class AlgOidCache | |
48 | { | |
49 | public: | |
50 | AlgOidCache() : | |
51 | mRsaEncryption(rsaEncryption_arc), | |
52 | mMd2WithRSAEncryption(md2WithRSAEncryption_arc), | |
53 | mMd5WithRSAEncryption(md5WithRSAEncryption_arc), | |
54 | mSha1withRSAEncryption(sha1withRSAEncryption_arc), | |
55 | mId_dsa(id_dsa_arc), | |
56 | mId_dsa_with_sha1(id_dsa_with_sha1_arc), | |
57 | mAppleFee(appleFee_arc), | |
58 | mAppleAsc(appleAsc_arc), | |
59 | mAppleFeeMD5(appleFeeMD5_arc), | |
60 | mAppleFeeSHA1(appleFeeSHA1_arc), | |
61 | mAppleFeed(appleFeed_arc), | |
62 | mAppleFeedExp(appleFeedExp_arc), | |
63 | mAppleECDSA(appleECDSA_arc) | |
64 | { } | |
65 | ||
66 | AsnOid mRsaEncryption; | |
67 | AsnOid mMd2WithRSAEncryption; | |
68 | AsnOid mMd5WithRSAEncryption; | |
69 | AsnOid mSha1withRSAEncryption; | |
70 | AsnOid mId_dsa; | |
71 | AsnOid mId_dsa_with_sha1; | |
72 | AsnOid mAppleFee; | |
73 | AsnOid mAppleAsc; | |
74 | AsnOid mAppleFeeMD5; | |
75 | AsnOid mAppleFeeSHA1; | |
76 | AsnOid mAppleFeed; | |
77 | AsnOid mAppleFeedExp; | |
78 | AsnOid mAppleECDSA; | |
79 | }; | |
80 | ||
81 | static ModuleNexus<AlgOidCache> algOidCache; | |
82 | ||
83 | /* | |
84 | * To ensure a secure means of signing and verifying TBSCert blobs, we | |
85 | * provide these functions to encode and decode just the top-level | |
86 | * elements of a certificate. Snacc doesn't allow you to specify, for | |
87 | * example, a fully encoded TBSCert prior to encoding the whole cert after | |
88 | * signing it - you have to decode the TBSCert, put it and the other | |
89 | * components into a Cert, and then encode the whole thing. Unfortunately | |
90 | * there is no guarantee that when you decode and re-encode a TBSCert blob, | |
91 | * you get the same thing you started with (although with DER rules, as | |
92 | * opposed to BER rules, you should). Thus when signing, we sign the TBSCert | |
93 | * and encode the signed cert here without ever decoding the TBSCert (or, | |
94 | * at least, without using the decoded version to get the encoded TBS blob). | |
95 | */ | |
96 | ||
97 | void | |
98 | CL_certDecodeComponents( | |
99 | const CssmData &signedCert, // DER-encoded | |
100 | CssmOwnedData &TBSCert, // still DER-encoded | |
101 | CssmOwnedData &algId, // ditto | |
102 | CssmOwnedData &rawSig) // raw bits (not an encoded AsnBits) | |
103 | { | |
104 | CssmAutoData encodedSig(rawSig.allocator); | |
105 | ||
106 | /* drop signedCert into an AsnBuf for processing */ | |
107 | AsnBuf buf; | |
108 | buf.InstallData(reinterpret_cast<char *>(signedCert.data()), signedCert.length()); | |
109 | ||
110 | /* based on snacc-generated Certificate::BDec() and BDecContent() */ | |
111 | AsnTag tag; | |
112 | AsnLen bytesDecoded = 0; | |
113 | AsnLen decLen; // from BDecLen | |
114 | AsnLen totalLen; // including tag and ASN length | |
115 | char *elemStart; // ptr to start of element, including tag | |
116 | ||
bac41a7b | 117 | ENV_TYPE env; |
29654253 | 118 | try { |
bac41a7b A |
119 | tag = BDecTag (buf, bytesDecoded, env); |
120 | if (tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) { | |
121 | errorLog1("CL_CertDecodeComponents: bad first-level tag (0x%x)\n", tag); | |
122 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
123 | } | |
124 | decLen = BDecLen (buf, bytesDecoded, env); // of total | |
125 | /* FIXME - we should be able to ensure right here that we have enough */ | |
126 | ||
127 | /* First element, TBSCert */ | |
128 | /* Note we need to include the tag and content in the outgoing blobs */ | |
129 | elemStart = buf.DataPtr() + bytesDecoded; | |
130 | tag = BDecTag (buf, bytesDecoded, env); | |
131 | if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) { | |
132 | errorLog1("CL_CertDecodeComponents: bad TBSCert tag (0x%x)\n", tag); | |
133 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
134 | } | |
135 | ||
136 | decLen = BDecLen (buf, bytesDecoded, env); // DER 'length' | |
137 | /* buf now at first content byte; simulate grabbing content */ | |
138 | totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr())); | |
139 | buf.Skip(decLen); | |
140 | bytesDecoded += decLen; | |
141 | TBSCert.copy(elemStart, totalLen); | |
142 | ddprintf(("CL_certDecodeComponents: TBS len %d; data %02x %02x %02x %02x...\n", | |
143 | totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1], | |
144 | ((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3])); | |
145 | ||
146 | /* next element, algId */ | |
147 | elemStart = buf.DataPtr() + bytesDecoded; | |
148 | tag = BDecTag (buf, bytesDecoded, env); | |
149 | if(tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)) { | |
150 | errorLog1("CL_CertDecodeComponents: bad AlgId tag (0x%x)\n", tag); | |
151 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
152 | } | |
153 | decLen = BDecLen (buf, bytesDecoded, env); | |
154 | totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr())); | |
155 | buf.Skip(decLen); | |
156 | bytesDecoded += decLen; | |
157 | algId.copy(elemStart, totalLen); | |
158 | ddprintf(("CL_certDecodeComponents: algId len %d; data %02x %02x %02x...\n", | |
159 | totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1], | |
160 | ((uint8 *)elemStart)[2])); | |
161 | ||
162 | /* next element, signature */ | |
163 | elemStart = buf.DataPtr() + bytesDecoded; | |
164 | tag = BDecTag (buf, bytesDecoded, env); | |
165 | if((tag != MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE)) && | |
166 | (tag != MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE))) { | |
167 | errorLog1("CL_CertDecodeComponents: bad sig tag 0x%x\n", tag); | |
168 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
169 | } | |
170 | decLen = BDecLen (buf, bytesDecoded, env); | |
171 | totalLen = decLen + (bytesDecoded - (elemStart - buf.DataPtr())); | |
172 | encodedSig.copy(elemStart, totalLen); | |
173 | ddprintf(("CL_certDecodeComponents: encodedSig len %d; data %02x %02x " | |
174 | "%02x %02x...\n", | |
175 | totalLen, ((uint8 *)elemStart)[0], ((uint8 *)elemStart)[1], | |
176 | ((uint8 *)elemStart)[2], ((uint8 *)elemStart)[3])); | |
177 | ||
178 | /* | |
179 | * encodedSig is a DER-encoded AsnBits. Decode for caller. | |
180 | */ | |
181 | SC_decodeAsnBitsToCssmData(encodedSig.get(), rawSig); | |
182 | ddprintf(("CL_certDecodeComponents: rawSig len %d\n", rawSig.length())); | |
183 | /* | |
184 | * OK, if we get here, we can skip the remaining stuff from | |
185 | * Certificate::BDecContent(), which involves getting to the end | |
186 | * of indefinte-length data. | |
187 | */ | |
188 | } | |
29654253 A |
189 | catch(...) { |
190 | errorLog0("CL_CertDecodeComponents: throw during decode\n"); | |
bac41a7b A |
191 | TBSCert.reset(); |
192 | algId.reset(); | |
193 | rawSig.reset(); | |
194 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
195 | } | |
196 | } | |
197 | ||
198 | /* | |
199 | * Given pre-DER-encoded blobs, do the final encode step for a signed cert. | |
200 | */ | |
201 | void | |
202 | CL_certEncodeComponents( | |
203 | const CssmData &TBSCert, // DER-encoded | |
204 | const CssmData &algId, // ditto | |
205 | const CssmData &rawSig, // raw bits, not encoded | |
206 | CssmOwnedData &signedCert) // DER-encoded | |
207 | { | |
208 | /* first BER-encode the signature */ | |
209 | AsnBits snaccSig(reinterpret_cast<char *>(rawSig.data()), | |
210 | rawSig.length() * 8); | |
211 | CssmAutoData encodedSig(signedCert.allocator); | |
212 | SC_encodeAsnObj(snaccSig, encodedSig, rawSig.length() + 10); | |
213 | ||
214 | /* | |
215 | * OK, we have all three cert components already DER-encoded. The encoded | |
216 | * cert is just (tag | contentLength | TBSCert | algId | encodedSig). | |
217 | * To avoid an unneccessary copy at the end of the encode, figure out | |
218 | * the length of tag and contentLength. The tag is known to be one byte. | |
219 | */ | |
220 | size_t contentLen = TBSCert.length() + algId.length() + encodedSig.length(); | |
221 | size_t lenLen = SC_lengthOfLength(contentLen); | |
222 | size_t totalLen = 1 /* tag */ + lenLen /* length bytes */ + contentLen; | |
223 | signedCert.malloc(totalLen); | |
224 | ||
225 | /* tag */ | |
226 | char *cp = (char *)signedCert.data(); | |
227 | *cp++ = UNIV | CONS | SEQ_TAG_CODE; | |
228 | ||
229 | /* length */ | |
230 | SC_encodeLength(contentLen, cp, lenLen); | |
231 | cp += lenLen; | |
232 | ||
233 | /* concatenate the existing components */ | |
234 | memcpy(cp, TBSCert.data(), TBSCert.length()); | |
235 | cp += TBSCert.length(); | |
236 | memcpy(cp, algId.data(), algId.length()); | |
237 | cp += algId.length(); | |
238 | memcpy(cp, encodedSig.data(), encodedSig.length()); | |
239 | CASSERT((cp + encodedSig.length()) == | |
240 | ((char *)signedCert.data() + signedCert.length())); | |
241 | } | |
242 | ||
243 | /* malloc/copy a CsmmOid from a snacc-style AsnOid. */ | |
244 | void CL_snaccOidToCssm( | |
245 | const AsnOid &inOid, | |
246 | CssmOid &outOid, | |
247 | CssmAllocator &alloc) | |
248 | { | |
249 | outOid.Data = (uint8 *)alloc.malloc(inOid.Len()); | |
250 | outOid.Length = inOid.Len(); | |
251 | const char *cp = inOid; | |
252 | memcpy(outOid.Data, cp, outOid.Length); | |
253 | } | |
254 | ||
255 | /* convert algorithm identifier from CSSM format to snacc format */ | |
256 | void CL_cssmAlgIdToSnacc ( | |
257 | const CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId, | |
258 | AlgorithmIdentifier &snaccAlgId) | |
259 | { | |
260 | snaccAlgId.algorithm.Set(reinterpret_cast<char *>( | |
261 | cssmAlgId.algorithm.Data), cssmAlgId.algorithm.Length); | |
262 | if(cssmAlgId.parameters.Data != NULL) { | |
263 | /* optional parameters, raw bytes */ | |
264 | /* FIXME - is that right? SHould we encode as a bit string? | |
265 | * I've never seen this "ANY" type field used... */ | |
266 | snaccAlgId.parameters = new AsnAny; | |
267 | CSM_Buffer *cbuf = new CSM_Buffer( | |
268 | reinterpret_cast<char *>(cssmAlgId.parameters.Data), | |
269 | cssmAlgId.parameters.Length); | |
270 | snaccAlgId.parameters->value = cbuf; | |
271 | } | |
272 | else { | |
273 | CL_nullAlgParams(snaccAlgId); | |
274 | } | |
275 | } | |
276 | ||
277 | /* convert algorithm indentifier from snacc format to CSSM format */ | |
278 | void CL_snaccAlgIdToCssm ( | |
279 | const AlgorithmIdentifier &snaccAlgId, | |
280 | CSSM_X509_ALGORITHM_IDENTIFIER &cssmAlgId, | |
281 | CssmAllocator &alloc) | |
282 | { | |
283 | memset(&cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); | |
284 | ||
285 | /* algorithm - required */ | |
286 | CssmOid &outOid = CssmOid::overlay(cssmAlgId.algorithm); | |
287 | CL_snaccOidToCssm(snaccAlgId.algorithm, outOid, alloc); | |
288 | ||
289 | /* parameters as AsnAny - optional - for now just pass back the raw bytes */ | |
290 | if(snaccAlgId.parameters != NULL) { | |
291 | CSM_Buffer *cbuf = snaccAlgId.parameters->value; | |
292 | cssmAlgId.parameters.Data = (uint8 *)alloc.malloc(cbuf->Length()); | |
293 | cssmAlgId.parameters.Length = cbuf->Length(); | |
294 | memmove(cssmAlgId.parameters.Data, cbuf->Access(), | |
295 | cssmAlgId.parameters.Length); | |
296 | } | |
297 | } | |
298 | ||
299 | /* convert between uint32-style CSSM algorithm and snacc-style AsnOid */ | |
300 | CSSM_ALGORITHMS CL_snaccOidToCssmAlg( | |
301 | const AsnOid &oid) | |
302 | { | |
303 | AlgOidCache &oc = algOidCache(); | |
304 | ||
305 | CSSM_ALGORITHMS cssmAlg = 0; | |
306 | if(oid == oc.mRsaEncryption) { | |
307 | cssmAlg = CSSM_ALGID_RSA; | |
308 | } | |
309 | else if(oid == oc.mMd2WithRSAEncryption) { | |
310 | cssmAlg = CSSM_ALGID_MD2WithRSA; | |
311 | } | |
312 | else if(oid == oc.mMd5WithRSAEncryption) { | |
313 | cssmAlg = CSSM_ALGID_MD5WithRSA; | |
314 | } | |
315 | else if(oid == oc.mSha1withRSAEncryption) { | |
316 | cssmAlg = CSSM_ALGID_SHA1WithRSA; | |
317 | } | |
318 | else if(oid == oc.mId_dsa) { | |
319 | cssmAlg = CSSM_ALGID_DSA; | |
320 | } | |
321 | else if(oid == oc.mId_dsa_with_sha1) { | |
322 | cssmAlg = CSSM_ALGID_SHA1WithDSA; | |
323 | } | |
324 | else if(oid == oc.mAppleFee) { | |
325 | cssmAlg = CSSM_ALGID_FEE; | |
326 | } | |
327 | else if(oid == oc.mAppleAsc) { | |
328 | cssmAlg = CSSM_ALGID_ASC; | |
329 | } | |
330 | else if(oid == oc.mAppleFeeMD5) { | |
331 | cssmAlg = CSSM_ALGID_FEE_MD5; | |
332 | } | |
333 | else if(oid == oc.mAppleFeeSHA1) { | |
334 | cssmAlg = CSSM_ALGID_FEE_SHA1; | |
335 | } | |
336 | else if(oid == oc.mAppleFeed) { | |
337 | cssmAlg = CSSM_ALGID_FEED; | |
338 | } | |
339 | else if(oid == oc.mAppleFeedExp) { | |
340 | cssmAlg = CSSM_ALGID_FEEDEXP; | |
341 | } | |
342 | else if(oid == oc.mAppleECDSA) { | |
343 | cssmAlg = CSSM_ALGID_SHA1WithECDSA; | |
344 | } | |
345 | /* etc. */ | |
346 | else { | |
347 | errorLog0("snaccOidToCssmAlg: unknown alg\n"); | |
348 | #ifndef NDEBUG | |
349 | printf("Bogus OID: "); oid.Print(cout); | |
350 | printf("\n"); | |
351 | #endif | |
352 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
353 | } | |
354 | return cssmAlg; | |
355 | } | |
356 | ||
357 | void CL_cssmAlgToSnaccOid( | |
358 | CSSM_ALGORITHMS cssmAlg, | |
359 | AsnOid &oid) | |
360 | { | |
361 | switch(cssmAlg) { | |
362 | case CSSM_ALGID_RSA: | |
363 | oid.ReSet(rsaEncryption_arc); | |
364 | break; | |
365 | case CSSM_ALGID_MD2WithRSA: | |
366 | oid.ReSet(md2WithRSAEncryption_arc); | |
367 | break; | |
368 | case CSSM_ALGID_MD5WithRSA: | |
29654253 | 369 | oid.ReSet(md5WithRSAEncryption_arc); |
bac41a7b A |
370 | break; |
371 | case CSSM_ALGID_SHA1WithRSA: | |
372 | oid.ReSet(sha1withRSAEncryption_arc); | |
373 | break; | |
374 | case CSSM_ALGID_DSA: | |
375 | oid.ReSet(id_dsa_arc); | |
376 | break; | |
377 | case CSSM_ALGID_SHA1WithDSA: | |
378 | oid.ReSet(id_dsa_with_sha1_arc); | |
379 | break; | |
380 | case CSSM_ALGID_FEE: | |
381 | oid.ReSet(appleFee_arc); | |
382 | break; | |
383 | case CSSM_ALGID_ASC: | |
384 | oid.ReSet(appleAsc_arc); | |
385 | break; | |
386 | case CSSM_ALGID_FEE_MD5: | |
387 | oid.ReSet(appleFeeMD5_arc); | |
388 | break; | |
389 | case CSSM_ALGID_FEE_SHA1: | |
390 | oid.ReSet(appleFeeSHA1_arc); | |
391 | break; | |
392 | case CSSM_ALGID_FEED: | |
393 | oid.ReSet(appleFeed_arc); | |
394 | break; | |
395 | case CSSM_ALGID_FEEDEXP: | |
396 | oid.ReSet(appleFeedExp_arc); | |
397 | break; | |
398 | case CSSM_ALGID_SHA1WithECDSA: | |
399 | oid.ReSet(appleECDSA_arc); | |
400 | break; | |
401 | /* etc. */ | |
402 | default: | |
403 | errorLog1("cssmAlgToSnaccOid: unknown alg (%d)\n", (int)cssmAlg); | |
404 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
405 | } | |
406 | } | |
407 | ||
408 | /* set up a encoded NULL for AlgorithmIdentifier.parameters */ | |
409 | void CL_nullAlgParams( | |
410 | AlgorithmIdentifier &snaccAlgId) | |
411 | { | |
412 | snaccAlgId.parameters = new AsnAny; | |
413 | char encodedNull[2] = {NULLTYPE_TAG_CODE, 0}; | |
414 | CSM_Buffer *cbuf = new CSM_Buffer(encodedNull, 2); | |
415 | snaccAlgId.parameters->value = cbuf; | |
416 | } | |
417 | ||
418 | /* AsnOcts --> CSSM_DATA */ | |
419 | void CL_AsnOctsToCssmData( | |
420 | const AsnOcts &octs, | |
421 | CSSM_DATA &cdata, | |
422 | CssmAllocator &alloc) | |
423 | { | |
424 | const char *cp = octs; | |
425 | CssmAutoData aData(alloc, (uint8 *)cp, octs.Len()); | |
426 | cdata = aData.release(); | |
427 | } | |
428 | ||
429 | #define MAX_NAME_SIZE (4 * 1024) | |
430 | ||
431 | /* snacc-style GeneralNames --> CE_GeneralNames */ | |
432 | /* GeneralNames from sm_x509cmn.h */ | |
433 | void CL_snaccGeneralNamesToCdsa( | |
434 | GeneralNames &snaccObj, | |
435 | CE_GeneralNames &cdsaObj, | |
436 | CssmAllocator &alloc) | |
437 | { | |
438 | cdsaObj.numNames = snaccObj.Count(); | |
439 | if(cdsaObj.numNames == 0) { | |
440 | cdsaObj.generalName = NULL; | |
441 | return; | |
442 | } | |
443 | cdsaObj.generalName = (CE_GeneralName *)alloc.malloc( | |
444 | cdsaObj.numNames * sizeof(CE_GeneralName)); | |
445 | snaccObj.SetCurrToFirst(); | |
446 | CssmAutoData aData(alloc); | |
447 | for(unsigned i=0; i<cdsaObj.numNames; i++) { | |
448 | CE_GeneralName *currCdsaName = &cdsaObj.generalName[i]; | |
449 | GeneralName *currSnaccName = snaccObj.Curr(); | |
450 | ||
451 | /* just take the simple ones for now */ | |
452 | char *src = NULL; | |
453 | unsigned len = 0; | |
454 | AsnType *toBeEncoded = NULL; | |
29654253 | 455 | bool freeSrc = false; |
bac41a7b A |
456 | switch(currSnaccName->choiceId) { |
457 | case GeneralName::otherNameCid: | |
458 | /* OTHER_NAME, AsnOid */ | |
459 | currCdsaName->nameType = GNT_OtherName; | |
460 | src = *currSnaccName->otherName; | |
461 | len = currSnaccName->otherName->Len(); | |
462 | break; | |
463 | case GeneralName::rfc822NameCid: | |
464 | /* IA5String, AsnOcts */ | |
465 | currCdsaName->nameType = GNT_RFC822Name; | |
466 | src = *currSnaccName->rfc822Name; | |
467 | len = currSnaccName->rfc822Name->Len(); | |
468 | break; | |
469 | case GeneralName::dNSNameCid: | |
470 | /* IA5String, AsnOcts */ | |
471 | currCdsaName->nameType = GNT_DNSName; | |
472 | src = *currSnaccName->dNSName; | |
473 | len = currSnaccName->dNSName->Len(); | |
474 | break; | |
475 | case GeneralName::x400AddressCid: | |
476 | /* ORAddress from sm_x411mtsas */ | |
477 | currCdsaName->nameType = GNT_X400Address; | |
478 | toBeEncoded = currSnaccName->x400Address; | |
479 | break; | |
480 | case GeneralName::directoryNameCid: | |
481 | /* Name from sm_x501if */ | |
482 | /* We actually have to to deal with this in CertFields.cpp; | |
483 | * it'll be easy to support this (with a mod to | |
484 | * CE_GeneralName). | |
485 | */ | |
486 | currCdsaName->nameType = GNT_DirectoryName; | |
487 | toBeEncoded = currSnaccName->directoryName; | |
488 | break; | |
489 | case GeneralName::ediPartyNameCid: | |
490 | /* EDIPartyName from sm_x509cmn */ | |
491 | currCdsaName->nameType = GNT_EdiPartyName; | |
492 | toBeEncoded = currSnaccName->ediPartyName; | |
493 | break; | |
494 | case GeneralName::uniformResourceIdentifierCid: | |
495 | /* IA5String, AsnOcts */ | |
496 | currCdsaName->nameType = GNT_URI; | |
497 | src = *currSnaccName->uniformResourceIdentifier; | |
498 | len = currSnaccName->uniformResourceIdentifier->Len(); | |
499 | break; | |
500 | case GeneralName::iPAddressCid: | |
501 | /* AsnOcts */ | |
502 | currCdsaName->nameType = GNT_IPAddress; | |
503 | src = *currSnaccName->iPAddress; | |
504 | len = currSnaccName->iPAddress->Len(); | |
505 | break; | |
506 | case GeneralName::registeredIDCid: | |
507 | /* AsnOid */ | |
508 | currCdsaName->nameType = GNT_RegisteredID; | |
509 | src = *currSnaccName->registeredID; | |
510 | len = currSnaccName->registeredID->Len(); | |
511 | break; | |
512 | } | |
513 | if(src == NULL) { | |
514 | /* punt - encode the complex object and give caller the encoded | |
515 | * bytes */ | |
516 | CASSERT(toBeEncoded != NULL); | |
517 | SC_encodeAsnObj(*toBeEncoded, aData, MAX_NAME_SIZE); | |
518 | src = aData; | |
519 | len = aData.length(); | |
520 | aData.release(); | |
29654253 | 521 | freeSrc = true; |
bac41a7b A |
522 | currCdsaName->berEncoded = CSSM_TRUE; |
523 | } | |
524 | else { | |
525 | CASSERT(toBeEncoded == NULL); | |
526 | currCdsaName->berEncoded = CSSM_FALSE; | |
527 | } | |
528 | ||
529 | /* src --> currCdsaName->name */ | |
530 | currCdsaName->name.Data = (uint8 *)alloc.malloc(len); | |
531 | currCdsaName->name.Length = len; | |
532 | memmove(currCdsaName->name.Data, src, len); | |
29654253 A |
533 | if(freeSrc) { |
534 | alloc.free(src); | |
535 | } | |
bac41a7b A |
536 | snaccObj.GoNext(); |
537 | } | |
538 | } | |
539 | ||
540 | /* CE_GeneralNames --> snacc-style GeneralNames */ | |
541 | /* GeneralNames from sm_x509cmn.h */ | |
542 | GeneralNames *CL_cdsaGeneralNamesToSnacc( | |
543 | CE_GeneralNames &cdsaObj) | |
544 | { | |
545 | GeneralNames *snaccObj = new GeneralNames; | |
546 | bool abortFlag = false; // true --> invalid incoming field | |
547 | CssmAllocator &alloc = CssmAllocator::standard(); | |
548 | ||
549 | for(unsigned i=0; i<cdsaObj.numNames; i++) { | |
550 | CE_GeneralName *currCdsaName = &cdsaObj.generalName[i]; | |
551 | char *rawData = reinterpret_cast<char *>(currCdsaName->name.Data); | |
552 | unsigned rawDataLen = currCdsaName->name.Length; | |
553 | GeneralName *currSnaccName = snaccObj->Append(); | |
554 | CssmData &berCdata = CssmData::overlay(currCdsaName->name); | |
555 | CssmRemoteData berData(alloc, berCdata); | |
556 | switch(currCdsaName->nameType) { | |
557 | case GNT_OtherName: | |
558 | /* OTHER_NAME, AsnOid */ | |
559 | if(currCdsaName->berEncoded) { | |
560 | abortFlag = true; | |
561 | break; | |
562 | } | |
563 | currSnaccName->choiceId = GeneralName::otherNameCid; | |
564 | currSnaccName->otherName = new AsnOid(rawData, rawDataLen); | |
565 | break; | |
566 | ||
567 | case GNT_RFC822Name: | |
568 | /* IA5String */ | |
569 | if(currCdsaName->berEncoded) { | |
570 | abortFlag = true; | |
571 | break; | |
572 | } | |
573 | currSnaccName->choiceId = GeneralName::rfc822NameCid; | |
574 | currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen); | |
575 | break; | |
576 | case GNT_DNSName: | |
577 | /* IA5String */ | |
578 | if(currCdsaName->berEncoded) { | |
579 | abortFlag = true; | |
580 | break; | |
581 | } | |
582 | currSnaccName->choiceId = GeneralName::dNSNameCid; | |
583 | currSnaccName->rfc822Name = new IA5String(rawData, rawDataLen); | |
584 | break; | |
585 | ||
586 | case GNT_X400Address: | |
587 | /* ORAddress from sm_x411mtsas */ | |
588 | if(!currCdsaName->berEncoded) { | |
589 | abortFlag = true; | |
590 | break; | |
591 | } | |
592 | currSnaccName->choiceId = GeneralName::x400AddressCid; | |
593 | currSnaccName->x400Address = new ORAddress; | |
594 | try { | |
595 | SC_decodeAsnObj(berData, *currSnaccName->x400Address); | |
596 | } | |
597 | catch(...) { | |
598 | abortFlag = true; | |
599 | } | |
600 | break; | |
601 | case GNT_DirectoryName: | |
602 | /* Name from sm_x501if */ | |
603 | /* We actually have to to deal with this in CertFields.cpp; | |
604 | * it'll be easy to support this (with a mod to | |
605 | * CE_GeneralName). | |
606 | */ | |
607 | if(!currCdsaName->berEncoded) { | |
608 | abortFlag = true; | |
609 | break; | |
610 | } | |
611 | currSnaccName->choiceId = GeneralName::directoryNameCid; | |
612 | currSnaccName->directoryName = new Name; | |
613 | try { | |
614 | SC_decodeAsnObj(berData, *currSnaccName->directoryName); | |
615 | } | |
616 | catch(...) { | |
617 | abortFlag = true; | |
618 | } | |
619 | break; | |
620 | ||
621 | case GNT_EdiPartyName: | |
622 | /* EDIPartyName from sm_x509cmn */ | |
623 | if(!currCdsaName->berEncoded) { | |
624 | abortFlag = true; | |
625 | break; | |
626 | } | |
627 | currSnaccName->choiceId = GeneralName::ediPartyNameCid; | |
628 | currSnaccName->ediPartyName = new EDIPartyName; | |
629 | try { | |
630 | SC_decodeAsnObj(berData, *currSnaccName->ediPartyName); | |
631 | } | |
632 | catch(...) { | |
633 | abortFlag = true; | |
634 | } | |
635 | break; | |
636 | ||
637 | case GNT_URI: | |
638 | /* IA5String */ | |
639 | if(currCdsaName->berEncoded) { | |
640 | abortFlag = true; | |
641 | break; | |
642 | } | |
643 | currSnaccName->choiceId = GeneralName::uniformResourceIdentifierCid; | |
644 | currSnaccName->uniformResourceIdentifier = | |
645 | new IA5String(rawData, rawDataLen); | |
646 | break; | |
647 | ||
648 | case GNT_IPAddress: | |
649 | /* AsnOcts */ | |
650 | if(currCdsaName->berEncoded) { | |
651 | abortFlag = true; | |
652 | break; | |
653 | } | |
654 | currSnaccName->choiceId = GeneralName::iPAddressCid; | |
655 | currSnaccName->iPAddress = new AsnOcts(rawData, rawDataLen); | |
656 | break; | |
657 | case GNT_RegisteredID: | |
658 | /* AsnOid */ | |
659 | if(currCdsaName->berEncoded) { | |
660 | abortFlag = true; | |
661 | break; | |
662 | } | |
663 | currSnaccName->choiceId = GeneralName::registeredIDCid; | |
664 | currSnaccName->registeredID = new AsnOid(rawData, rawDataLen); | |
665 | break; | |
666 | } | |
667 | berData.release(); | |
668 | if(abortFlag) { | |
669 | break; | |
670 | } | |
671 | } | |
672 | if(abortFlag) { | |
673 | delete snaccObj; | |
674 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
675 | } | |
676 | return snaccObj; | |
677 | } | |
678 | ||
679 | void CL_normalizeString( | |
680 | char *strPtr, | |
681 | int &strLen) | |
682 | { | |
683 | char *pCh = strPtr; // working ptr | |
684 | char *pD = pCh; // start of good string chars | |
685 | char *pEos = pCh + strLen - 1; | |
686 | ||
687 | if(strLen == 0) { | |
688 | return; | |
689 | } | |
690 | ||
691 | /* adjust if Length included NULL terminator */ | |
692 | while(*pEos == 0) { | |
693 | pEos--; | |
694 | } | |
695 | ||
696 | /* Remove trailing spaces */ | |
697 | while(isspace(*pEos)) { | |
698 | pEos--; | |
699 | } | |
700 | ||
701 | /* Point to one past last non-space character */ | |
702 | pEos++; | |
703 | ||
704 | /* upper case */ | |
705 | while(pCh < pEos) { | |
29654253 A |
706 | *pCh = toupper(*pCh); |
707 | pCh++; | |
bac41a7b A |
708 | } |
709 | ||
710 | /* clean out whitespace */ | |
711 | /* | |
712 | * 1. skip all leading whitespace | |
713 | */ | |
714 | pCh = pD; | |
715 | while(isspace(*pCh) && (pCh < pEos)) { | |
716 | pCh++; | |
717 | } | |
718 | ||
719 | /* | |
720 | * 2. eliminate multiple whitespace. | |
721 | * pCh points to first non-white char. | |
722 | * pD still points to start of string | |
723 | */ | |
724 | char ch; | |
725 | while(pCh < pEos) { | |
726 | ch = *pCh++; | |
727 | *pD++ = ch; // normal case | |
728 | if( isspace(ch) ){ | |
729 | /* skip 'til next nonwhite */ | |
730 | while(isspace(*pCh) && (pCh < pEos)) { | |
731 | pCh++; | |
732 | } | |
733 | } | |
734 | }; | |
735 | ||
736 | strLen = pD - strPtr; | |
737 | } | |
738 | ||
739 | /* | |
740 | * Normalize an RDN. Per RFC2459 (4.1.2.4), printable strings are case | |
741 | * insensitive and we're supposed to ignore leading and trailing | |
742 | * whitespace, and collapse multiple whitespace characters into one. | |
743 | */ | |
744 | void CL_normalizeX509Name( | |
745 | Name &name, | |
746 | CssmAllocator &alloc) | |
747 | { | |
748 | RDNSequence *rdns = name.rDNSequence; | |
749 | int numRdns = rdns->Count(); | |
750 | if((rdns == NULL) || (numRdns == 0)) { | |
751 | /* not technically an error */ | |
752 | return; | |
753 | } | |
754 | ||
755 | rdns->SetCurrElmt(0); | |
756 | for(int rdnDex=0; rdnDex<numRdns; rdnDex++) { | |
757 | /* from sm_x501if */ | |
758 | RelativeDistinguishedName *rdn = rdns->Curr(); | |
759 | if(rdn == NULL) { | |
760 | /* not sure how this can happen... */ | |
761 | dprintf1("clNormalizeX509Name: NULL rdn at index %d\n", rdnDex); | |
762 | rdns->GoNext(); | |
763 | continue; | |
764 | } | |
765 | int numAttrs = rdn->Count(); | |
766 | if(numAttrs == 0) { | |
767 | dprintf1("clNormalizeX509Name: zero numAttrs at index %d\n", rdnDex); | |
768 | rdns->GoNext(); | |
769 | continue; | |
770 | } | |
771 | ||
772 | /* descend into array of attribute/values */ | |
773 | rdn->SetCurrElmt(0); | |
774 | for(int attrDex=0; attrDex<numAttrs; attrDex++) { | |
775 | /* from sm_x501if */ | |
776 | AttributeTypeAndDistinguishedValue *att = rdn->Curr(); | |
777 | if(att == NULL) { | |
778 | /* not sure how this can happen... */ | |
779 | dprintf1("clNormalizeX509Name: NULL att at index %d\n", attrDex); | |
780 | rdn->GoNext(); | |
781 | continue; | |
782 | } | |
783 | ||
784 | /* | |
785 | * att->value is an AsnAny (CSM_Buffer) containing an encoded | |
786 | * string - supposedly a DirectoryString, but some certs put an | |
787 | * IA5String here which is not handled by DirectoryString. | |
788 | * | |
789 | * (See e.g. the Thawte serverbasic cert, which has an email | |
790 | * address in IA5String format.) In the IA5String case we skip the | |
791 | * normalization. | |
792 | * | |
793 | * Anyway, figure out what's there, snag the raw string, normalize the | |
794 | * string, cook up an appropriate DirectoryString for it, encode the | |
795 | * result, and put the encoding back in att->value. | |
796 | */ | |
797 | CSM_Buffer *cbuf = att->value.value; | |
798 | DirectoryString dirStr; | |
799 | char *cbufData = const_cast<char *>(cbuf->Access()); | |
800 | CssmData encodedStr(cbufData, cbuf->Length()); | |
801 | ||
802 | /* avoid exception if this is an IA5String... */ | |
803 | char tagByte = cbufData[0]; | |
804 | if((tagByte == (UNIV | PRIM | IA5STRING_TAG_CODE)) || | |
805 | (tagByte == (UNIV | CONS | IA5STRING_TAG_CODE))) { | |
806 | /* can't normalize */ | |
807 | return; | |
808 | } | |
809 | try { | |
810 | SC_decodeAsnObj(encodedStr, dirStr); | |
811 | } | |
812 | catch (...) { | |
813 | /* can't normalize */ | |
814 | errorLog0("clNormalizeX509Name: malformed DirectoryString (1)\n"); | |
815 | return; | |
816 | } | |
817 | ||
818 | /* normalize, we don't need to know what kind of string it is */ | |
819 | char *strPtr = *dirStr.teletexString; | |
820 | int newLen = dirStr.teletexString->Len(); | |
821 | CL_normalizeString(strPtr, newLen); | |
822 | ||
823 | /* set new AsnOcts data from normalized version, freeing old */ | |
824 | dirStr.teletexString->ReSet(strPtr, newLen); | |
825 | ||
826 | /* encode result */ | |
827 | CssmAutoData normEncoded(alloc); | |
828 | SC_encodeAsnObj(dirStr, normEncoded, newLen + 8); | |
829 | ||
830 | /* set new AsnAny data */ | |
831 | cbuf->Set((char *)normEncoded.data(), normEncoded.length()); | |
832 | ||
833 | rdn->GoNext(); | |
834 | } /* for each attribute/value */ | |
835 | rdns->GoNext(); | |
836 | } /* for each RDN */ | |
837 | } | |
838 | ||
29654253 A |
839 | /* |
840 | * Obtain a CSSM_KEY from a SubjectPublicKeyInfo, inferring as much as we can | |
841 | * from required fields (subjectPublicKeyInfo) and extensions (for | |
842 | * KeyUse, obtained from the optional DecodedCert). | |
843 | */ | |
844 | CSSM_KEY_PTR CL_extractCSSMKey( | |
845 | SubjectPublicKeyInfo &snaccKeyInfo, | |
846 | CssmAllocator &alloc, | |
847 | const DecodedCert *decodedCert) // optional | |
848 | { | |
849 | CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR) alloc.malloc(sizeof(CSSM_KEY)); | |
850 | memset(cssmKey, 0, sizeof(CSSM_KEY)); | |
851 | CSSM_KEYHEADER &hdr = cssmKey->KeyHeader; | |
852 | CssmRemoteData keyData(alloc, cssmKey->KeyData); | |
853 | try { | |
854 | hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; | |
855 | /* CspId blank */ | |
856 | hdr.BlobType = CSSM_KEYBLOB_RAW; | |
857 | hdr.AlgorithmId = CL_snaccOidToCssmAlg(snaccKeyInfo.algorithm->algorithm); | |
858 | ||
859 | /* | |
860 | * Format inferred from AlgorithmId. I have never seen these defined | |
861 | * anywhere, e.g., whart's the format of an RSA public key in a cert? | |
862 | * X509 certainly doesn't say. However. the following two cases are known | |
863 | * to be correct. | |
864 | */ | |
865 | switch(hdr.AlgorithmId) { | |
866 | case CSSM_ALGID_RSA: | |
867 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | |
868 | break; | |
869 | case CSSM_ALGID_DSA: | |
870 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186; | |
871 | break; | |
872 | case CSSM_ALGID_FEE: | |
873 | /* CSSM_KEYBLOB_RAW_FORMAT_NONE --> DER encoded */ | |
874 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
875 | break; | |
876 | default: | |
877 | /* punt */ | |
878 | hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
879 | } | |
880 | hdr.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; | |
881 | ||
882 | /* KeyUsage inferred from extensions */ | |
883 | if(decodedCert) { | |
884 | hdr.KeyUsage = decodedCert->inferKeyUsage(); | |
885 | } | |
886 | else { | |
887 | hdr.KeyUsage = CSSM_KEYUSE_ANY; | |
888 | } | |
889 | ||
890 | /* start/end date unknown, leave zero */ | |
891 | hdr.WrapAlgorithmId = CSSM_ALGID_NONE; | |
892 | hdr.WrapMode = CSSM_ALGMODE_NONE; | |
893 | ||
894 | /* | |
895 | * subjectPublicKeyInfo.subjectPublicKey (AsnBits) ==> KeyData | |
896 | */ | |
897 | SC_asnBitsToCssmData(snaccKeyInfo.subjectPublicKey, keyData); | |
898 | keyData.release(); | |
899 | ||
900 | /* | |
901 | * LogicalKeySizeInBits - ask the CSP | |
902 | */ | |
903 | CSSM_CSP_HANDLE cspHand = getGlobalCspHand(true); | |
904 | CSSM_KEY_SIZE keySize; | |
905 | CSSM_RETURN crtn; | |
906 | crtn = CSSM_QueryKeySizeInBits(cspHand, CSSM_INVALID_HANDLE, cssmKey, &keySize); | |
907 | if(crtn) { | |
908 | CssmError::throwMe(crtn); | |
909 | } | |
910 | cssmKey->KeyHeader.LogicalKeySizeInBits = | |
911 | keySize.LogicalKeySizeInBits; | |
912 | } | |
913 | catch (...) { | |
914 | alloc.free(cssmKey); | |
915 | throw; | |
916 | } | |
917 | return cssmKey; | |
918 | } | |
919 | ||
920 | void CL_freeCSSMKey( | |
921 | CSSM_KEY_PTR cssmKey, | |
922 | CssmAllocator &alloc, | |
923 | bool freeTop) | |
924 | { | |
925 | if(cssmKey == NULL) { | |
926 | return; | |
927 | } | |
928 | alloc.free(cssmKey->KeyData.Data); | |
929 | memset(cssmKey, 0, sizeof(CSSM_KEY)); | |
930 | if(freeTop) { | |
931 | alloc.free(cssmKey); | |
932 | } | |
933 | } | |
bac41a7b | 934 |