SecWrappedKeys.cpp
1 /* 2 * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * SecWrappedKeys.cpp - SecExportRep and SecImportRep methods dealing with 24 * wrapped private keys (other than PKCS8 format). 25 */ 26 27 #include "SecExternalRep.h" 28 #include "SecImportExportUtils.h" 29 #include "SecImportExportPem.h" 30 #include "SecImportExportCrypto.h" 31 #include <Security/cssmtype.h> 32 #include <Security/cssmapi.h> 33 #include <Security/SecKeyPriv.h> 34 #include <security_asn1/SecNssCoder.h> 35 #include <security_cdsa_utils/cuCdsaUtils.h> 36 37 #include <security_utilities/simulatecrash_assert.h> 38 39 using namespace Security; 40 using namespace KeychainCore; 41 42 static int hexToDigit( 43 char digit, 44 uint8 *rtn) // RETURNED 45 { 46 if((digit >= '0') && (digit <= '9')) { 47 *rtn = digit - '0'; 48 return 0; 49 } 50 if((digit >= 'a') && (digit <= 'f')) { 51 *rtn = digit - 'a' + 10; 52 return 0; 53 } 54 if((digit >= 'A') && (digit <= 'F')) { 55 *rtn = digit - 'A' + 10; 56 return 0; 57 } 58 return -1; 59 } 60 61 /* 62 * Convert two ascii characters starting at cp to an unsigned char. 63 * Returns nonzero on error. 64 */ 65 static int hexToUchar( 66 const char *cp, 67 uint8 *rtn) // RETURNED 68 { 69 uint8 rtnc = 0; 70 uint8 c; 71 if(hexToDigit(*cp++, &c)) { 72 return -1; 73 } 74 rtnc = c << 4; 75 if(hexToDigit(*cp, &c)) { 76 return -1; 77 } 78 rtnc |= c; 79 *rtn = rtnc; 80 return 0; 81 } 82 83 /* 84 * Given an array of PEM parameter lines, infer parameters for key derivation and 85 * encryption. 86 */ 87 static OSStatus opensslPbeParams( 88 CFArrayRef paramLines, // elements are CFStrings 89 SecNssCoder &coder, // IV allocd with this 90 /* remaining arguments RETURNED */ 91 CSSM_ALGORITHMS &pbeAlg, 92 CSSM_ALGORITHMS &keyAlg, 93 CSSM_ALGORITHMS &encrAlg, 94 CSSM_ENCRYPT_MODE &encrMode, 95 CSSM_PADDING &encrPad, 96 uint32 &keySizeInBits, 97 unsigned &blockSizeInBytes, 98 CSSM_DATA &iv) 99 { 100 /* 101 * This format requires PEM parameter lines. We could have gotten here 102 * without them if caller specified wrong format. 103 */ 104 if(paramLines == NULL) { 105 SecImpExpDbg("importWrappedKeyOpenssl: no PEM parameter lines"); 106 return errSecUnknownFormat; 107 } 108 CFStringRef dekInfo = NULL; 109 CFIndex numLines = CFArrayGetCount(paramLines); 110 for(CFIndex dex=0; dex<numLines; dex++) { 111 CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(paramLines, dex); 112 CFRange range; 113 range = CFStringFind(str, CFSTR("DEK-Info: "), 0); 114 if(range.length != 0) { 115 dekInfo = str; 116 break; 117 } 118 } 119 if(dekInfo == NULL) { 120 SecImpExpDbg("importWrappedKeyOpenssl: no DEK-Info lines"); 121 return errSecUnknownFormat; 122 } 123 124 /* drop down to C strings for low level grunging */ 125 char cstr[1024]; 126 if(!CFStringGetCString(dekInfo, cstr, sizeof(cstr), kCFStringEncodingASCII)) { 127 SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (1)"); 128 return errSecUnknownFormat; 129 } 130 131 /* 132 * This line looks like this: 133 * DEK-Info: DES-CBC,A22977A0A6A6F696 134 * 135 * Now parse, getting the cipher spec and the IV. 136 */ 137 char *cp = strchr(cstr, ':'); 138 if(cp == NULL) { 139 SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (2)"); 140 return errSecUnknownFormat; 141 } 142 if((cp[1] == ' ') && (cp[2] != '\0')) { 143 /* as it normally does... */ 144 cp += 2; 145 } 146 147 /* We only support DES and 3DES here */ 148 if(!strncmp(cp, "DES-EDE3-CBC", 12)) { 149 keyAlg = CSSM_ALGID_3DES_3KEY; 150 encrAlg = CSSM_ALGID_3DES_3KEY_EDE; 151 keySizeInBits = 64 * 3; 152 blockSizeInBytes = 8; 153 } 154 else if(!strncmp(cp, "DES-CBC", 7)) { 155 keyAlg = CSSM_ALGID_DES; 156 encrAlg = CSSM_ALGID_DES; 157 keySizeInBits = 64; 158 blockSizeInBytes = 8; 159 } 160 else { 161 SecImpExpDbg("importWrappedKeyOpenssl: unrecognized wrap alg (%s)", 162 cp); 163 return errSecUnknownFormat; 164 } 165 166 /* these are more or less fixed */ 167 pbeAlg = CSSM_ALGID_PBE_OPENSSL_MD5; 168 encrMode = CSSM_ALGMODE_CBCPadIV8; 169 encrPad = CSSM_PADDING_PKCS7; 170 171 /* now get the ASCII hex version of the IV */ 172 cp = strchr(cp, ','); 173 if(cp == NULL) { 174 SecImpExpDbg("importWrappedKeyOpenssl: No IV in DEK-Info line"); 175 return errSecUnknownFormat; 176 } 177 if(cp[1] != '\0') { 178 cp++; 179 } 180 181 /* remainder should be just the IV */ 182 if(strlen(cp) != (blockSizeInBytes * 2)) { 183 SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (1)"); 184 return errSecUnknownFormat; 185 } 186 187 coder.allocItem(iv, blockSizeInBytes); 188 for(unsigned dex=0; dex<blockSizeInBytes; dex++) { 189 if(hexToUchar(cp + (dex * 2), &iv.Data[dex])) { 190 SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (2)"); 191 return errSecUnknownFormat; 192 } 193 } 194 return errSecSuccess; 195 } 196 197 /* 198 * Common code to derive an openssl-wrap style wrap/unwrap key. 199 */ 200 static OSStatus deriveKeyOpensslWrap( 201 const SecKeyImportExportParameters *keyParams, // required 202 CSSM_CSP_HANDLE cspHand, // required 203 impExpVerifyPhrase vp, // import/export 204 CSSM_ALGORITHMS pbeAlg, 205 CSSM_ALGORITHMS keyAlg, 206 uint32 keySizeInBits, 207 const CSSM_DATA &salt, 208 CSSM_KEY_PTR derivedKey) 209 { 210 CFDataRef cfPhrase = NULL; 211 CSSM_KEY *passKey = NULL; 212 OSStatus ortn; 213 214 /* passphrase or passkey? */ 215 ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, vp, 216 (CFTypeRef *)&cfPhrase, &passKey); 217 if(ortn) { 218 return ortn; 219 } 220 /* subsequent errors to errOut: */ 221 222 CSSM_CRYPTO_DATA seed; 223 CSSM_CC_HANDLE ccHand = 0; 224 CSSM_ACCESS_CREDENTIALS creds; 225 SecNssCoder coder; 226 CSSM_DATA param = {0, NULL}; 227 CSSM_DATA dummyLabel; 228 229 memset(&seed, 0, sizeof(seed)); 230 if(cfPhrase != NULL) { 231 size_t len = CFDataGetLength(cfPhrase); 232 coder.allocItem(seed.Param, len); 233 memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len); 234 CFRelease(cfPhrase); 235 } 236 237 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 238 ortn = CSSM_CSP_CreateDeriveKeyContext(cspHand, 239 pbeAlg, 240 keyAlg, 241 keySizeInBits, 242 &creds, 243 passKey, // BaseKey 244 1, // iterCount - yup, this is what openssl does 245 &salt, 246 &seed, 247 &ccHand); 248 if(ortn) { 249 SecImpExpDbg("deriveKeyOpensslWrap: CSSM_CSP_CreateDeriveKeyContext error"); 250 goto errOut; 251 } 252 253 memset(derivedKey, 0, sizeof(CSSM_KEY)); 254 255 dummyLabel.Data = (uint8 *)"temp unwrap key"; 256 dummyLabel.Length = strlen((char *)dummyLabel.Data); 257 258 ortn = CSSM_DeriveKey(ccHand, 259 ¶m, // i.e., derived IV - don't want one 260 CSSM_KEYUSE_ANY, 261 /* not extractable even for the short time this key lives */ 262 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 263 &dummyLabel, 264 NULL, // cred and acl 265 derivedKey); 266 if(ortn) { 267 SecImpExpDbg("importWrappedKeyOpenssl: PKCS5 v1.5 CSSM_DeriveKey failure"); 268 } 269 270 errOut: 271 if(ccHand != 0) { 272 CSSM_DeleteContext(ccHand); 273 } 274 if(passKey != NULL) { 275 CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); 276 free(passKey); 277 } 278 return ortn; 279 } 280 281 OSStatus SecImportRep::importWrappedKeyOpenssl( 282 SecKeychainRef importKeychain, // optional 283 CSSM_CSP_HANDLE cspHand, // required 284 SecItemImportExportFlags flags, 285 const SecKeyImportExportParameters *keyParams, // optional 286 CFMutableArrayRef outArray) // optional, append here 287 { 288 assert(mExternFormat == kSecFormatWrappedOpenSSL); 289 290 /* I think this is an assert - only private keys are wrapped in opensssl format */ 291 assert(mExternType == kSecItemTypePrivateKey); 292 assert(cspHand != 0); 293 294 if(keyParams == NULL) { 295 return errSecParam; 296 } 297 298 OSStatus ortn; 299 SecNssCoder coder; 300 impExpKeyUnwrapParams unwrapParams; 301 CSSM_ALGORITHMS pbeAlg = CSSM_ALGID_NONE; 302 CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE; 303 uint32 keySizeInBits; 304 unsigned blockSizeInBytes; 305 306 memset(&unwrapParams, 0, sizeof(unwrapParams)); 307 308 /* parse PEM header lines */ 309 ortn = opensslPbeParams(mPemParamLines, coder, 310 pbeAlg, keyAlg, 311 unwrapParams.encrAlg, 312 unwrapParams.encrMode, 313 unwrapParams.encrPad, 314 keySizeInBits, 315 blockSizeInBytes, 316 unwrapParams.iv); 317 if(ortn) { 318 return ortn; 319 } 320 321 /* derive unwrapping key */ 322 CSSM_KEY unwrappingKey; 323 324 ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Import, pbeAlg, keyAlg, 325 keySizeInBits, 326 unwrapParams.iv, /* salt = IV for these algs */ 327 &unwrappingKey); 328 if(ortn) { 329 return ortn; 330 } 331 332 /* set up key to unwrap */ 333 CSSM_KEY wrappedKey; 334 CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader; 335 memset(&wrappedKey, 0, sizeof(CSSM_KEY)); 336 hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; 337 /* CspId : don't care */ 338 hdr.BlobType = CSSM_KEYBLOB_WRAPPED; 339 hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL; 340 hdr.AlgorithmId = mKeyAlg; 341 hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; 342 /* LogicalKeySizeInBits : calculated by CSP during unwrap */ 343 hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; 344 hdr.KeyUsage = CSSM_KEYUSE_ANY; 345 346 wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(mExternal); 347 wrappedKey.KeyData.Length = CFDataGetLength(mExternal); 348 349 unwrapParams.unwrappingKey = &unwrappingKey; 350 351 /* GO */ 352 ortn = impExpImportKeyCommon(&wrappedKey, importKeychain, cspHand, 353 flags, keyParams, &unwrapParams, NULL, outArray); 354 355 if(unwrappingKey.KeyData.Data != NULL) { 356 CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE); 357 } 358 return ortn; 359 } 360 361 /* 362 * Hard coded parameters for export, we only do one flavor. 363 */ 364 #define OPENSSL_WRAP_KEY_ALG CSSM_ALGID_3DES_3KEY 365 #define OPENSSL_WRAP_PBE_ALG CSSM_ALGID_PBE_OPENSSL_MD5 366 #define OPENSSL_WRAP_KEY_SIZE (64 * 3) 367 #define OPENSSL_WRAP_ENCR_ALG CSSM_ALGID_3DES_3KEY_EDE 368 #define OPENSSL_WRAP_ENCR_MODE CSSM_ALGMODE_CBCPadIV8 369 #define OPENSSL_WRAP_ENCR_PAD CSSM_PADDING_PKCS7 370 371 OSStatus impExpWrappedKeyOpenSslExport( 372 SecKeyRef secKey, 373 SecItemImportExportFlags flags, 374 const SecKeyImportExportParameters *keyParams, // optional 375 CFMutableDataRef outData, // output appended here 376 const char **pemHeader, // RETURNED 377 CFArrayRef *pemParamLines) // RETURNED 378 { 379 SecNssCoder coder; 380 CSSM_CSP_HANDLE cspHand = 0; 381 OSStatus ortn; 382 bool releaseCspHand = false; 383 CFMutableArrayRef paramLines; 384 CFStringRef cfStr; 385 char dekStr[100]; 386 char ivStr[3]; 387 388 if(keyParams == NULL) { 389 return errSecParam; 390 } 391 392 /* we need a CSPDL handle - try to get it from the key */ 393 ortn = SecKeyGetCSPHandle(secKey, &cspHand); 394 if(ortn) { 395 cspHand = cuCspStartup(CSSM_FALSE); 396 if(cspHand == 0) { 397 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 398 } 399 releaseCspHand = true; 400 } 401 /* subsequent errors to errOut: */ 402 403 /* 8 bytes of random IV/salt */ 404 uint8 saltIv[8]; 405 CSSM_DATA saltIvData = { 8, saltIv} ; 406 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, sizeof(saltIv), saltIv)); 407 408 /* derive wrapping key */ 409 CSSM_KEY wrappingKey; 410 wrappingKey.KeyData.Data = NULL; 411 wrappingKey.KeyData.Length = 0; 412 ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Export, 413 OPENSSL_WRAP_PBE_ALG, OPENSSL_WRAP_KEY_ALG, 414 OPENSSL_WRAP_KEY_SIZE, 415 saltIvData, // IV == salt for this wrapping alg 416 &wrappingKey); 417 if(ortn) { 418 goto errOut; 419 } 420 421 /* wrap the outgoing key */ 422 CSSM_KEY wrappedKey; 423 memset(&wrappedKey, 0, sizeof(CSSM_KEY)); 424 425 ortn = impExpExportKeyCommon(cspHand, secKey, &wrappingKey, &wrappedKey, 426 OPENSSL_WRAP_ENCR_ALG, OPENSSL_WRAP_ENCR_MODE, OPENSSL_WRAP_ENCR_PAD, 427 CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL, 428 CSSM_ATTRIBUTE_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE, 429 NULL, &saltIvData); 430 if(ortn) { 431 goto errOut; 432 } 433 434 /* 435 * That wrapped key's KeyData is our output 436 */ 437 CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length); 438 439 /* PEM header depends on key algorithm */ 440 switch(wrappedKey.KeyHeader.AlgorithmId) { 441 case CSSM_ALGID_RSA: 442 *pemHeader = PEM_STRING_RSA; 443 break; 444 case CSSM_ALGID_DH: 445 *pemHeader = PEM_STRING_DH_PRIVATE; 446 break; 447 case CSSM_ALGID_DSA: 448 *pemHeader = PEM_STRING_DSA; 449 break; 450 case CSSM_ALGID_ECDSA: 451 *pemHeader = PEM_STRING_ECDSA_PRIVATE; 452 break; 453 default: 454 SecImpExpDbg("impExpWrappedKeyOpenSslExport unknown private key alg " 455 "%lu", (unsigned long)wrappedKey.KeyHeader.AlgorithmId); 456 /* punt though I think something is seriously hosed */ 457 *pemHeader = "Private Key"; 458 } 459 CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE); 460 461 /* 462 * Last thing: set up outgoing PEM parameter lines 463 */ 464 assert(pemParamLines != NULL); 465 paramLines = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 466 cfStr = CFStringCreateWithCString(NULL, 467 "Proc-Type: 4,ENCRYPTED", kCFStringEncodingASCII); 468 CFArrayAppendValue(paramLines, cfStr); 469 CFRelease(cfStr); // owned by array now */ 470 strcpy(dekStr, "DEK-Info: DES-EDE3-CBC,"); 471 /* next goes the IV */ 472 for(unsigned dex=0; dex<8; dex++) { 473 sprintf(ivStr, "%02X", saltIv[dex]); 474 strcat(dekStr, ivStr); 475 } 476 cfStr = CFStringCreateWithCString(NULL, dekStr, kCFStringEncodingASCII); 477 CFArrayAppendValue(paramLines, cfStr); 478 CFRelease(cfStr); // owned by array now */ 479 /* and an empty line */ 480 cfStr = CFStringCreateWithCString(NULL, "", kCFStringEncodingASCII); 481 CFArrayAppendValue(paramLines, cfStr); 482 CFRelease(cfStr); // owned by array now */ 483 *pemParamLines = paramLines; 484 485 errOut: 486 if(wrappingKey.KeyData.Data != NULL) { 487 CSSM_FreeKey(cspHand, NULL, &wrappingKey, CSSM_FALSE); 488 } 489 return ortn; 490 491 } 492