]> git.saurik.com Git - apple/security.git/blob - AppleCSP/RSA_DSA/RSA_DSA_utils.cpp
Security-176.tar.gz
[apple/security.git] / AppleCSP / RSA_DSA / RSA_DSA_utils.cpp
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 * RSA_DSA_utils.cpp
21 */
22
23 #include "RSA_DSA_utils.h"
24 #include "RSA_DSA_keys.h"
25 #include <opensslUtils/opensslAsn1.h>
26 #include <opensslUtils/opensslUtils.h>
27 #include <Security/logging.h>
28 #include <Security/debugging.h>
29 #include <openssl/bn.h>
30 #include <openssl/rsa.h>
31 #include <openssl/dsa.h>
32 #include <openssl/err.h>
33
34 #define rsaMiscDebug(args...) secdebug("rsaMisc", ## args)
35
36 /*
37 * Given a Context:
38 * -- obtain CSSM key (there must only be one)
39 * -- validate keyClass
40 * -- validate keyUsage
41 * -- convert to RSA *, allocating the RSA key if necessary
42 */
43 RSA *contextToRsaKey(
44 const Context &context,
45 AppleCSPSession &session,
46 CSSM_KEYCLASS keyClass, // CSSM_KEYCLASS_{PUBLIC,PRIVATE}_KEY
47 CSSM_KEYUSE usage, // CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_SIGN, etc.
48 bool &mallocdKey) // RETURNED
49 {
50 CssmKey &cssmKey =
51 context.get<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
52 const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
53 if(hdr.AlgorithmId != CSSM_ALGID_RSA) {
54 CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH);
55 }
56 if(hdr.KeyClass != keyClass) {
57 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
58 }
59 cspValidateIntendedKeyUsage(&hdr, usage);
60 cspVerifyKeyTimes(hdr);
61 return cssmKeyToRsa(cssmKey, session, mallocdKey);
62 }
63 /*
64 * Convert a CssmKey to an RSA * key. May result in the creation of a new
65 * RSA (when cssmKey is a raw key); allocdKey is true in that case
66 * in which case the caller generally has to free the allocd key).
67 */
68 RSA *cssmKeyToRsa(
69 const CssmKey &cssmKey,
70 AppleCSPSession &session,
71 bool &allocdKey) // RETURNED
72 {
73 RSA *rsaKey = NULL;
74 allocdKey = false;
75
76 const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
77 if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
78 // someone else's key (should never happen)
79 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
80 }
81 switch(hdr->BlobType) {
82 case CSSM_KEYBLOB_RAW:
83 rsaKey = rawCssmKeyToRsa(cssmKey);
84 allocdKey = true;
85 break;
86 case CSSM_KEYBLOB_REFERENCE:
87 {
88 BinaryKey &binKey = session.lookupRefKey(cssmKey);
89 RSABinaryKey *rsaBinKey = dynamic_cast<RSABinaryKey *>(&binKey);
90 /* this cast failing means that this is some other
91 * kind of binary key */
92 if(rsaBinKey == NULL) {
93 rsaMiscDebug("cssmKeyToRsa: wrong BinaryKey subclass\n");
94 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
95 }
96 assert(rsaBinKey->mRsaKey != NULL);
97 rsaKey = rsaBinKey->mRsaKey;
98 break;
99 }
100 default:
101 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
102 }
103 return rsaKey;
104 }
105
106 /*
107 * Convert a raw CssmKey to a newly alloc'd RSA key.
108 */
109 RSA *rawCssmKeyToRsa(
110 const CssmKey &cssmKey)
111 {
112 const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
113 bool isPub;
114
115 assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
116
117 if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
118 // someone else's key (should never happen)
119 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
120 }
121 /* validate and figure out what we're dealing with */
122 switch(hdr->KeyClass) {
123 case CSSM_KEYCLASS_PUBLIC_KEY:
124 switch(hdr->Format) {
125 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
126 case CSSM_KEYBLOB_RAW_FORMAT_X509:
127 break;
128 /* openssh real soon now */
129 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
130 default:
131 CssmError::throwMe(
132 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
133 }
134 isPub = true;
135 break;
136 case CSSM_KEYCLASS_PRIVATE_KEY:
137 switch(hdr->Format) {
138 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: // default
139 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: // openssl style
140 break;
141 /* openssh real soon now */
142 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
143 default:
144 CssmError::throwMe(
145 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT);
146 }
147 isPub = false;
148 break;
149 default:
150 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
151 }
152
153 RSA *rsaKey = RSA_new();
154 if(rsaKey == NULL) {
155 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
156 }
157 CSSM_RETURN crtn;
158 if(isPub) {
159 crtn = RSAPublicKeyDecode(rsaKey, hdr->Format,
160 cssmKey.KeyData.Data, cssmKey.KeyData.Length);
161 }
162 else {
163 crtn = RSAPrivateKeyDecode(rsaKey, hdr->Format,
164 cssmKey.KeyData.Data, cssmKey.KeyData.Length);
165 }
166 if(crtn) {
167 CssmError::throwMe(crtn);
168 }
169 return rsaKey;
170 }
171
172 /*
173 * Given a partially formed DSA public key (with no p, q, or g) and a
174 * CssmKey representing a supposedly fully-formed DSA key, populate
175 * the public key's p, g, and q with values from the fully formed key.
176 */
177 CSSM_RETURN dsaGetParamsFromKey(
178 DSA *partialKey,
179 const CssmKey &paramKey,
180 AppleCSPSession &session)
181 {
182 bool allocdKey;
183 DSA *dsaParamKey = cssmKeyToDsa(paramKey, session, allocdKey);
184 if(dsaParamKey == NULL) {
185 errorLog0("dsaGetParamsFromKey: bad paramKey\n");
186 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
187 }
188 CSSM_RETURN crtn = CSSM_OK;
189
190 /* require fully formed other key of course... */
191 if((dsaParamKey->p == NULL) ||
192 (dsaParamKey->q == NULL) ||
193 (dsaParamKey->g == NULL)) {
194 errorLog0("dsaGetParamsFromKey: incomplete paramKey\n");
195 crtn = CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
196 goto abort;
197 }
198 rsaMiscDebug("dsaGetParamsFromKey: partialKey %p paramKey %p",
199 partialKey, dsaParamKey);
200
201 partialKey->q = BN_dup(dsaParamKey->q);
202 partialKey->p = BN_dup(dsaParamKey->p);
203 partialKey->g = BN_dup(dsaParamKey->g);
204
205 abort:
206 if(allocdKey) {
207 DSA_free(dsaParamKey);
208 }
209 return crtn;
210 }
211
212 /*
213 * Given a Context:
214 * -- obtain CSSM key (there must only be one)
215 * -- validate keyClass
216 * -- validate keyUsage
217 * -- convert to DSA *, allocating the DSA key if necessary
218 */
219 DSA *contextToDsaKey(
220 const Context &context,
221 AppleCSPSession &session,
222 CSSM_KEYCLASS keyClass, // CSSM_KEYCLASS_{PUBLIC,PRIVATE}_KEY
223 CSSM_KEYUSE usage, // CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_SIGN, etc.
224 bool &mallocdKey) // RETURNED
225 {
226 CssmKey &cssmKey =
227 context.get<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
228 const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
229 if(hdr.AlgorithmId != CSSM_ALGID_DSA) {
230 CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH);
231 }
232 if(hdr.KeyClass != keyClass) {
233 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
234 }
235 cspValidateIntendedKeyUsage(&hdr, usage);
236 cspVerifyKeyTimes(hdr);
237 DSA *rtnDsa = cssmKeyToDsa(cssmKey, session, mallocdKey);
238 if((keyClass == CSSM_KEYCLASS_PUBLIC_KEY) &&
239 (rtnDsa->p == NULL)) {
240 /*
241 * Special case: this specific key is only partially formed;
242 * it's missing the DSA parameters p, g, and q. To proceed with this
243 * key, the caller must pass in another fully formned DSA public key
244 * in raw form in the context. If it's there we use those parameters.
245 */
246 rsaMiscDebug("contextToDsaKey; partial DSA key %p", rtnDsa);
247 CssmKey *paramKey = context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
248 if(paramKey == NULL) {
249 rsaMiscDebug("contextToDsaKey: missing DSA params, no pub key in "
250 "context");
251 if(mallocdKey) {
252 DSA_free(rtnDsa);
253 mallocdKey = false;
254 }
255 CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE);
256 }
257
258 /*
259 * If this is a ref key, we have to cook up a new DSA key to
260 * avoid modifying the existing key. If we started with a raw key,
261 * we can modify it directly since the underlying DSA key has
262 * a lifetime only as long as this context (and since the context
263 * contains the parameter-bearing key, the params are valid
264 * as long as the DSA key).
265 */
266 if(!mallocdKey) {
267 DSA *existKey = rtnDsa;
268 rtnDsa = DSA_new();
269 if(rtnDsa == NULL) {
270 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
271 }
272 rtnDsa->pub_key = BN_dup(existKey->pub_key);
273 rsaMiscDebug("contextToDsaKey; temp partial copy %p", rtnDsa);
274 mallocdKey = true;
275 }
276
277 /*
278 * Add params from paramKey into rtnDsa
279 */
280 CSSM_RETURN crtn = dsaGetParamsFromKey(rtnDsa, *paramKey, session);
281 if(crtn) {
282 if(mallocdKey) {
283 DSA_free(rtnDsa);
284 mallocdKey = false;
285 }
286 CssmError::throwMe(crtn);
287 }
288 }
289 return rtnDsa;
290 }
291
292 /*
293 * Convert a CssmKey to an DSA * key. May result in the creation of a new
294 * DSA (when cssmKey is a raw key); allocdKey is true in that case
295 * in which case the caller generally has to free the allocd key).
296 */
297 DSA *cssmKeyToDsa(
298 const CssmKey &cssmKey,
299 AppleCSPSession &session,
300 bool &allocdKey) // RETURNED
301 {
302 DSA *dsaKey = NULL;
303 allocdKey = false;
304
305 const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
306 if(hdr->AlgorithmId != CSSM_ALGID_DSA) {
307 // someone else's key (should never happen)
308 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
309 }
310 switch(hdr->BlobType) {
311 case CSSM_KEYBLOB_RAW:
312 dsaKey = rawCssmKeyToDsa(cssmKey, session, NULL);
313 allocdKey = true;
314 break;
315 case CSSM_KEYBLOB_REFERENCE:
316 {
317 BinaryKey &binKey = session.lookupRefKey(cssmKey);
318 DSABinaryKey *dsaBinKey = dynamic_cast<DSABinaryKey *>(&binKey);
319 /* this cast failing means that this is some other
320 * kind of binary key */
321 if(dsaBinKey == NULL) {
322 rsaMiscDebug("cssmKeyToDsa: wrong BinaryKey subclass\n");
323 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
324 }
325 assert(dsaBinKey->mDsaKey != NULL);
326 dsaKey = dsaBinKey->mDsaKey;
327 break;
328 }
329 default:
330 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
331 }
332 return dsaKey;
333 }
334
335 /*
336 * Convert a raw CssmKey to a newly alloc'd DSA key.
337 */
338 DSA *rawCssmKeyToDsa(
339 const CssmKey &cssmKey,
340 AppleCSPSession &session,
341 const CssmKey *paramKey) // optional
342 {
343 const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
344 bool isPub;
345
346 assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
347
348 if(hdr->AlgorithmId != CSSM_ALGID_DSA) {
349 // someone else's key (should never happen)
350 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
351 }
352 /* validate and figure out what we're dealing with */
353 switch(hdr->KeyClass) {
354 case CSSM_KEYCLASS_PUBLIC_KEY:
355 switch(hdr->Format) {
356 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
357 case CSSM_KEYBLOB_RAW_FORMAT_X509:
358 break;
359 /* openssh real soon now */
360 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
361 default:
362 CssmError::throwMe(
363 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
364 }
365 isPub = true;
366 break;
367 case CSSM_KEYCLASS_PRIVATE_KEY:
368 switch(hdr->Format) {
369 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186: // default
370 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL: // openssl style
371 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: // SMIME style
372 break;
373 /* openssh real soon now */
374 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
375 default:
376 CssmError::throwMe(
377 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT);
378 }
379 isPub = false;
380 break;
381 default:
382 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
383 }
384
385 DSA *dsaKey = DSA_new();
386 if(dsaKey == NULL) {
387 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
388 }
389 CSSM_RETURN crtn;
390 if(isPub) {
391 crtn = DSAPublicKeyDecode(dsaKey, hdr->Format,
392 cssmKey.KeyData.Data,
393 cssmKey.KeyData.Length);
394 }
395 else {
396 crtn = DSAPrivateKeyDecode(dsaKey, hdr->Format,
397 cssmKey.KeyData.Data,
398 cssmKey.KeyData.Length);
399 }
400 if(crtn) {
401 CssmError::throwMe(crtn);
402 }
403
404 /*
405 * Add in optional external parameters if this is not fully formed.
406 * This path is only taken from DSAKeyInfoProvider::CssmKeyToBinary,
407 * e.g., when doing a NULL unwrap of a partially formed DSA public
408 * key with the "complete the key with these params" option.
409 */
410 if(isPub && (dsaKey->p == NULL) && (paramKey != NULL)) {
411 rsaMiscDebug("rawCssmKeyToDsa; updating dsaKey %p", dsaKey);
412 crtn = dsaGetParamsFromKey(dsaKey, *paramKey, session);
413 if(crtn) {
414 DSA_free(dsaKey);
415 CssmError::throwMe(crtn);
416 }
417 }
418 return dsaKey;
419 }
420
421 /*
422 * Given a DSA private key, calculate its public component if it
423 * doesn't already exist. Used for calculating the key digest of
424 * an incoming raw private key.
425 */
426 void dsaKeyPrivToPub(
427 DSA *dsaKey)
428 {
429 assert(dsaKey != NULL);
430 assert(dsaKey->priv_key != NULL);
431
432 if(dsaKey->pub_key != NULL) {
433 return;
434 }
435
436 /* logic copied from DSA_generate_key() */
437 dsaKey->pub_key = BN_new();
438 if(dsaKey->pub_key == NULL) {
439 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
440 }
441 BN_CTX *ctx = BN_CTX_new();
442 if (ctx == NULL) {
443 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
444 }
445 int rtn = BN_mod_exp(dsaKey->pub_key,
446 dsaKey->g,
447 dsaKey->priv_key,
448 dsaKey->p,
449 ctx);
450 BN_CTX_free(ctx);
451 if(rtn == 0) {
452 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
453 }
454 }