--- /dev/null
+/*
+ * Copyright (c) 2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * createFVMaster.c
+ */
+
+#include "createFVMaster.h"
+
+#include "readline_cssm.h"
+#include "security_tool.h"
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <Security/SecKeychain.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecKeychain.h>
+#include <Security/oidsalg.h>
+#include <Security/oidsattr.h>
+#include <limits.h>
+#include <utilities/SecCFRelease.h>
+
+#include "srCdsaUtils.h"
+
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+const char * const _masterKeychainName = "FileVaultMaster.keychain";
+const char * const _masterKeychainPath = "./FileVaultMaster";
+
+/*
+ * Parameters used to create key pairs and certificates in
+ * SR_CertificateAndKeyCreate().
+ */
+#define SR_KEY_ALGORITHM CSSM_ALGID_RSA
+#define SR_KEY_SIZE_IN_BITS 1024
+
+#define SR2_KEY_SIZE_IN_BITS 2048 // Recommended size for FileVault2 (FDE)
+
+/*
+ * The CSSM_ALGORITHMS and OID values defining the signature
+ * algorithm in the generated certificate.
+ */
+#define SR_CERT_SIGNATURE_ALGORITHM CSSM_ALGID_SHA256WithRSA
+#define SR_CERT_SIGNATURE_ALG_OID CSSMOID_SHA256WithRSA
+
+OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef);
+
+OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert);
+OSStatus generateKeyPair(CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, CSSM_ALGORITHMS keyAlg,
+ uint32 keySizeInBits, const char *keyLabel, CSSM_KEY_PTR *pubKeyPtr, CSSM_KEY_PTR *privKeyPtr);
+OSStatus createRootCert(CSSM_TP_HANDLE tpHand, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR subjPubKey, CSSM_KEY_PTR signerPrivKey, const char *hostName, const char *userName,
+ CSSM_ALGORITHMS sigAlg, const CSSM_OID *sigOid, CSSM_DATA_PTR certData);
+
+static char *secCopyCString(CFStringRef theString);
+
+OSStatus makeMasterPassword(const char *fvmkcName, const char *masterPasswordPassword, uint32 keySizeInBits, SecKeychainRef *keychainRef)
+{
+ /*
+ OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword);
+
+ *** In the real code, this will be done directly rather than exec'ing a tool, since there are too many parameters to specify
+ *** this needs to be done as root, since the keychain will be a system keychain
+ /usr/bin/certtool y c k=/Library/Keychains/FileVaultMaster.keychain p=<masterPasswordPassword>
+ /usr/bin/certtool c k=/Library/Keychains/FileVaultMaster.keychain o=/Library/Keychains/FileVaultMaster.cer
+ Two steps: create the keychain, then create the keypair
+ */
+
+ SecAccessRef initialAccess = NULL;
+
+ if (!masterPasswordPassword)
+ {
+ sec_error("You must supply a non-empty password");
+ return -2;
+ }
+
+ // We return an error if the keychain already exists
+ OSStatus status = SecKeychainCreate(fvmkcName, (UInt32) strlen(masterPasswordPassword), masterPasswordPassword, false, initialAccess, keychainRef);
+ if (status!=noErr)
+ {
+ if (status==errSecDuplicateKeychain || status==CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
+ sec_error("The keychain file %s already exists", fvmkcName);
+ return status;
+ }
+
+ // Create the key pair
+ char host[PATH_MAX];
+ int rx = gethostname(host, sizeof(host));
+ if (rx)
+ strcpy(host, "localhost");
+
+ CFStringRef hostName = CFSTR("FileVault Recovery Key"); // This is what shows up in Keychain Access display
+ CFStringRef userName = CFStringCreateWithCString(kCFAllocatorDefault, host, kCFStringEncodingUTF8);
+ CFDataRef certData = NULL;
+ printf("Generating a %d bit key pair; this may take several minutes\n", keySizeInBits);
+ status = createPair(hostName,userName,*keychainRef,keySizeInBits, &certData);
+ CFReleaseNull(userName);
+ if (status)
+ sec_error("Error in createPair: %s", sec_errstr(status));
+ if (certData)
+ CFRelease(certData);
+
+ return status;
+}
+
+OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert)
+{
+ SecCertificateRef certRef = NULL;
+ CSSM_DL_DB_HANDLE dlDbHand = {0, 0};
+ CSSM_CSP_HANDLE cspHand = 0;
+ CSSM_TP_HANDLE tpHand = 0;
+ CSSM_CL_HANDLE clHand = 0;
+ CSSM_KEY_PTR pubKey = NULL;
+ CSSM_KEY_PTR privKey = NULL;
+ CSSM_DATA certData = {0, NULL};
+ char *hostStr = NULL;
+ char *userStr = NULL;
+ OSStatus ortn;
+ CSSM_OID algOid = SR_CERT_SIGNATURE_ALG_OID;
+
+ hostStr = secCopyCString(hostName);
+ userStr = secCopyCString(userName);
+ if (!hostStr || !userStr) // could not convert to UTF-8
+ {
+ ortn = paramErr;
+ goto xit;
+ }
+
+ // open keychain, connect to all the CDSA modules we'll need
+
+ ortn = SecKeychainGetCSPHandle(keychainRef, &cspHand);
+ if (ortn)
+ goto xit;
+
+ ortn = SecKeychainGetDLDBHandle(keychainRef, &dlDbHand);
+ if (ortn)
+ goto xit;
+
+ tpHand = srTpStartup();
+ if (tpHand == 0)
+ {
+ ortn = ioErr;
+ goto xit;
+ }
+
+ clHand = srClStartup();
+ if (clHand == 0)
+ {
+ ortn = ioErr;
+ goto xit;
+ }
+
+ // generate key pair, private key stored in keychain
+ ortn = generateKeyPair(cspHand, dlDbHand, SR_KEY_ALGORITHM, keySizeInBits,
+ "FileVault Master Password Key", &pubKey, &privKey);
+ if (ortn)
+ goto xit;
+
+ // generate the cert
+ ortn = createRootCert(tpHand,clHand,cspHand,pubKey,privKey,hostStr,userStr,
+ SR_CERT_SIGNATURE_ALGORITHM,&algOid,&certData);
+ if (ortn)
+ goto xit;
+
+ // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData]
+ ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef);
+ if (ortn)
+ goto xit;
+
+ ortn = SecCertificateAddToKeychain(certRef, keychainRef);
+ if (ortn)
+ goto xit;
+
+ // return the cert to caller
+ *cert = CFDataCreate(NULL, certData.Data, certData.Length);
+
+ // cleanup
+xit:
+ if (certRef)
+ CFRelease(certRef);
+ if (certData.Data)
+ free(certData.Data);
+ if (hostStr)
+ free(hostStr);
+ if (userStr)
+ free(userStr);
+ if (tpHand)
+ CSSM_ModuleDetach(tpHand);
+ if (clHand)
+ CSSM_ModuleDetach(clHand);
+ if (pubKey)
+ {
+ CSSM_FreeKey(cspHand,
+ NULL, // access cred
+ pubKey,
+ CSSM_FALSE); // delete
+ APP_FREE(pubKey);
+ }
+ if (privKey)
+ {
+ CSSM_FreeKey(cspHand,
+ NULL, // access cred
+ privKey,
+ CSSM_FALSE); // delete
+ APP_FREE(privKey);
+ }
+
+ return ortn;
+}
+
+/*
+* Given a CFStringRef, this function allocates and returns a pointer
+* to a null-terminated 'C' string copy. If conversion of the string
+* to UTF8 fails for some reason, the function will return NULL.
+*
+* The caller must free this pointer
+*/
+
+char *secCopyCString(CFStringRef theString)
+{
+ CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(theString), kCFStringEncodingUTF8) + 1;
+ char* buffer = (char*) malloc(maxLength);
+ Boolean converted = CFStringGetCString(theString, buffer, maxLength, kCFStringEncodingUTF8);
+ if (!converted) {
+ free(buffer);
+ buffer = NULL;
+ }
+ return buffer;
+}
+
+
+#pragma mark -------------------- SecFileVaultCert private implementation --------------------
+
+OSStatus createRootCert(
+ CSSM_TP_HANDLE tpHand,
+ CSSM_CL_HANDLE clHand,
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR subjPubKey,
+ CSSM_KEY_PTR signerPrivKey,
+ const char *hostName, // CSSMOID_CommonName
+ const char *userName, // CSSMOID_Description
+ CSSM_ALGORITHMS sigAlg,
+ const CSSM_OID *sigOid,
+ CSSM_DATA_PTR certData) // mallocd and RETURNED
+{
+ CE_DataAndType exts[2];
+ CE_DataAndType *extp = exts;
+ unsigned numExts;
+ CSSM_DATA refId; // mallocd by
+ // CSSM_TP_SubmitCredRequest
+ CSSM_APPLE_TP_CERT_REQUEST certReq;
+ CSSM_TP_REQUEST_SET reqSet;
+ sint32 estTime;
+ CSSM_BOOL confirmRequired;
+ CSSM_TP_RESULT_SET_PTR resultSet=NULL;
+ CSSM_ENCODED_CERT *encCert=NULL;
+ CSSM_APPLE_TP_NAME_OID subjectNames[2];
+ CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
+ CSSM_FIELD policyId;
+
+ numExts = 0;
+
+ certReq.challengeString = NULL;
+
+ /* KeyUsage extension */
+ extp->type = DT_KeyUsage;
+ extp->critical = CSSM_FALSE;
+ extp->extension.keyUsage = CE_KU_DigitalSignature |
+ CE_KU_KeyCertSign |
+ CE_KU_KeyEncipherment |
+ CE_KU_DataEncipherment;
+ extp++;
+ numExts++;
+
+ /* BasicConstraints */
+ extp->type = DT_BasicConstraints;
+ extp->critical = CSSM_TRUE;
+ extp->extension.basicConstraints.cA = CSSM_FALSE;
+ extp->extension.basicConstraints.pathLenConstraintPresent = CSSM_FALSE;
+ extp++;
+ numExts++;
+
+ /* name array */
+ subjectNames[0].string = hostName;
+ subjectNames[0].oid = &CSSMOID_CommonName;
+ subjectNames[1].string = userName;
+ subjectNames[1].oid = &CSSMOID_Description;
+
+ /* certReq */
+ certReq.cspHand = cspHand;
+ certReq.clHand = clHand;
+ certReq.serialNumber = arc4random();
+ certReq.numSubjectNames = 2;
+ certReq.subjectNames = subjectNames;
+
+ certReq.numIssuerNames = 0; // root for now
+ certReq.issuerNames = NULL;
+ certReq.issuerNameX509 = NULL;
+ certReq.certPublicKey = subjPubKey;
+ certReq.issuerPrivateKey = signerPrivKey;
+ certReq.signatureAlg = sigAlg;
+ certReq.signatureOid = *sigOid;
+ certReq.notBefore = 0;
+ certReq.notAfter = 60 * 60 * 24 * 365; // seconds from now, one year
+ certReq.numExtensions = numExts;
+ certReq.extensions = exts;
+
+ reqSet.NumberOfRequests = 1;
+ reqSet.Requests = &certReq;
+
+ /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
+ memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
+ memset(&policyId, 0, sizeof(CSSM_FIELD));
+ policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
+
+ CallerAuthContext.Policy.NumberOfPolicyIds = 1;
+ CallerAuthContext.Policy.PolicyIds = &policyId;
+
+ CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tpHand,
+ NULL, // PreferredAuthority
+ CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
+ &reqSet,
+ &CallerAuthContext,
+ &estTime,
+ &refId);
+
+ if(crtn) {
+ sec_error("CSSM_TP_SubmitCredRequest: %s", sec_errstr(crtn));
+ goto xit;
+ }
+ crtn = CSSM_TP_RetrieveCredResult(tpHand,
+ &refId,
+ NULL, // CallerAuthCredentials
+ &estTime,
+ &confirmRequired,
+ &resultSet);
+ if(crtn) {
+ sec_error("CSSM_TP_RetrieveCredResult: %s", sec_errstr(crtn));
+ goto xit;
+ }
+ if(resultSet == NULL) {
+ sec_error("CSSM_TP_RetrieveCredResult: returned NULL result set");
+ crtn = ioErr;
+ goto xit;
+ }
+ encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
+ certData->Length = encCert->CertBlob.Length;
+ certData->Data = malloc(encCert->CertBlob.Length);
+ if (certData->Data)
+ memcpy(certData->Data, encCert->CertBlob.Data, encCert->CertBlob.Length);
+ crtn = noErr;
+
+xit:
+ /* free resources allocated by TP */
+ APP_FREE(refId.Data);
+ if (encCert)
+ {
+ if (encCert->CertBlob.Data)
+ {
+ APP_FREE(encCert->CertBlob.Data);
+ }
+ APP_FREE(encCert);
+ }
+ APP_FREE(resultSet);
+ return crtn;
+}
+
+/* Convert a reference key to a raw key. */
+static CSSM_RETURN refKeyToRaw(
+ CSSM_CSP_HANDLE cspHand,
+ const CSSM_KEY *refKey,
+ CSSM_KEY_PTR rawKey) // RETURNED
+{
+ CSSM_CC_HANDLE ccHand;
+ CSSM_RETURN crtn;
+ CSSM_ACCESS_CREDENTIALS creds;
+
+ memset(rawKey, 0, sizeof(CSSM_KEY));
+ memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+ crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
+ CSSM_ALGID_NONE,
+ CSSM_ALGMODE_NONE,
+ &creds, // passPhrase
+ NULL, // wrapping key
+ NULL, // init vector
+ CSSM_PADDING_NONE, // Padding
+ 0, // Params
+ &ccHand);
+ if(crtn) {
+ sec_error("CSSM_CSP_CreateSymmetricContext: refKeyToRaw context err: %s", sec_errstr(crtn));
+ return crtn;
+ }
+
+ crtn = CSSM_WrapKey(ccHand,
+ &creds,
+ refKey,
+ NULL, // DescriptiveData
+ rawKey);
+ if(crtn != CSSM_OK) {
+ sec_error("CSSM_WrapKey: refKeyToRaw wrap err: %s", sec_errstr(crtn));
+ return crtn;
+ }
+ CSSM_DeleteContext(ccHand);
+ return CSSM_OK;
+}
+
+/*
+ * Find private key by label, modify its Label attr to be the
+ * hash of the associated public key.
+ */
+static CSSM_RETURN setPubKeyHash(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_DL_DB_HANDLE dlDbHand,
+ const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL
+ const char *keyLabel) // look up by this
+{
+ CSSM_QUERY query;
+ CSSM_SELECTION_PREDICATE predicate;
+ CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
+ CSSM_RETURN crtn;
+ CSSM_DATA labelData;
+ CSSM_HANDLE resultHand;
+
+ labelData.Data = (uint8 *)keyLabel;
+ labelData.Length = strlen(keyLabel) + 1; // incl. NULL
+ query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+ query.Conjunctive = CSSM_DB_NONE;
+ query.NumSelectionPredicates = 1;
+ predicate.DbOperator = CSSM_DB_EQUAL;
+
+ predicate.Attribute.Info.AttributeNameFormat =
+ CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+ predicate.Attribute.Info.Label.AttributeName = "Label";
+ predicate.Attribute.Info.AttributeFormat =
+ CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+ predicate.Attribute.Value = &labelData;
+ query.SelectionPredicate = &predicate;
+
+ query.QueryLimits.TimeLimit = 0;
+ query.QueryLimits.SizeLimit = 1;
+ query.QueryFlags = 0;
+
+ /* build Record attribute with one attr */
+ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
+ CSSM_DB_ATTRIBUTE_DATA attr;
+ attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+ attr.Info.Label.AttributeName = "Label";
+ attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+
+ recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+ recordAttrs.NumberOfAttributes = 1;
+ recordAttrs.AttributeData = &attr;
+
+ crtn = CSSM_DL_DataGetFirst(dlDbHand,
+ &query,
+ &resultHand,
+ &recordAttrs,
+ NULL, // hopefully optional ...theData,
+ &record);
+ /* abort only on success */
+ if(crtn != CSSM_OK) {
+ sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn));
+ return crtn;
+ }
+
+ /*
+ * If specified key is a ref key, do NULL unwrap for use with raw CSP.
+ * If the CSPDL and SecurityServer support the key digest passthrough
+ * this is unnecessary.
+ */
+ CSSM_KEY rawKeyToDigest;
+ if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
+ crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest);
+ if(crtn) {
+ sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn));
+ return crtn;
+ }
+ }
+ else {
+ /* use as is */
+ rawKeyToDigest = *pubOrPrivKey;
+ }
+
+ /* connect to raw CSP */
+ CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE);
+ if(rawCspHand == 0) {
+ printf("***Error connecting to raw CSP; aborting.\n");
+ return -1;
+ }
+
+ /* calculate hash of pub key from private or public part */
+ CSSM_DATA_PTR keyDigest = NULL;
+ CSSM_CC_HANDLE ccHand;
+ crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand,
+ &rawKeyToDigest,
+ &ccHand);
+ if(ccHand == 0) {
+ sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
+ return -1;
+ }
+ crtn = CSSM_CSP_PassThrough(ccHand,
+ CSSM_APPLECSP_KEYDIGEST,
+ NULL,
+ (void **)&keyDigest);
+ if(crtn) {
+ sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
+ return crtn;
+ }
+ if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
+ /* created in refKeyToRaw().... */
+ CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE);
+ }
+ CSSM_DeleteContext(ccHand);
+ CSSM_ModuleDetach(rawCspHand);
+
+ /*
+ * Replace Label attr data with hash.
+ * NOTE: the module which allocated this attribute data - a DL -
+ * was loaded and attached by the Sec layer, not by us. Thus
+ * we can't use the memory allocator functions *we* used when
+ * attaching to the CSPDL - we have to use the ones
+ * which the Sec layer registered with the DL.
+ */
+ CSSM_API_MEMORY_FUNCS memFuncs;
+ crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
+ if(crtn) {
+ sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn));
+ /* oh well, leak and continue */
+ }
+ else {
+ memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
+ memFuncs.free_func(attr.Value, memFuncs.AllocRef);
+ }
+ attr.Value = keyDigest;
+
+ /* modify key attributes */
+ crtn = CSSM_DL_DataModify(dlDbHand,
+ CSSM_DL_DB_RECORD_PRIVATE_KEY,
+ record,
+ &recordAttrs,
+ NULL, // DataToBeModified
+ CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+ if(crtn) {
+ sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn));
+ return crtn;
+ }
+ crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
+ if(crtn) {
+ sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn));
+ /* let's keep going in this case */
+ }
+ crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
+ if(crtn) {
+ sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn));
+ /* let's keep going in this case */
+ crtn = CSSM_OK;
+ }
+
+ /* free resources */
+ if (keyDigest)
+ {
+ srAppFree(keyDigest->Data, NULL);
+ srAppFree(keyDigest, NULL);
+ }
+ return CSSM_OK;
+}
+
+/*
+ * Generate a key pair using the CSPDL.
+ */
+OSStatus generateKeyPair(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_DL_DB_HANDLE dlDbHand,
+ CSSM_ALGORITHMS keyAlg, // e.g., CSSM_ALGID_RSA
+ uint32 keySizeInBits,
+ const char *keyLabel, // C string
+ CSSM_KEY_PTR *pubKeyPtr, // mallocd, created, RETURNED
+ CSSM_KEY_PTR *privKeyPtr) // mallocd, created, RETURNED
+{
+ CSSM_KEY_PTR pubKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY)));
+ CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)(APP_MALLOC(sizeof(CSSM_KEY)));
+ if((pubKey == NULL) || (privKey == NULL)) {
+ return memFullErr;
+ }
+
+ CSSM_RETURN crtn;
+ CSSM_KEYUSE pubKeyUse;
+ CSSM_KEYUSE privKeyUse;
+
+ pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT |
+ CSSM_KEYUSE_WRAP;
+ privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT |
+ CSSM_KEYUSE_UNWRAP;
+
+ crtn = srCspGenKeyPair(cspHand,
+ &dlDbHand,
+ keyAlg,
+ keyLabel,
+ (int) strlen(keyLabel) + 1,
+ keySizeInBits,
+ pubKey,
+ pubKeyUse,
+ CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF,
+ privKey,
+ privKeyUse,
+ CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF |
+ CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE);
+
+ if(crtn) {
+ APP_FREE(pubKey);
+ APP_FREE(privKey);
+ return paramErr;
+ }
+
+ /* bind private key to cert by public key hash */
+ crtn = setPubKeyHash(cspHand,
+ dlDbHand,
+ pubKey,
+ keyLabel);
+ if(crtn) {
+ sec_error("setPubKeyHash: Error setting public key hash. Continuing at peril: %s", sec_errstr(crtn));
+ }
+
+ *pubKeyPtr = pubKey;
+ *privKeyPtr = privKey;
+ return noErr;
+}
+
+//==========================================================================
+
+int
+keychain_createMFV(int argc, char * const *argv)
+{
+ int zero_password = 0;
+ char *password = NULL;
+ const char *keychainName = NULL;
+ int result = 0, ch = 0;
+ Boolean do_prompt = FALSE;
+ SecKeychainRef keychainRef = NULL;
+ uint32 keySizeInBits = SR2_KEY_SIZE_IN_BITS; // default
+
+/* AG: getopts optstring name [args]
+ AG: while loop calling getopt is used to extract password from cl from user
+ password is the only option to keychain_create
+ optstring is a string containing the legitimate option
+ characters. If such a character is followed by a colon,
+ the option requires an argument, so getopt places a
+ pointer to the following text in the same argv-element, or
+ the text of the following argv-element, in optarg.
+*/
+ while ((ch = getopt(argc, argv, "hp:s:P")) != -1)
+ {
+ switch (ch)
+ {
+ case 'p':
+ password = optarg;
+ break;
+ case 'P':
+ do_prompt = TRUE;
+ break;
+ case 's':
+ // Specify the keysize in bits (default 1024)
+ keySizeInBits = atoi(optarg);
+ if (!(keySizeInBits == SR_KEY_SIZE_IN_BITS || keySizeInBits == SR2_KEY_SIZE_IN_BITS || keySizeInBits == 4096))
+ return SHOW_USAGE_MESSAGE;
+ break;
+ case '?':
+ default:
+ return SHOW_USAGE_MESSAGE;
+ }
+ }
+/*
+ AG: The external variable optind is the index of the next
+ array element of argv[] to be processed; it communicates
+ from one call of getopt() to the next which element to
+ process.
+ 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.
+
+*/
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ return SHOW_USAGE_MESSAGE;
+
+ keychainName = (argc == 1)?*argv:_masterKeychainName;
+ if (!keychainName || *keychainName == '\0')
+ return -1;
+
+ if (!password && !do_prompt)
+ {
+ int compare = 1;
+ int tries;
+
+ for (tries = 3; tries-- > 0;)
+ {
+ char *firstpass;
+
+ password = getpass("password for new keychain: ");
+ if (!password)
+ {
+ result = -1;
+ goto loser;
+ }
+
+ firstpass = malloc(strlen(password) + 1);
+ strcpy(firstpass, password);
+ password = getpass("retype password for new keychain: ");
+ compare = password ? strcmp(password, firstpass) : 1;
+ memset(firstpass, 0, strlen(firstpass));
+ free(firstpass);
+ if (!password)
+ {
+ result = -1;
+ goto loser;
+ }
+
+ if (compare)
+ {
+ fprintf(stderr, "passwords don't match\n");
+ memset(password, 0, strlen(password));
+ }
+ else
+ {
+ zero_password = 1;
+ break;
+ }
+ }
+
+ if (compare)
+ {
+ result = 1;
+ goto loser;
+ }
+ }
+
+ do
+ {
+ // result = do_create(keychain, password, do_prompt);
+ result = makeMasterPassword(keychainName, password, keySizeInBits, &keychainRef);
+ if (keychainRef)
+ CFRelease(keychainRef);
+ if (zero_password)
+ memset(password, 0, strlen(password));
+ if (result)
+ goto loser;
+
+ argc--;
+ argv++;
+ keychainName = *argv;
+ } while (argc > 0);
+
+loser:
+
+ return result;
+}