2 * Copyright (c) 2003 Apple Computer, 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
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
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
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
20 * SecFileVaultCert.cpp - Certificate support for FileVault
23 #include "SecFileVaultCert.h"
24 #include "srCdsaUtils.h"
25 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
28 #include <Security/SecCertificate.h>
29 #include <Security/Certificate.h>
30 #include <Security/SecKeychain.h>
31 #include <Security/KCCursor.h>
32 #include <Security/cfutilities.h>
33 #include <Security/SecRuntime.h>
34 #include <Security/SecCFTypes.h>
36 using namespace KeychainCore
;
38 #pragma mark -------------------- SecFileVaultCert public implementation --------------------
40 SecFileVaultCert::SecFileVaultCert()
44 SecFileVaultCert::~SecFileVaultCert()
48 OSStatus
SecFileVaultCert::createPair(CFStringRef hostName
,CFStringRef userName
,SecKeychainRef keychainRef
, CFDataRef
*cert
)
50 SecCertificateRef certRef
= NULL
;
51 CSSM_DL_DB_HANDLE dlDbHand
= {0, 0};
52 CSSM_CSP_HANDLE cspHand
= 0;
53 CSSM_TP_HANDLE tpHand
= 0;
54 CSSM_CL_HANDLE clHand
= 0;
55 CSSM_KEY_PTR pubKey
= NULL
;
56 CSSM_KEY_PTR privKey
= NULL
;
57 CSSM_DATA certData
= {0, NULL
};
61 CSSM_OID algOid
= SR_CERT_SIGNATURE_ALG_OID
;
63 KeychainCore::Keychain keychain
= KeychainCore::Keychain::optional(keychainRef
);
65 hostStr
= srCfStrToCString(hostName
);
66 userStr
= srCfStrToCString(userName
);
67 if (!hostStr
|| !userStr
) // probably not ASCII capable
68 MacOSError::throwMe(paramErr
);
70 // open keychain, connect to all the CDSA modules we'll need
72 dlDbHand
= keychain
->database()->handle();
73 cspHand
= keychain
->csp()->handle();
75 tpHand
= srTpStartup();
77 MacOSError::throwMe(ioErr
);
79 clHand
= srClStartup();
81 MacOSError::throwMe(ioErr
);
83 // generate key pair, private key stored in keychain
84 ortn
= generateKeyPair(cspHand
, dlDbHand
, SR_KEY_ALGORITHM
, SR_KEY_SIZE_IN_BITS
,
85 "FileVault Master Password Key", &pubKey
, &privKey
);
87 MacOSError::throwMe(ortn
);
90 ortn
= createRootCert(tpHand
,clHand
,cspHand
,pubKey
,privKey
,hostStr
,userStr
,
91 SR_CERT_SIGNATURE_ALGORITHM
,&algOid
,&certData
);
93 MacOSError::throwMe(ortn
);
95 // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData]
97 SecPointer
<Certificate
> certificatePtr(new Certificate(Required(&certData
), CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
));
98 Required(&certRef
) = certificatePtr
->handle();
100 // Add the certificate item to the keychain [see SecCertificateAddToKeychain]
101 KeychainCore::Item
item(Certificate::required(certRef
));
106 // return the cert to caller
107 *cert
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
115 CSSM_ModuleDetach(tpHand
);
117 CSSM_ModuleDetach(clHand
);
120 CSSM_FreeKey(cspHand
,
123 CSSM_FALSE
); // delete
128 CSSM_FreeKey(cspHand
,
131 CSSM_FALSE
); // delete
138 #pragma mark -------------------- SecFileVaultCert private implementation --------------------
140 OSStatus
SecFileVaultCert::createRootCert(
141 CSSM_TP_HANDLE tpHand
,
142 CSSM_CL_HANDLE clHand
,
143 CSSM_CSP_HANDLE cspHand
,
144 CSSM_KEY_PTR subjPubKey
,
145 CSSM_KEY_PTR signerPrivKey
,
146 const char *hostName
, // CSSMOID_CommonName
147 const char *userName
, // CSSMOID_Description
148 CSSM_ALGORITHMS sigAlg
,
149 const CSSM_OID
*sigOid
,
150 CSSM_DATA_PTR certData
) // mallocd and RETURNED
152 CE_DataAndType exts
[2];
153 CE_DataAndType
*extp
= exts
;
155 CSSM_DATA refId
; // mallocd by
156 // CSSM_TP_SubmitCredRequest
157 CSSM_APPLE_TP_CERT_REQUEST certReq
;
158 CSSM_TP_REQUEST_SET reqSet
;
160 CSSM_BOOL confirmRequired
;
161 CSSM_TP_RESULT_SET_PTR resultSet
;
162 CSSM_ENCODED_CERT
*encCert
;
163 CSSM_APPLE_TP_NAME_OID subjectNames
[2];
164 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext
;
169 certReq
.challengeString
= NULL
;
171 /* KeyUsage extension */
172 extp
->type
= DT_KeyUsage
;
173 extp
->critical
= CSSM_FALSE
;
174 extp
->extension
.keyUsage
= CE_KU_DigitalSignature
|
176 CE_KU_KeyEncipherment
|
177 CE_KU_DataEncipherment
;
181 /* BasicConstraints */
182 extp
->type
= DT_BasicConstraints
;
183 extp
->critical
= CSSM_TRUE
;
184 extp
->extension
.basicConstraints
.cA
= CSSM_TRUE
;
185 extp
->extension
.basicConstraints
.pathLenConstraintPresent
= CSSM_FALSE
;
190 subjectNames
[0].string
= hostName
;
191 subjectNames
[0].oid
= &CSSMOID_CommonName
;
192 subjectNames
[1].string
= userName
;
193 subjectNames
[1].oid
= &CSSMOID_Description
;
196 certReq
.cspHand
= cspHand
;
197 certReq
.clHand
= clHand
;
198 randUint32(certReq
.serialNumber
); // random serial number
199 certReq
.numSubjectNames
= 2;
200 certReq
.subjectNames
= subjectNames
;
202 certReq
.numIssuerNames
= 0; // root for now
203 certReq
.issuerNames
= NULL
;
204 certReq
.issuerNameX509
= NULL
;
205 certReq
.certPublicKey
= subjPubKey
;
206 certReq
.issuerPrivateKey
= signerPrivKey
;
207 certReq
.signatureAlg
= sigAlg
;
208 certReq
.signatureOid
= *sigOid
;
209 certReq
.notBefore
= 0;
210 certReq
.notAfter
= 60 * 60 * 24 * 365; // seconds from now, one year
211 certReq
.numExtensions
= numExts
;
212 certReq
.extensions
= exts
;
214 reqSet
.NumberOfRequests
= 1;
215 reqSet
.Requests
= &certReq
;
217 /* a CSSM_TP_CALLERAUTH_CONTEXT to specify an OID */
218 memset(&CallerAuthContext
, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT
));
219 memset(&policyId
, 0, sizeof(CSSM_FIELD
));
220 policyId
.FieldOid
= CSSMOID_APPLE_TP_LOCAL_CERT_GEN
;
222 CallerAuthContext
.Policy
.NumberOfPolicyIds
= 1;
223 CallerAuthContext
.Policy
.PolicyIds
= &policyId
;
225 CSSM_RETURN crtn
= CSSM_TP_SubmitCredRequest(tpHand
,
226 NULL
, // PreferredAuthority
227 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE
,
234 printError("***Error submitting credential request",
235 "CSSM_TP_SubmitCredRequest", crtn
);
238 crtn
= CSSM_TP_RetrieveCredResult(tpHand
,
240 NULL
, // CallerAuthCredentials
245 printError("***Error retreiving credential request",
246 "CSSM_TP_RetrieveCredResult", crtn
);
249 if(resultSet
== NULL
) {
250 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
253 encCert
= (CSSM_ENCODED_CERT
*)resultSet
->Results
;
254 *certData
= encCert
->CertBlob
;
256 /* free resources allocated by TP */
257 APP_FREE(refId
.Data
);
263 /* Convert a reference key to a raw key. */
264 CSSM_RETURN
SecFileVaultCert::refKeyToRaw(
265 CSSM_CSP_HANDLE cspHand
,
266 const CSSM_KEY
*refKey
,
267 CSSM_KEY_PTR rawKey
) // RETURNED
269 CSSM_CC_HANDLE ccHand
;
271 CSSM_ACCESS_CREDENTIALS creds
;
273 memset(rawKey
, 0, sizeof(CSSM_KEY
));
274 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
275 crtn
= CSSM_CSP_CreateSymmetricContext(cspHand
,
278 &creds
, // passPhrase
279 NULL
, // wrapping key
281 CSSM_PADDING_NONE
, // Padding
285 printError("refKeyToRaw: context err",
286 "CSSM_CSP_CreateSymmetricContext", crtn
);
290 crtn
= CSSM_WrapKey(ccHand
,
293 NULL
, // DescriptiveData
295 if(crtn
!= CSSM_OK
) {
296 printError("refKeyToRaw: wrap err", "CSSM_WrapKey", crtn
);
299 CSSM_DeleteContext(ccHand
);
304 * Find private key by label, modify its Label attr to be the
305 * hash of the associated public key.
307 CSSM_RETURN
SecFileVaultCert::setPubKeyHash(
308 CSSM_CSP_HANDLE cspHand
,
309 CSSM_DL_DB_HANDLE dlDbHand
,
310 const CSSM_KEY
*pubOrPrivKey
, // to get hash; raw or ref/CSPDL
311 const char *keyLabel
) // look up by this
314 CSSM_SELECTION_PREDICATE predicate
;
315 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
318 CSSM_HANDLE resultHand
;
320 labelData
.Data
= (uint8
*)keyLabel
;
321 labelData
.Length
= strlen(keyLabel
) + 1; // incl. NULL
322 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
323 query
.Conjunctive
= CSSM_DB_NONE
;
324 query
.NumSelectionPredicates
= 1;
325 predicate
.DbOperator
= CSSM_DB_EQUAL
;
327 predicate
.Attribute
.Info
.AttributeNameFormat
=
328 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
329 predicate
.Attribute
.Info
.Label
.AttributeName
= "Label";
330 predicate
.Attribute
.Info
.AttributeFormat
=
331 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
332 predicate
.Attribute
.Value
= &labelData
;
333 query
.SelectionPredicate
= &predicate
;
335 query
.QueryLimits
.TimeLimit
= 0;
336 query
.QueryLimits
.SizeLimit
= 1;
337 query
.QueryFlags
= 0;
339 /* build Record attribute with one attr */
340 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
341 CSSM_DB_ATTRIBUTE_DATA attr
;
342 attr
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
343 attr
.Info
.Label
.AttributeName
= "Label";
344 attr
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
346 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
347 recordAttrs
.NumberOfAttributes
= 1;
348 recordAttrs
.AttributeData
= &attr
;
350 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
354 NULL
, // hopefully optional ...theData,
356 /* abort only on success */
357 if(crtn
!= CSSM_OK
) {
358 printError("***setPubKeyHash: can't find private key",
359 "CSSM_DL_DataGetFirst", crtn
);
364 * If specified key is a ref key, do NULL unwrap for use with raw CSP.
365 * If the CSPDL and SecurityServer support the key digest passthrough
366 * this is unnecessary.
368 CSSM_KEY rawKeyToDigest
;
369 if(pubOrPrivKey
->KeyHeader
.BlobType
== CSSM_KEYBLOB_REFERENCE
) {
370 crtn
= refKeyToRaw(cspHand
, pubOrPrivKey
, &rawKeyToDigest
);
372 printError("***Error converting public key to raw format",
373 "setPubKeyHash", crtn
);
379 rawKeyToDigest
= *pubOrPrivKey
;
382 /* connect to raw CSP */
383 CSSM_CSP_HANDLE rawCspHand
= srCspStartup(CSSM_TRUE
);
384 if(rawCspHand
== 0) {
385 printf("***Error connecting to raw CSP; aborting.\n");
389 /* calculate hash of pub key from private or public part */
390 CSSM_DATA_PTR keyDigest
= NULL
;
391 CSSM_CC_HANDLE ccHand
;
392 crtn
= CSSM_CSP_CreatePassThroughContext(rawCspHand
,
396 printError("***Error calculating public key hash. Aborting:",
397 "CSSM_CSP_CreatePassThroughContext", crtn
);
400 crtn
= CSSM_CSP_PassThrough(ccHand
,
401 CSSM_APPLECSP_KEYDIGEST
,
403 (void **)&keyDigest
);
405 printError("***Error calculating public key hash. Aborting:",
406 "CSSM_CSP_PassThrough(PUBKEYHASH)", crtn
); // <<<<<<<<<<<<<<<<<<<
409 if(pubOrPrivKey
->KeyHeader
.BlobType
== CSSM_KEYBLOB_REFERENCE
) {
410 /* created in refKeyToRaw().... */
411 CSSM_FreeKey(cspHand
, NULL
, &rawKeyToDigest
, CSSM_FALSE
);
413 CSSM_DeleteContext(ccHand
);
414 CSSM_ModuleDetach(rawCspHand
);
417 * Replace Label attr data with hash.
418 * NOTE: the module which allocated this attribute data - a DL -
419 * was loaded and attached by the Sec layer, not by us. Thus
420 * we can't use the memory allocator functions *we* used when
421 * attaching to the CSPDL - we have to use the ones
422 * which the Sec layer registered with the DL.
424 CSSM_API_MEMORY_FUNCS memFuncs
;
425 crtn
= CSSM_GetAPIMemoryFunctions(dlDbHand
.DLHandle
, &memFuncs
);
427 printError("***Error ", "CSSM_GetAPIMemoryFunctions(DLHandle)",
429 /* oh well, leak and continue */
432 memFuncs
.free_func(attr
.Value
->Data
, memFuncs
.AllocRef
);
433 memFuncs
.free_func(attr
.Value
, memFuncs
.AllocRef
);
435 attr
.Value
= keyDigest
;
437 /* modify key attributes */
438 crtn
= CSSM_DL_DataModify(dlDbHand
,
439 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
442 NULL
, // DataToBeModified
443 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
445 printError("***Error setting public key hash. Aborting",
446 "CSSM_DL_DataModify(PUBKEYHASH)", crtn
);
449 crtn
= CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
451 printError("***Error while stopping query",
452 "CSSM_DL_DataAbortQuery", crtn
);
453 /* let's keep going in this case */
455 crtn
= CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
457 printError("***Error while freeing record",
458 "CSSM_DL_FreeUniqueRecord", crtn
);
459 /* let's keep going in this case */
464 srAppFree(keyDigest
->Data
, NULL
); //***
469 * Generate a key pair using the CSPDL.
471 OSStatus
SecFileVaultCert::generateKeyPair(
472 CSSM_CSP_HANDLE cspHand
,
473 CSSM_DL_DB_HANDLE dlDbHand
,
474 CSSM_ALGORITHMS keyAlg
, // e.g., CSSM_ALGID_RSA
475 uint32 keySizeInBits
,
476 const char *keyLabel
, // C string
477 CSSM_KEY_PTR
*pubKeyPtr
, // mallocd, created, RETURNED
478 CSSM_KEY_PTR
*privKeyPtr
) // mallocd, created, RETURNED
480 CSSM_KEY_PTR pubKey
= reinterpret_cast<CSSM_KEY_PTR
>(
481 APP_MALLOC(sizeof(CSSM_KEY
)));
482 CSSM_KEY_PTR privKey
= reinterpret_cast<CSSM_KEY_PTR
>(
483 APP_MALLOC(sizeof(CSSM_KEY
)));
484 if((pubKey
== NULL
) || (privKey
== NULL
)) {
489 CSSM_KEYUSE pubKeyUse
;
490 CSSM_KEYUSE privKeyUse
;
492 pubKeyUse
= CSSM_KEYUSE_VERIFY
| CSSM_KEYUSE_ENCRYPT
|
494 privKeyUse
= CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_DECRYPT
|
497 crtn
= srCspGenKeyPair(cspHand
,
501 strlen(keyLabel
) + 1,
505 CSSM_KEYATTR_EXTRACTABLE
| CSSM_KEYATTR_RETURN_REF
,
508 CSSM_KEYATTR_SENSITIVE
| CSSM_KEYATTR_RETURN_REF
|
509 CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_EXTRACTABLE
);
517 /* bind private key to cert by public key hash */
518 crtn
= setPubKeyHash(cspHand
,
523 printError("***Error setting public key hash. Continuing at peril",
524 "setPubKeyHash", crtn
);
528 *privKeyPtr
= privKey
;
532 #pragma mark -------------------- utility functions --------------------
534 void SecFileVaultCert::printError(const char *errDescription
,const char *errLocation
,OSStatus crtn
)
536 int len
= 1; // trailing NULL in any case
538 len
+= strlen(errDescription
);
541 len
+= strlen(errLocation
);
543 char *buf
= (char *)malloc(len
);
546 strcpy(buf
, errDescription
);
549 strcat(buf
, errLocation
);
551 cssmPerror(buf
, crtn
);
555 // Fill a uint32 with random data
556 void SecFileVaultCert::randUint32(uint32
&u
)
558 int dev
= open("/dev/random", O_RDONLY
);
562 read(dev
, &u
, sizeof(u
));