2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
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>
34 #define rsaMiscDebug(args...) secdebug("rsaMisc", ## args)
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
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
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
);
56 if(hdr
.KeyClass
!= keyClass
) {
57 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
59 cspValidateIntendedKeyUsage(&hdr
, usage
);
60 cspVerifyKeyTimes(hdr
);
61 return cssmKeyToRsa(cssmKey
, session
, mallocdKey
);
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).
69 const CssmKey
&cssmKey
,
70 AppleCSPSession
&session
,
71 bool &allocdKey
) // RETURNED
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
);
81 switch(hdr
->BlobType
) {
82 case CSSM_KEYBLOB_RAW
:
83 rsaKey
= rawCssmKeyToRsa(cssmKey
);
86 case CSSM_KEYBLOB_REFERENCE
:
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
);
96 assert(rsaBinKey
->mRsaKey
!= NULL
);
97 rsaKey
= rsaBinKey
->mRsaKey
;
101 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
107 * Convert a raw CssmKey to a newly alloc'd RSA key.
109 RSA
*rawCssmKeyToRsa(
110 const CssmKey
&cssmKey
)
112 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
115 assert(hdr
->BlobType
== CSSM_KEYBLOB_RAW
);
117 if(hdr
->AlgorithmId
!= CSSM_ALGID_RSA
) {
118 // someone else's key (should never happen)
119 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
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
:
128 /* openssh real soon now */
129 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
132 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT
);
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
141 /* openssh real soon now */
142 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
145 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT
);
150 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
153 RSA
*rsaKey
= RSA_new();
155 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
159 crtn
= RSAPublicKeyDecode(rsaKey
, hdr
->Format
,
160 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
);
163 crtn
= RSAPrivateKeyDecode(rsaKey
, hdr
->Format
,
164 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
);
167 CssmError::throwMe(crtn
);
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.
177 CSSM_RETURN
dsaGetParamsFromKey(
179 const CssmKey
¶mKey
,
180 AppleCSPSession
&session
)
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
;
188 CSSM_RETURN crtn
= CSSM_OK
;
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
;
198 rsaMiscDebug("dsaGetParamsFromKey: partialKey %p paramKey %p",
199 partialKey
, dsaParamKey
);
201 partialKey
->q
= BN_dup(dsaParamKey
->q
);
202 partialKey
->p
= BN_dup(dsaParamKey
->p
);
203 partialKey
->g
= BN_dup(dsaParamKey
->g
);
207 DSA_free(dsaParamKey
);
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
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
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
);
232 if(hdr
.KeyClass
!= keyClass
) {
233 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
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
)) {
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.
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 "
255 CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
);
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).
267 DSA
*existKey
= rtnDsa
;
270 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
272 rtnDsa
->pub_key
= BN_dup(existKey
->pub_key
);
273 rsaMiscDebug("contextToDsaKey; temp partial copy %p", rtnDsa
);
278 * Add params from paramKey into rtnDsa
280 CSSM_RETURN crtn
= dsaGetParamsFromKey(rtnDsa
, *paramKey
, session
);
286 CssmError::throwMe(crtn
);
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).
298 const CssmKey
&cssmKey
,
299 AppleCSPSession
&session
,
300 bool &allocdKey
) // RETURNED
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
);
310 switch(hdr
->BlobType
) {
311 case CSSM_KEYBLOB_RAW
:
312 dsaKey
= rawCssmKeyToDsa(cssmKey
, session
, NULL
);
315 case CSSM_KEYBLOB_REFERENCE
:
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
);
325 assert(dsaBinKey
->mDsaKey
!= NULL
);
326 dsaKey
= dsaBinKey
->mDsaKey
;
330 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
336 * Convert a raw CssmKey to a newly alloc'd DSA key.
338 DSA
*rawCssmKeyToDsa(
339 const CssmKey
&cssmKey
,
340 AppleCSPSession
&session
,
341 const CssmKey
*paramKey
) // optional
343 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
346 assert(hdr
->BlobType
== CSSM_KEYBLOB_RAW
);
348 if(hdr
->AlgorithmId
!= CSSM_ALGID_DSA
) {
349 // someone else's key (should never happen)
350 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
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
:
359 /* openssh real soon now */
360 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
363 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT
);
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
373 /* openssh real soon now */
374 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
377 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT
);
382 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
385 DSA
*dsaKey
= DSA_new();
387 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
391 crtn
= DSAPublicKeyDecode(dsaKey
, hdr
->Format
,
392 cssmKey
.KeyData
.Data
,
393 cssmKey
.KeyData
.Length
);
396 crtn
= DSAPrivateKeyDecode(dsaKey
, hdr
->Format
,
397 cssmKey
.KeyData
.Data
,
398 cssmKey
.KeyData
.Length
);
401 CssmError::throwMe(crtn
);
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.
410 if(isPub
&& (dsaKey
->p
== NULL
) && (paramKey
!= NULL
)) {
411 rsaMiscDebug("rawCssmKeyToDsa; updating dsaKey %p", dsaKey
);
412 crtn
= dsaGetParamsFromKey(dsaKey
, *paramKey
, session
);
415 CssmError::throwMe(crtn
);
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.
426 void dsaKeyPrivToPub(
429 assert(dsaKey
!= NULL
);
430 assert(dsaKey
->priv_key
!= NULL
);
432 if(dsaKey
->pub_key
!= NULL
) {
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
);
441 BN_CTX
*ctx
= BN_CTX_new();
443 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
445 int rtn
= BN_mod_exp(dsaKey
->pub_key
,
452 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);