2 * Copyright (c) 2000-2001,2011-2012,2014 Apple 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_utilities/logging.h>
28 #include <security_utilities/debugging.h>
29 #include <openssl/bn_legacy.h>
30 #include <openssl/rsa_legacy.h>
31 #include <openssl/dsa_legacy.h>
32 #include <openssl/opensslerr.h>
33 #include <security_utilities/simpleprefs.h>
34 #include <security_utilities/threading.h>
35 #include <security_utilities/globalizer.h>
36 #include <CoreFoundation/CFNumber.h>
38 #define rsaMiscDebug(args...) secinfo("rsaMisc", ## args)
41 * Obtain and cache max key sizes. System preferences only consulted
42 * at most once per process.
46 * Do dictionary lookup, convert possible CFNumber to uint32.
47 * Does not alter val if valid number is not found.
49 static void rsaLookupVal(
54 CFNumberRef cfVal
= (CFNumberRef
)prefs
.getValue(key
);
58 if(CFGetTypeID(cfVal
) != CFNumberGetTypeID()) {
62 /* ensure the number is positive, not relying on gcc 64-bit arithmetic */
64 CFNumberRef cfLimit
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &s32
);
65 CFComparisonResult result
= CFNumberCompare(cfVal
, cfLimit
, NULL
);
67 if(result
== kCFCompareLessThan
) {
68 /* negative value in preference */
72 /* ensure the number fits in 31 bits (the useful size of a SInt32 for us) */
74 cfLimit
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &s32
);
75 result
= CFNumberCompare(cfVal
, cfLimit
, NULL
);
77 if(result
== kCFCompareGreaterThan
) {
78 /* too large; discard it */
82 if(!CFNumberGetValue(cfVal
, kCFNumberSInt64Type
, &s64
)) {
83 /* impossible, right? We already range checked */
91 uint32 maxPubExponentSize
;
95 /* one-time only prefs lookup */
96 RSAKeySizes::RSAKeySizes()
98 /* set defaults, these might get overridden */
99 maxKeySize
= RSA_MAX_KEY_SIZE
;
100 maxPubExponentSize
= RSA_MAX_PUB_EXPONENT_SIZE
;
102 /* now see if there are prefs set for either of these */
103 Dictionary
* d
= NULL
;
105 d
= Dictionary::CreateDictionary(kRSAKeySizePrefsDomain
, Dictionary::US_System
, true);
117 unique_ptr
<Dictionary
>apd(d
);
118 rsaLookupVal(*apd
, kRSAMaxKeySizePref
, maxKeySize
);
119 rsaLookupVal(*apd
, kRSAMaxPublicExponentPref
, maxPubExponentSize
);
127 static ModuleNexus
<RSAKeySizes
> rsaKeySizes
;
130 * Public functions to obtain the currently configured max sizes of
131 * RSA key and public exponent.
133 uint32
rsaMaxKeySize()
135 return rsaKeySizes().maxKeySize
;
138 uint32
rsaMaxPubExponentSize()
140 return rsaKeySizes().maxPubExponentSize
;
145 * -- obtain CSSM key (there must only be one)
146 * -- validate keyClass
147 * -- validate keyUsage
148 * -- convert to RSA *, allocating the RSA key if necessary
150 RSA
*contextToRsaKey(
151 const Context
&context
,
152 AppleCSPSession
&session
,
153 CSSM_KEYCLASS keyClass
, // CSSM_KEYCLASS_{PUBLIC,PRIVATE}_KEY
154 CSSM_KEYUSE usage
, // CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_SIGN, etc.
155 bool &mallocdKey
, // RETURNED
156 CSSM_DATA
&label
) // mallocd and RETURNED for OAEP
159 context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
, CSSMERR_CSP_MISSING_ATTR_KEY
);
160 const CSSM_KEYHEADER
&hdr
= cssmKey
.KeyHeader
;
161 if(hdr
.AlgorithmId
!= CSSM_ALGID_RSA
) {
162 CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH
);
164 if(hdr
.KeyClass
!= keyClass
) {
165 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
167 cspValidateIntendedKeyUsage(&hdr
, usage
);
168 cspVerifyKeyTimes(hdr
);
169 return cssmKeyToRsa(cssmKey
, session
, mallocdKey
, label
);
172 * Convert a CssmKey to an RSA * key. May result in the creation of a new
173 * RSA (when cssmKey is a raw key); allocdKey is true in that case
174 * in which case the caller generally has to free the allocd key).
177 const CssmKey
&cssmKey
,
178 AppleCSPSession
&session
,
179 bool &allocdKey
, // RETURNED
180 CSSM_DATA
&label
) // mallocd and RETURNED for OAEP
185 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
186 if(hdr
->AlgorithmId
!= CSSM_ALGID_RSA
) {
187 // someone else's key (should never happen)
188 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
190 switch(hdr
->BlobType
) {
191 case CSSM_KEYBLOB_RAW
:
192 rsaKey
= rawCssmKeyToRsa(cssmKey
, label
);
195 case CSSM_KEYBLOB_REFERENCE
:
197 BinaryKey
&binKey
= session
.lookupRefKey(cssmKey
);
198 RSABinaryKey
*rsaBinKey
= dynamic_cast<RSABinaryKey
*>(&binKey
);
199 /* this cast failing means that this is some other
200 * kind of binary key */
201 if(rsaBinKey
== NULL
) {
202 rsaMiscDebug("cssmKeyToRsa: wrong BinaryKey subclass\n");
203 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
205 assert(rsaBinKey
->mRsaKey
!= NULL
);
206 rsaKey
= rsaBinKey
->mRsaKey
;
210 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
216 * Convert a raw CssmKey to a newly alloc'd RSA key.
218 RSA
*rawCssmKeyToRsa(
219 const CssmKey
&cssmKey
,
220 CSSM_DATA
&label
) // mallocd and RETURNED for OAEP keys
222 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
226 assert(hdr
->BlobType
== CSSM_KEYBLOB_RAW
);
228 switch(hdr
->AlgorithmId
) {
231 case CSSM_ALGMODE_PKCS1_EME_OAEP
:
235 // someone else's key (should never happen)
236 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
239 /* validate and figure out what we're dealing with */
240 switch(hdr
->KeyClass
) {
241 case CSSM_KEYCLASS_PUBLIC_KEY
:
242 switch(hdr
->Format
) {
243 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
:
244 case CSSM_KEYBLOB_RAW_FORMAT_X509
:
245 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
246 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2
:
250 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT
);
252 if(isOaep
&& (hdr
->Format
!= CSSM_KEYBLOB_RAW_FORMAT_X509
)) {
253 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT
);
257 case CSSM_KEYCLASS_PRIVATE_KEY
:
258 switch(hdr
->Format
) {
259 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
: // default
260 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
: // openssl style
261 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
265 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT
);
267 if(isOaep
&& (hdr
->Format
!= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
)) {
268 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT
);
273 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
276 RSA
*rsaKey
= RSA_new();
278 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
283 crtn
= RSAOAEPPublicKeyDecode(rsaKey
,
284 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
,
288 crtn
= RSAOAEPPrivateKeyDecode(rsaKey
,
289 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
,
295 crtn
= RSAPublicKeyDecode(rsaKey
, hdr
->Format
,
296 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
);
299 crtn
= RSAPrivateKeyDecode(rsaKey
, hdr
->Format
,
300 cssmKey
.KeyData
.Data
, cssmKey
.KeyData
.Length
);
305 CssmError::throwMe(crtn
);
308 /* enforce max key size and max public exponent size */
310 uint32 keySize
= RSA_size(rsaKey
) * 8;
311 if(keySize
> rsaMaxKeySize()) {
312 rsaMiscDebug("rawCssmKeyToRsa: key size exceeded");
316 keySize
= BN_num_bytes(rsaKey
->e
) * 8;
317 if(keySize
> rsaMaxPubExponentSize()) {
319 rsaMiscDebug("rawCssmKeyToRsa: pub exponent size exceeded");
322 if (BN_is_one(rsaKey
->e
)) {
324 rsaMiscDebug("rawCssmKeyToRsa: e = 1");
328 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
334 * Given a partially formed DSA public key (with no p, q, or g) and a
335 * CssmKey representing a supposedly fully-formed DSA key, populate
336 * the public key's p, g, and q with values from the fully formed key.
338 CSSM_RETURN
dsaGetParamsFromKey(
340 const CssmKey
¶mKey
,
341 AppleCSPSession
&session
)
344 DSA
*dsaParamKey
= cssmKeyToDsa(paramKey
, session
, allocdKey
);
345 if(dsaParamKey
== NULL
) {
346 errorLog0("dsaGetParamsFromKey: bad paramKey\n");
347 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
;
349 CSSM_RETURN crtn
= CSSM_OK
;
351 /* require fully formed other key of course... */
352 if((dsaParamKey
->p
== NULL
) ||
353 (dsaParamKey
->q
== NULL
) ||
354 (dsaParamKey
->g
== NULL
)) {
355 errorLog0("dsaGetParamsFromKey: incomplete paramKey\n");
356 crtn
= CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
;
359 rsaMiscDebug("dsaGetParamsFromKey: partialKey %p paramKey %p",
360 partialKey
, dsaParamKey
);
362 partialKey
->q
= BN_dup(dsaParamKey
->q
);
363 partialKey
->p
= BN_dup(dsaParamKey
->p
);
364 partialKey
->g
= BN_dup(dsaParamKey
->g
);
368 DSA_free(dsaParamKey
);
375 * -- obtain CSSM key (there must only be one)
376 * -- validate keyClass
377 * -- validate keyUsage
378 * -- convert to DSA *, allocating the DSA key if necessary
380 DSA
*contextToDsaKey(
381 const Context
&context
,
382 AppleCSPSession
&session
,
383 CSSM_KEYCLASS keyClass
, // CSSM_KEYCLASS_{PUBLIC,PRIVATE}_KEY
384 CSSM_KEYUSE usage
, // CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_SIGN, etc.
385 bool &mallocdKey
) // RETURNED
388 context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
, CSSMERR_CSP_MISSING_ATTR_KEY
);
389 const CSSM_KEYHEADER
&hdr
= cssmKey
.KeyHeader
;
390 if(hdr
.AlgorithmId
!= CSSM_ALGID_DSA
) {
391 CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH
);
393 if(hdr
.KeyClass
!= keyClass
) {
394 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
396 cspValidateIntendedKeyUsage(&hdr
, usage
);
397 cspVerifyKeyTimes(hdr
);
398 DSA
*rtnDsa
= cssmKeyToDsa(cssmKey
, session
, mallocdKey
);
399 if((keyClass
== CSSM_KEYCLASS_PUBLIC_KEY
) &&
400 (rtnDsa
->p
== NULL
)) {
402 * Special case: this specific key is only partially formed;
403 * it's missing the DSA parameters p, g, and q. To proceed with this
404 * key, the caller must pass in another fully formned DSA public key
405 * in raw form in the context. If it's there we use those parameters.
407 rsaMiscDebug("contextToDsaKey; partial DSA key %p", rtnDsa
);
408 CssmKey
*paramKey
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_PARAM_KEY
);
409 if(paramKey
== NULL
) {
410 rsaMiscDebug("contextToDsaKey: missing DSA params, no pub key in "
416 CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
);
420 * If this is a ref key, we have to cook up a new DSA key to
421 * avoid modifying the existing key. If we started with a raw key,
422 * we can modify it directly since the underlying DSA key has
423 * a lifetime only as long as this context (and since the context
424 * contains the parameter-bearing key, the params are valid
425 * as long as the DSA key).
428 DSA
*existKey
= rtnDsa
;
431 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
433 rtnDsa
->pub_key
= BN_dup(existKey
->pub_key
);
434 rsaMiscDebug("contextToDsaKey; temp partial copy %p", rtnDsa
);
439 * Add params from paramKey into rtnDsa
441 CSSM_RETURN crtn
= dsaGetParamsFromKey(rtnDsa
, *paramKey
, session
);
447 CssmError::throwMe(crtn
);
454 * Convert a CssmKey to an DSA * key. May result in the creation of a new
455 * DSA (when cssmKey is a raw key); allocdKey is true in that case
456 * in which case the caller generally has to free the allocd key).
459 const CssmKey
&cssmKey
,
460 AppleCSPSession
&session
,
461 bool &allocdKey
) // RETURNED
466 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
467 if(hdr
->AlgorithmId
!= CSSM_ALGID_DSA
) {
468 // someone else's key (should never happen)
469 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
471 switch(hdr
->BlobType
) {
472 case CSSM_KEYBLOB_RAW
:
473 dsaKey
= rawCssmKeyToDsa(cssmKey
, session
, NULL
);
476 case CSSM_KEYBLOB_REFERENCE
:
478 BinaryKey
&binKey
= session
.lookupRefKey(cssmKey
);
479 DSABinaryKey
*dsaBinKey
= dynamic_cast<DSABinaryKey
*>(&binKey
);
480 /* this cast failing means that this is some other
481 * kind of binary key */
482 if(dsaBinKey
== NULL
) {
483 rsaMiscDebug("cssmKeyToDsa: wrong BinaryKey subclass\n");
484 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
486 assert(dsaBinKey
->mDsaKey
!= NULL
);
487 dsaKey
= dsaBinKey
->mDsaKey
;
491 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
497 * Convert a raw CssmKey to a newly alloc'd DSA key.
499 DSA
*rawCssmKeyToDsa(
500 const CssmKey
&cssmKey
,
501 AppleCSPSession
&session
,
502 const CssmKey
*paramKey
) // optional
504 const CSSM_KEYHEADER
*hdr
= &cssmKey
.KeyHeader
;
507 assert(hdr
->BlobType
== CSSM_KEYBLOB_RAW
);
509 if(hdr
->AlgorithmId
!= CSSM_ALGID_DSA
) {
510 // someone else's key (should never happen)
511 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
513 /* validate and figure out what we're dealing with */
514 switch(hdr
->KeyClass
) {
515 case CSSM_KEYCLASS_PUBLIC_KEY
:
516 switch(hdr
->Format
) {
517 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
:
518 case CSSM_KEYBLOB_RAW_FORMAT_X509
:
519 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2
:
523 CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT
);
527 case CSSM_KEYCLASS_PRIVATE_KEY
:
528 switch(hdr
->Format
) {
529 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
: // default
530 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
: // openssl style
531 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
: // SMIME style
533 /* openssh real soon now */
534 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
:
537 CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT
);
542 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
546 DSA
*dsaKey
= DSA_new();
548 if (dsaKey
== NULL
) {
549 crtn
= CSSMERR_CSP_MEMORY_ERROR
;
553 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
556 crtn
= DSAPublicKeyDecode(dsaKey
, hdr
->Format
,
557 cssmKey
.KeyData
.Data
,
558 cssmKey
.KeyData
.Length
);
561 crtn
= DSAPrivateKeyDecode(dsaKey
, hdr
->Format
,
562 cssmKey
.KeyData
.Data
,
563 cssmKey
.KeyData
.Length
);
568 if (dsaKey
!= NULL
) {
572 CssmError::throwMe(crtn
);
575 * Add in optional external parameters if this is not fully formed.
576 * This path is only taken from DSAKeyInfoProvider::CssmKeyToBinary,
577 * e.g., when doing a NULL unwrap of a partially formed DSA public
578 * key with the "complete the key with these params" option.
580 if(isPub
&& (dsaKey
->p
== NULL
) && (paramKey
!= NULL
)) {
581 rsaMiscDebug("rawCssmKeyToDsa; updating dsaKey %p", dsaKey
);
582 crtn
= dsaGetParamsFromKey(dsaKey
, *paramKey
, session
);
585 CssmError::throwMe(crtn
);
589 if(dsaKey
->p
!= NULL
) {
590 /* avoid use of provided DSA key which exceeds the max size */
591 uint32 keySize
= BN_num_bits(dsaKey
->p
);
592 if(keySize
> DSA_MAX_KEY_SIZE
) {
594 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
601 * Given a DSA private key, calculate its public component if it
602 * doesn't already exist. Used for calculating the key digest of
603 * an incoming raw private key.
605 void dsaKeyPrivToPub(
608 assert(dsaKey
!= NULL
);
609 assert(dsaKey
->priv_key
!= NULL
);
611 if(dsaKey
->pub_key
!= NULL
) {
615 /* logic copied from DSA_generate_key() */
616 dsaKey
->pub_key
= BN_new();
617 if(dsaKey
->pub_key
== NULL
) {
618 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
620 BN_CTX
*ctx
= BN_CTX_new();
622 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
624 int rtn
= BN_mod_exp(dsaKey
->pub_key
,
631 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);