1/* 2 * Copyright (c) 2011-2012,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 * createFVMaster.c 24 */ 25 26#include "createFVMaster.h" 27 28#include "readline.h" 29#include "security.h" 30 31#include <pwd.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36#include <fcntl.h> 37 38#include <Security/SecKeychain.h> 39#include <Security/SecCertificate.h> 40#include <Security/SecKeychain.h> 41#include <Security/oidsalg.h> 42#include <Security/oidsattr.h> 43#include <limits.h> 44 45#include "srCdsaUtils.h" 46 47#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 48 49const char * const _masterKeychainName = "FileVaultMaster.keychain"; 50const char * const _masterKeychainPath = "./FileVaultMaster"; 51 52/* 53 * Parameters used to create key pairs and certificates in 54 * SR_CertificateAndKeyCreate(). 55 */ 56#define SR_KEY_ALGORITHM CSSM_ALGID_RSA 57#define SR_KEY_SIZE_IN_BITS 1024 58 59#define SR2_KEY_SIZE_IN_BITS 2048 // Recommended size for FileVault2 (FDE) 60 61/* 62 * The CSSM_ALGORITHMS and OID values defining the signature 63 * algorithm in the generated certificate. 64 */ 65#define SR_CERT_SIGNATURE_ALGORITHM CSSM_ALGID_SHA1WithRSA 66#define SR_CERT_SIGNATURE_ALG_OID CSSMOID_SHA1WithRSA 67 68OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef); 69 70OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert); 71OSStatus generateKeyPair(CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, CSSM_ALGORITHMS keyAlg, 72 uint32 keySizeInBits, const char *keyLabel, CSSM_KEY_PTR *pubKeyPtr, CSSM_KEY_PTR *privKeyPtr); 73OSStatus createRootCert(CSSM_TP_HANDLE tpHand, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, 74 CSSM_KEY_PTR subjPubKey, CSSM_KEY_PTR signerPrivKey, const char *hostName, const char *userName, 75 CSSM_ALGORITHMS sigAlg, const CSSM_OID *sigOid, CSSM_DATA_PTR certData); 76void randUint32(uint32 *u); 77 78static char *secCopyCString(CFStringRef theString); 79 80OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef) 81{ 82 /* 83 OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword); 84 85 *** In the real code, this will be done directly rather than exec'ing a tool, since there are too many parameters to specify 86 *** this needs to be done as root, since the keychain will be a system keychain 87 /usr/bin/certtool y c k=/Library/Keychains/FileVaultMaster.keychain p=<masterPasswordPassword> 88 /usr/bin/certtool c k=/Library/Keychains/FileVaultMaster.keychain o=/Library/Keychains/FileVaultMaster.cer 89 Two steps: create the keychain, then create the keypair 90 */ 91 92 SecAccessRef initialAccess = NULL; 93 94 if (!masterPasswordPassword) 95 { 96 sec_error("You must supply a non-empty password"); 97 return -2; 98 } 99 100 // We return an error if the keychain already exists 101 OSStatus status = SecKeychainCreate(fvmkcName, strlen(masterPasswordPassword), masterPasswordPassword, false, initialAccess, keychainRef); 102 if (status!=noErr) 103 { 104 if (status==errSecDuplicateKeychain || status==CSSMERR_DL_DATASTORE_ALREADY_EXISTS) 105 sec_error("The keychain file %s already exists", fvmkcName); 106 return status; 107 } 108 109 // Create the key pair 110 char host[PATH_MAX]; 111 int rx = gethostname(host, sizeof(host)); 112 if (rx) 113 strcpy(host, "localhost"); 114 115 CFStringRef hostName = CFSTR("FileVault Recovery Key"); // This is what shows up in Keychain Access display 116 CFStringRef userName = CFStringCreateWithCString(kCFAllocatorDefault, host, kCFStringEncodingUTF8); 117 CFDataRef certData = NULL; 118 printf("Generating a %d bit key pair; this may take several minutes\n", keySizeInBits); 119 status = createPair(hostName,userName,*keychainRef,keySizeInBits, &certData); 120 if (status) 121 sec_error("Error in createPair: %s", sec_errstr(status)); 122 if (certData) 123 CFRelease(certData); 124 125 return status; 126} 127 128OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert) 129{ 130 SecCertificateRef certRef = NULL; 131 CSSM_DL_DB_HANDLE dlDbHand = {0, 0}; 132 CSSM_CSP_HANDLE cspHand = 0; 133 CSSM_TP_HANDLE tpHand = 0; 134 CSSM_CL_HANDLE clHand = 0; 135 CSSM_KEY_PTR pubKey = NULL; 136 CSSM_KEY_PTR privKey = NULL; 137 CSSM_DATA certData = {0, NULL}; 138 char *hostStr = NULL; 139 char *userStr = NULL; 140 OSStatus ortn; 141 CSSM_OID algOid = SR_CERT_SIGNATURE_ALG_OID; 142 143 hostStr = secCopyCString(hostName); 144 userStr = secCopyCString(userName); 145 if (!hostStr || !userStr) // could not convert to UTF-8 146 { 147 ortn = paramErr; 148 goto xit; 149 } 150 151 // open keychain, connect to all the CDSA modules we'll need 152 153 ortn = SecKeychainGetCSPHandle(keychainRef, &cspHand); 154 if (ortn) 155 goto xit; 156 157 ortn = SecKeychainGetDLDBHandle(keychainRef, &dlDbHand); 158 if (ortn) 159 goto xit; 160 161 tpHand = srTpStartup(); 162 if (tpHand == 0) 163 { 164 ortn = ioErr; 165 goto xit; 166 } 167 168 clHand = srClStartup(); 169 if (clHand == 0) 170 { 171 ortn = ioErr; 172 goto xit; 173 } 174 175 // generate key pair, private key stored in keychain 176 ortn = generateKeyPair(cspHand, dlDbHand, SR_KEY_ALGORITHM, keySizeInBits, 177 "FileVault Master Password Key", &pubKey, &privKey); 178 if (ortn) 179 goto xit; 180 181 // generate the cert 182 ortn = createRootCert(tpHand,clHand,cspHand,pubKey,privKey,hostStr,userStr, 183 SR_CERT_SIGNATURE_ALGORITHM,&algOid,&certData); 184 if (ortn) 185 goto xit; 186 187 // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData] 188 ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef); 189 if (ortn) 190 goto xit; 191 192 ortn = SecCertificateAddToKeychain(certRef, keychainRef); 193 if (ortn) 194 goto xit; 195 196 // return the cert to caller 197 *cert = CFDataCreate(NULL, certData.Data, certData.Length); 198 199 // cleanup 200xit: 201 if (certRef) 202 CFRelease(certRef); 203 if (certData.Data) 204 free(certData.Data); 205 if (hostStr) 206 free(hostStr); 207 if (userStr) 208 free(userStr); 209 if (tpHand) 210 CSSM_ModuleDetach(tpHand); 211 if (clHand) 212 CSSM_ModuleDetach(clHand); 213 if (pubKey) 214 { 215 CSSM_FreeKey(cspHand, 216 NULL, // access cred 217 pubKey, 218 CSSM_FALSE); // delete 219 APP_FREE(pubKey); 220 } 221 if (privKey) 222 { 223 CSSM_FreeKey(cspHand, 224 NULL, // access cred 225 privKey, 226 CSSM_FALSE); // delete 227 APP_FREE(privKey); 228 } 229 230 return ortn; 231} 232 233/* 234* Given a CFStringRef, this function allocates and returns a pointer 235* to a null-terminated 'C' string copy. If conversion of the string 236* to UTF8 fails for some reason, the function will return NULL. 237* 238* The caller must free this pointer 239*/ 240 241char *secCopyCString(CFStringRef theString) 242{ 243 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(theString), kCFStringEncodingUTF8) + 1; 244 char* buffer = (char*) malloc(maxLength); 245 Boolean converted = CFStringGetCString(theString, buffer, maxLength, kCFStringEncodingUTF8); 246 if (!converted) { 247 free(buffer); 248 buffer = NULL; 249 } 250 return buffer; 251} 252 253 254#pragma mark -------------------- SecFileVaultCert private implementation -------------------- 255 256OSStatus createRootCert( 257 CSSM_TP_HANDLE tpHand, 258 CSSM_CL_HANDLE clHand, 259 CSSM_CSP_HANDLE cspHand, 260 CSSM_KEY_PTR subjPubKey, 261 CSSM_KEY_PTR signerPrivKey, 262 const char *hostName, // CSSMOID_CommonName 263 const char *userName, // CSSMOID_Description 264 CSSM_ALGORITHMS sigAlg, 265 const CSSM_OID *sigOid, 266 CSSM_DATA_PTR certData) // mallocd and RETURNED 267{ 268 CE_DataAndType exts[2]; 269 CE_DataAndType *extp = exts; 270 unsigned numExts; 271 CSSM_DATA refId; // mallocd by 272 // CSSM_TP_SubmitCredRequest 273 CSSM_APPLE_TP_CERT_REQUEST certReq; 274 CSSM_TP_REQUEST_SET reqSet; 275 sint32 estTime; 276 CSSM_BOOL confirmRequired; 277 CSSM_TP_RESULT_SET_PTR resultSet=NULL; 278 CSSM_ENCODED_CERT *encCert=NULL; 279 CSSM_APPLE_TP_NAME_OID subjectNames[2]; 280 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext; 281 CSSM_FIELD policyId; 282 283 numExts = 0; 284 285 certReq.challengeString = NULL; 286 287 /* KeyUsage extension */ 288 extp->type = DT_KeyUsage; 289 extp->critical = CSSM_FALSE; 290 extp->extension.keyUsage = CE_KU_DigitalSignature | 291 CE_KU_KeyCertSign | 292 CE_KU_KeyEncipherment | 293 CE_KU_DataEncipherment; 294 extp++; 295 numExts++; 296 297 /* BasicConstraints */ 298 extp->type = DT_BasicConstraints; 299 extp->critical = CSSM_TRUE; 300 extp->extension.basicConstraints.cA = CSSM_FALSE; 301 extp->extension.basicConstraints.pathLenConstraintPresent = CSSM_FALSE; 302 extp++; 303 numExts++; 304 305 /* name array */ 306 subjectNames[0].string = hostName; 307 subjectNames[0].oid = &CSSMOID_CommonName; 308 subjectNames[1].string = userName; 309 subjectNames[1].oid = &CSSMOID_Description; 310 311 /* certReq */ 312 certReq.cspHand = cspHand; 313 certReq.clHand = clHand; 314 randUint32(&certReq.serialNumber); // random serial number 315 certReq.numSubjectNames = 2; 316 certReq.subjectNames = subjectNames; 317 318 certReq.numIssuerNames = 0; // root for now 319 certReq.issuerNames = NULL; 320 certReq.issuerNameX509 = NULL; 321 certReq.certPublicKey = subjPubKey; 322 certReq.issuerPrivateKey = signerPrivKey; 323 certReq.signatureAlg = sigAlg; 324 certReq.signatureOid = *sigOid; 325 certReq.notBefore = 0; 326 certReq.notAfter = 60 * 60 * 24 * 365; // seconds from now, one year 327 certReq.numExtensions = numExts; 328 certReq.extensions = exts; 329 330 reqSet.NumberOfRequests = 1; 331 reqSet.Requests = &certReq; 332 333 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */ 334 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); 335 memset(&policyId, 0, sizeof(CSSM_FIELD)); 336 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; 337 338 CallerAuthContext.Policy.NumberOfPolicyIds = 1; 339 CallerAuthContext.Policy.PolicyIds = &policyId; 340 341 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand, 342 NULL, // PreferredAuthority 343 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, 344 &reqSet, 345 &CallerAuthContext, 346 &estTime, 347 &refId); 348 349 if(crtn) { 350 sec_error("CSSM_TP_SubmitCredRequest: %s", sec_errstr(crtn)); 351 goto xit; 352 } 353 crtn = CSSM_TP_RetrieveCredResult(tpHand, 354 &refId, 355 NULL, // CallerAuthCredentials 356 &estTime, 357 &confirmRequired, 358 &resultSet); 359 if(crtn) { 360 sec_error("CSSM_TP_RetrieveCredResult: %s", sec_errstr(crtn)); 361 goto xit; 362 } 363 if(resultSet == NULL) { 364 sec_error("CSSM_TP_RetrieveCredResult: returned NULL result set"); 365 crtn = ioErr; 366 goto xit; 367 } 368 encCert = (CSSM_ENCODED_CERT *)resultSet->Results; 369 certData->Length = encCert->CertBlob.Length; 370 certData->Data = malloc(encCert->CertBlob.Length); 371 if (certData->Data) 372 memcpy(certData->Data, encCert->CertBlob.Data, encCert->CertBlob.Length); 373 crtn = noErr; 374 375xit: 376 /* free resources allocated by TP */ 377 APP_FREE(refId.Data); 378 if (encCert) 379 { 380 if (encCert->CertBlob.Data) 381 { 382 APP_FREE(encCert->CertBlob.Data); 383 } 384 APP_FREE(encCert); 385 } 386 APP_FREE(resultSet); 387 return crtn; 388} 389 390/* Convert a reference key to a raw key. */ 391static CSSM_RETURN refKeyToRaw( 392 CSSM_CSP_HANDLE cspHand, 393 const CSSM_KEY *refKey, 394 CSSM_KEY_PTR rawKey) // RETURNED 395{ 396 CSSM_CC_HANDLE ccHand; 397 CSSM_RETURN crtn; 398 CSSM_ACCESS_CREDENTIALS creds; 399 400 memset(rawKey, 0, sizeof(CSSM_KEY)); 401 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 402 crtn = CSSM_CSP_CreateSymmetricContext(cspHand, 403 CSSM_ALGID_NONE, 404 CSSM_ALGMODE_NONE, 405 &creds, // passPhrase 406 NULL, // wrapping key 407 NULL, // init vector 408 CSSM_PADDING_NONE, // Padding 409 0, // Params 410 &ccHand); 411 if(crtn) { 412 sec_error("CSSM_CSP_CreateSymmetricContext: refKeyToRaw context err: %s", sec_errstr(crtn)); 413 return crtn; 414 } 415 416 crtn = CSSM_WrapKey(ccHand, 417 &creds, 418 refKey, 419 NULL, // DescriptiveData 420 rawKey); 421 if(crtn != CSSM_OK) { 422 sec_error("CSSM_WrapKey: refKeyToRaw wrap err: %s", sec_errstr(crtn)); 423 return crtn; 424 } 425 CSSM_DeleteContext(ccHand); 426 return CSSM_OK; 427} 428 429/* 430 * Find private key by label, modify its Label attr to be the 431 * hash of the associated public key. 432 */ 433CSSM_RETURN setPubKeyHash( 434 CSSM_CSP_HANDLE cspHand, 435 CSSM_DL_DB_HANDLE dlDbHand, 436 const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL 437 const char *keyLabel) // look up by this 438{ 439 CSSM_QUERY query; 440 CSSM_SELECTION_PREDICATE predicate; 441 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 442 CSSM_RETURN crtn; 443 CSSM_DATA labelData; 444 CSSM_HANDLE resultHand; 445 446 labelData.Data = (uint8 *)keyLabel; 447 labelData.Length = strlen(keyLabel) + 1; // incl. NULL 448 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 449 query.Conjunctive = CSSM_DB_NONE; 450 query.NumSelectionPredicates = 1; 451 predicate.DbOperator = CSSM_DB_EQUAL; 452 453 predicate.Attribute.Info.AttributeNameFormat = 454 CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 455 predicate.Attribute.Info.Label.AttributeName = "Label"; 456 predicate.Attribute.Info.AttributeFormat = 457 CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 458 predicate.Attribute.Value = &labelData; 459 query.SelectionPredicate = &predicate; 460 461 query.QueryLimits.TimeLimit = 0; 462 query.QueryLimits.SizeLimit = 1; 463 query.QueryFlags = 0; 464 465 /* build Record attribute with one attr */ 466 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 467 CSSM_DB_ATTRIBUTE_DATA attr; 468 attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 469 attr.Info.Label.AttributeName = "Label"; 470 attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 471 472 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 473 recordAttrs.NumberOfAttributes = 1; 474 recordAttrs.AttributeData = &attr; 475 476 crtn = CSSM_DL_DataGetFirst(dlDbHand, 477 &query, 478 &resultHand, 479 &recordAttrs, 480 NULL, // hopefully optional ...theData, 481 &record); 482 /* abort only on success */ 483 if(crtn != CSSM_OK) { 484 sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn)); 485 return crtn; 486 } 487 488 /* 489 * If specified key is a ref key, do NULL unwrap for use with raw CSP. 490 * If the CSPDL and SecurityServer support the key digest passthrough 491 * this is unnecessary. 492 */ 493 CSSM_KEY rawKeyToDigest; 494 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { 495 crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest); 496 if(crtn) { 497 sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn)); 498 return crtn; 499 } 500 } 501 else { 502 /* use as is */ 503 rawKeyToDigest = *pubOrPrivKey; 504 } 505 506 /* connect to raw CSP */ 507 CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE); 508 if(rawCspHand == 0) { 509 printf("***Error connecting to raw CSP; aborting.\n"); 510 return -1; 511 } 512 513 /* calculate hash of pub key from private or public part */ 514 CSSM_DATA_PTR keyDigest = NULL; 515 CSSM_CC_HANDLE ccHand; 516 crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand, 517 &rawKeyToDigest, 518 &ccHand); 519 if(ccHand == 0) { 520 sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); 521 return -1; 522 } 523 crtn = CSSM_CSP_PassThrough(ccHand, 524 CSSM_APPLECSP_KEYDIGEST, 525 NULL, 526 (void **)&keyDigest); 527 if(crtn) { 528 sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); 529 return crtn; 530 } 531 if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { 532 /* created in refKeyToRaw().... */ 533 CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE); 534 } 535 CSSM_DeleteContext(ccHand); 536 CSSM_ModuleDetach(rawCspHand); 537 538 /* 539 * Replace Label attr data with hash. 540 * NOTE: the module which allocated this attribute data - a DL - 541 * was loaded and attached by the Sec layer, not by us. Thus 542 * we can't use the memory allocator functions *we* used when 543 * attaching to the CSPDL - we have to use the ones 544 * which the Sec layer registered with the DL. 545 */ 546 CSSM_API_MEMORY_FUNCS memFuncs; 547 crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs); 548 if(crtn) { 549 sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn)); 550 /* oh well, leak and continue */ 551 } 552 else { 553 memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef); 554 memFuncs.free_func(attr.Value, memFuncs.AllocRef); 555 } 556 attr.Value = keyDigest; 557 558 /* modify key attributes */ 559 crtn = CSSM_DL_DataModify(dlDbHand, 560 CSSM_DL_DB_RECORD_PRIVATE_KEY, 561 record, 562 &recordAttrs, 563 NULL, // DataToBeModified 564 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); 565 if(crtn) { 566 sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn)); 567 return crtn; 568 } 569 crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand); 570 if(crtn) { 571 sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn)); 572 /* let's keep going in this case */ 573 } 574 crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record); 575 if(crtn) { 576 sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn)); 577 /* let's keep going in this case */ 578 crtn = CSSM_OK; 579 } 580 581 /* free resources */ 582 if (keyDigest) 583 { 584 srAppFree(keyDigest->Data, NULL); 585 srAppFree(keyDigest, NULL); 586 } 587 return CSSM_OK; 588} 589 590/* 591 * Generate a key pair using the CSPDL. 592 */ 593OSStatus generateKeyPair( 594 CSSM_CSP_HANDLE cspHand, 595 CSSM_DL_DB_HANDLE dlDbHand, 596 CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA 597 uint32 keySizeInBits, 598 const char *keyLabel, // C string 599 CSSM_KEY_PTR *pubKeyPtr, // mallocd, created, RETURNED 600 CSSM_KEY_PTR *privKeyPtr) // mallocd, created, RETURNED 601{ 602 CSSM_KEY_PTR pubKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY))); 603 CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY))); 604 if((pubKey == NULL) || (privKey == NULL)) { 605 return memFullErr; 606 } 607 608 CSSM_RETURN crtn; 609 CSSM_KEYUSE pubKeyUse; 610 CSSM_KEYUSE privKeyUse; 611 612 pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | 613 CSSM_KEYUSE_WRAP; 614 privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT | 615 CSSM_KEYUSE_UNWRAP; 616 617 crtn = srCspGenKeyPair(cspHand, 618 &dlDbHand, 619 keyAlg, 620 keyLabel, 621 strlen(keyLabel) + 1, 622 keySizeInBits, 623 pubKey, 624 pubKeyUse, 625 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF, 626 privKey, 627 privKeyUse, 628 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | 629 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE); 630 631 if(crtn) { 632 APP_FREE(pubKey); 633 APP_FREE(privKey); 634 return paramErr; 635 } 636 637 /* bind private key to cert by public key hash */ 638 crtn = setPubKeyHash(cspHand, 639 dlDbHand, 640 pubKey, 641 keyLabel); 642 if(crtn) { 643 sec_error("setPubKeyHash: Error setting public key hash. Continuing at peril: %s", sec_errstr(crtn)); 644 } 645 646 *pubKeyPtr = pubKey; 647 *privKeyPtr = privKey; 648 return noErr; 649} 650 651// Fill a uint32 with random data 652void randUint32(uint32 *u) 653{ 654 int dev = open("/dev/random", O_RDONLY); 655 if(dev < 0) { 656 return; 657 } 658 read(dev, u, sizeof(*u)); 659 close(dev); 660} 661 662 663//========================================================================== 664 665int 666keychain_createMFV(int argc, char * const *argv) 667{ 668 int zero_password = 0; 669 char *password = NULL; 670 const char *keychainName = NULL; 671 int result = 0, ch = 0; 672 Boolean do_prompt = FALSE; 673 SecKeychainRef keychainRef = NULL; 674 uint32 keySizeInBits = SR2_KEY_SIZE_IN_BITS; // default 675 676/* AG: getopts optstring name [args] 677 AG: while loop calling getopt is used to extract password from cl from user 678 password is the only option to keychain_create 679 optstring is a string containing the legitimate option 680 characters. If such a character is followed by a colon, 681 the option requires an argument, so getopt places a 682 pointer to the following text in the same argv-element, or 683 the text of the following argv-element, in optarg. 684*/ 685 while ((ch = getopt(argc, argv, "hp:s:P")) != -1) 686 { 687 switch (ch) 688 { 689 case 'p': 690 password = optarg; 691 break; 692 case 'P': 693 do_prompt = TRUE; 694 break; 695 case 's': 696 // Specify the keysize in bits (default 1024) 697 keySizeInBits = atoi(optarg); 698 if (!(keySizeInBits == SR_KEY_SIZE_IN_BITS || keySizeInBits == SR2_KEY_SIZE_IN_BITS || keySizeInBits == 4096)) 699 return 2; 700 break; 701 case '?': 702 default: 703 return 2; /* @@@ Return 2 triggers usage message. */ 704 } 705 } 706/* 707 AG: The external variable optind is the index of the next 708 array element of argv[] to be processed; it communicates 709 from one call of getopt() to the next which element to 710 process. 711 The variable optind is the index of the next element of the argv[] vector to be processed. It shall be initialized to 1 by the system, and getopt() shall update it when it finishes with each element of argv[]. When an element of argv[] contains multiple option characters, it is unspecified how getopt() determines which options have already been processed. 712 713*/ 714 argc -= optind; 715 argv += optind; 716 717 if (argc > 1) 718 return 2; /* @@@ Return 2 triggers usage message. */ 719 720 keychainName = (argc == 1)?*argv:_masterKeychainName; 721 if (!keychainName || *keychainName == '\0') 722 return -1; 723 724 if (!password && !do_prompt) 725 { 726 int compare = 1; 727 int tries; 728 729 for (tries = 3; tries-- > 0;) 730 { 731 char *firstpass; 732 733 password = getpass("password for new keychain: "); 734 if (!password) 735 { 736 result = -1; 737 goto loser; 738 } 739 740 firstpass = malloc(strlen(password) + 1); 741 strcpy(firstpass, password); 742 password = getpass("retype password for new keychain: "); 743 compare = password ? strcmp(password, firstpass) : 1; 744 memset(firstpass, 0, strlen(firstpass)); 745 free(firstpass); 746 if (!password) 747 { 748 result = -1; 749 goto loser; 750 } 751 752 if (compare) 753 { 754 fprintf(stderr, "passwords don't match\n"); 755 memset(password, 0, strlen(password)); 756 } 757 else 758 { 759 zero_password = 1; 760 break; 761 } 762 } 763 764 if (compare) 765 { 766 result = 1; 767 goto loser; 768 } 769 } 770 771 do 772 { 773 // result = do_create(keychain, password, do_prompt); 774 result = makeMasterPassword(keychainName, password, keySizeInBits, &keychainRef); 775 if (keychainRef) 776 CFRelease(keychainRef); 777 if (zero_password) 778 memset(password, 0, strlen(password)); 779 if (result) 780 goto loser; 781 782 argc--; 783 argv++; 784 keychainName = *argv; 785 } while (argc > 0); 786 787loser: 788 789 return result; 790} 791