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                 auto_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
);