]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecSCEP.c
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / sec / Security / SecSCEP.c
1 /*
2 * Copyright (c) 2008-2010,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
24 #include "SecSCEP.h"
25
26 #include <Security/SecCMS.h>
27 #include <Security/SecRandom.h>
28 #include <Security/SecIdentityPriv.h>
29 #include <string.h>
30 #include <AssertMacros.h>
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CommonCrypto/CommonDigestSPI.h>
33 #include <Security/SecItem.h>
34 #include <Security/SecInternal.h>
35 #include <Security/SecCertificateInternal.h>
36 #include <Security/SecKeyPriv.h>
37 #include <Security/SecInternal.h>
38 #include <libDER/DER_Encode.h>
39 #include <uuid/uuid.h>
40 #include <utilities/array_size.h>
41 #include <utilities/debugging.h>
42 #include <utilities/SecIOFormat.h>
43
44 typedef enum {
45 messageType = 2,
46 pkiStatus = 3,
47 failInfo = 4,
48 senderNonce = 5,
49 recipientNonce = 6,
50 transId = 7
51 } scep_attr_t;
52
53 static CF_RETURNS_RETAINED CFDataRef scep_oid(scep_attr_t type)
54 {
55 /* +-------------------+-----------------------------------------------+
56 | Name | ASN.1 Definition |
57 +-------------------+-----------------------------------------------+
58 | id-VeriSign | OBJECT_IDENTIFIER ::= {2 16 US(840) 1 |
59 | | VeriSign(113733)} |
60 | id-pki | OBJECT_IDENTIFIER ::= {id-VeriSign pki(1)} |
61 | id-attributes | OBJECT_IDENTIFIER ::= {id-pki attributes(9)} |
62 | id-messageType | OBJECT_IDENTIFIER ::= {id-attributes |
63 | | messageType(2)} |
64 | id-pkiStatus | OBJECT_IDENTIFIER ::= {id-attributes |
65 | | pkiStatus(3)} |
66 | id-failInfo | OBJECT_IDENTIFIER ::= {id-attributes |
67 | | failInfo(4)} |
68 | id-senderNonce | OBJECT_IDENTIFIER ::= {id-attributes |
69 | | senderNonce(5)} |
70 | id-recipientNonce | OBJECT_IDENTIFIER ::= {id-attributes |
71 | | recipientNonce(6)} |
72 | id-transId | OBJECT_IDENTIFIER ::= {id-attributes |
73 | | transId(7)} |
74 | id-extensionReq | OBJECT_IDENTIFIER ::= {id-attributes |
75 | | extensionReq(8)} |
76 +-------------------+-----------------------------------------------+ */
77 uint8_t oid_scep_attrs[] =
78 { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0 };
79 /* messageType:2 pkiStatus:3 failInfo:4 senderNonce:5 recipientNonce:6 transId:7 */
80 if ((type < messageType) || (type > transId))
81 return NULL;
82
83 oid_scep_attrs[sizeof(oid_scep_attrs) - 1] = type;
84 return CFDataCreate(kCFAllocatorDefault, oid_scep_attrs, sizeof(oid_scep_attrs));
85 }
86
87 static const char CertRep[] = "3";
88 static const char PKCSReq[] = "19";
89 static const char GetCertInitial[] = "20";
90 __unused static const char GetCert[] = "21";
91 __unused static const char GetCRL[] = "22";
92 static const char PKIStatusSUCCESS[] = "0";
93 __unused static const char PKIStatusFAILURE[] = "2";
94 static const char PKIStatusPENDING[] = "3";
95
96 static CF_RETURNS_RETAINED CFDataRef
97 printable_string_data(size_t length, const char *bytes)
98 {
99 DERSize der_length_len = DERLengthOfLength(length);
100 size_t value_length = sizeof(SecASN1PrintableString) + der_length_len + length;
101 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, value_length);
102 CFDataSetLength(data, value_length);
103 uint8_t *ptr = (uint8_t *)CFDataGetBytePtr(data);
104 *ptr++ = SecASN1PrintableString;
105 DEREncodeLength(length, ptr, &der_length_len);
106 ptr += der_length_len;
107 memcpy(ptr, bytes, length);
108 return (CFDataRef)data;
109 }
110
111 #define scep_result(value) printable_string_data(sizeof(value)-1, value)
112
113 static CF_RETURNS_NOT_RETAINED CFTypeRef
114 dictionary_array_value_1(CFDictionaryRef attrs, CFTypeRef attr)
115 {
116 CFTypeRef value = NULL;
117 CFArrayRef attr_values = NULL;
118
119 require(attr_values = (CFArrayRef)CFDictionaryGetValue(attrs, attr), out);
120 require(CFArrayGetCount(attr_values) == 1, out);
121 value = CFArrayGetValueAtIndex(attr_values, 0);
122 out:
123 return value;
124 }
125
126 /* @@@ consider splitting into function returning single value
127 and function creating printable string from c str */
128 static bool scep_attr_has_val(CFDictionaryRef attrs, scep_attr_t attr, const char *val)
129 {
130 bool result = false;
131 CFDataRef msgtype_value_data = printable_string_data(strlen(val), val);
132 CFArrayRef msgtype_value_datas = CFArrayCreate(kCFAllocatorDefault,
133 (const void **)&msgtype_value_data, 1, &kCFTypeArrayCallBacks);
134 CFRelease(msgtype_value_data);
135 CFDataRef msgtype_oid_data = scep_oid(attr);
136 CFArrayRef msgtype_values = (CFArrayRef)CFDictionaryGetValue(attrs, msgtype_oid_data);
137 CFRelease(msgtype_oid_data);
138 if (msgtype_values && CFEqual(msgtype_value_datas, msgtype_values))
139 result = true;
140 CFRelease(msgtype_value_datas);
141
142 return result;
143 }
144
145 static CF_RETURNS_RETAINED CFDataRef hexencode(CFDataRef data)
146 {
147 CFIndex ix, length = CFDataGetLength(data);
148 const uint8_t *bin_data = CFDataGetBytePtr(data);
149 uint8_t *hex_data = calloc(1, 2*length + 1);
150 require(length && bin_data && hex_data, out);
151
152 for (ix = 0; ix < length; ix++)
153 snprintf((char *)&hex_data[2*ix], 3, "%02X", bin_data[ix]);
154
155 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, hex_data,
156 2*length, kCFAllocatorMalloc);
157 out:
158 if (hex_data)
159 free(hex_data);
160 return NULL;
161 }
162
163 static CF_RETURNS_RETAINED CFDataRef pubkeyhash(SecKeyRef key)
164 {
165 CFTypeRef key_type = NULL;
166 CFDictionaryRef pubkey_attrs = NULL;
167 CFDataRef hash_pubkey_data = NULL, pubkey_data = NULL;
168 uint8_t pubkey_hash[CC_SHA1_DIGEST_LENGTH];
169
170 require(pubkey_attrs = SecKeyCopyAttributeDictionary(key), out);
171 require( (key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyClass)) &&
172 CFEqual(key_type, kSecAttrKeyClassPublic), out);
173 require(pubkey_data = CFDictionaryGetValue(pubkey_attrs, kSecValueData), out);
174 require((unsigned long)CFDataGetLength(pubkey_data)<=UINT32_MAX, out); /* Correct as long as CFIndex is long */
175 CCDigest(kCCDigestSHA1, CFDataGetBytePtr(pubkey_data), (CC_LONG)CFDataGetLength(pubkey_data), pubkey_hash);
176 hash_pubkey_data = CFDataCreate(kCFAllocatorDefault, pubkey_hash, sizeof(pubkey_hash));
177 out:
178 CFReleaseSafe(pubkey_attrs);
179 return hash_pubkey_data;
180 }
181
182 static int generate_sender_nonce(CFMutableDictionaryRef dict)
183 {
184 /* random sender nonce, to be verified against recipient nonce in reply */
185 CFDataRef senderNonce_oid_data = scep_oid(senderNonce);
186 uint8_t senderNonce_value[18] = { 4, 16, };
187 int status = SecRandomCopyBytes(kSecRandomDefault, sizeof(senderNonce_value) - 2, senderNonce_value + 2);
188 CFDataRef senderNonce_value_data = CFDataCreate(kCFAllocatorDefault,
189 senderNonce_value, sizeof(senderNonce_value));
190 if (senderNonce_oid_data && senderNonce_value_data)
191 CFDictionarySetValue(dict, senderNonce_oid_data, senderNonce_value_data);
192 CFReleaseNull(senderNonce_oid_data);
193 CFReleaseNull(senderNonce_value_data);
194 return status;
195 }
196
197 SecIdentityRef SecSCEPCreateTemporaryIdentity(SecKeyRef publicKey, SecKeyRef privateKey)
198 {
199 int key_usage = kSecKeyUsageDigitalSignature | kSecKeyUsageKeyEncipherment;
200 CFDictionaryRef self_signed_parameters = NULL;
201 CFNumberRef key_usage_num = NULL;
202 SecCertificateRef self_signed_certificate = NULL;
203 SecIdentityRef self_signed_identity = NULL;
204 CFStringRef cn_uuid = NULL;
205 CFArrayRef cn_dn = NULL, cn_dns = NULL, unique_rdns = NULL;
206
207 key_usage_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage);
208 require(key_usage_num, out);
209
210 const void *key[] = { kSecCertificateKeyUsage };
211 const void *val[] = { key_usage_num };
212 self_signed_parameters = CFDictionaryCreate(kCFAllocatorDefault,
213 key, val, array_size(key),
214 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
215 require(self_signed_parameters, out);
216
217 char uuid_string[37] = {};
218 uuid_t uuid;
219 uuid_generate_random(uuid);
220 uuid_unparse(uuid, uuid_string);
221 cn_uuid = CFStringCreateWithCString(kCFAllocatorDefault, uuid_string, kCFStringEncodingASCII);
222 require(cn_uuid, out);
223 const void * cn[] = { kSecOidCommonName, cn_uuid };
224 cn_dn = CFArrayCreate(kCFAllocatorDefault, cn, 2, NULL);
225 require(cn_dn, out);
226 cn_dns = CFArrayCreate(kCFAllocatorDefault, (const void **)&cn_dn, 1, NULL);
227 require(cn_dns, out);
228 unique_rdns = CFArrayCreate(kCFAllocatorDefault, (const void **)&cn_dns, 1, NULL);
229 require(unique_rdns, out);
230
231 self_signed_certificate = SecGenerateSelfSignedCertificate(unique_rdns, self_signed_parameters, publicKey, privateKey);
232 require(self_signed_certificate, out);
233 self_signed_identity = SecIdentityCreate(kCFAllocatorDefault, self_signed_certificate, privateKey);
234
235 out:
236 CFReleaseSafe(key_usage_num);
237 CFReleaseSafe(self_signed_parameters);
238 CFReleaseSafe(self_signed_certificate);
239 CFReleaseSafe(unique_rdns);
240 CFReleaseSafe(cn_dns);
241 CFReleaseSafe(cn_dn);
242 CFReleaseSafe(cn_uuid);
243
244 return self_signed_identity;
245 }
246
247 CFDataRef
248 SecSCEPGenerateCertificateRequest(CFArrayRef subject, CFDictionaryRef parameters,
249 SecKeyRef __unused publicKey, SecKeyRef privateKey,
250 SecIdentityRef signer, CFTypeRef recipients)
251 {
252 CFDataRef csr = NULL;
253 CFMutableDataRef enveloped_data = NULL;
254 CFMutableDictionaryRef simple_attr = NULL;
255 SecIdentityRef self_signed_identity = NULL;
256 CFMutableDataRef signed_request = NULL;
257 SecCertificateRef recipient = NULL;
258 CFDataRef msgtype_value_data = NULL;
259 CFDataRef msgtype_oid_data = NULL;
260 SecKeyRef realPublicKey = NULL;
261 SecKeyRef recipientKey = NULL;
262
263 if (CFGetTypeID(recipients) == SecCertificateGetTypeID()) {
264 recipient = (SecCertificateRef)recipients;
265 } else if (CFGetTypeID(recipients) == CFArrayGetTypeID()) {
266 CFIndex recipient_count = CFArrayGetCount(recipients);
267 if (recipient_count > 1) {
268 /* get the encryption cert */
269 recipient = (SecCertificateRef)CFArrayGetValueAtIndex(recipients, 0);
270 } else if (recipient_count == 1) {
271 /* if there is at least one we'll assume it's sign+encrypt */
272 recipient = (SecCertificateRef)CFArrayGetValueAtIndex(recipients, 0);
273 }
274 }
275 require(recipient, out);
276
277 /* We don't support EC recipients for SCEP yet. */
278 #if TARGET_OS_IPHONE
279 recipientKey = SecCertificateCopyPublicKey(recipient);
280 #else
281 recipientKey = SecCertificateCopyPublicKey_ios(recipient);
282 #endif
283 require(SecKeyGetAlgorithmId(recipientKey) == kSecRSAAlgorithmID, out);
284
285 require(realPublicKey = SecKeyCopyPublicKey(privateKey), out);
286 require(csr = SecGenerateCertificateRequest(subject, parameters, realPublicKey, privateKey), out);
287 require(enveloped_data = CFDataCreateMutable(kCFAllocatorDefault, 0), out);
288 require_noerr(SecCMSCreateEnvelopedData(recipient, parameters, csr, enveloped_data), out);
289 CFReleaseNull(csr);
290
291 simple_attr = CFDictionaryCreateMutable(kCFAllocatorDefault, 3,
292 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
293
294 /* generate a transaction id: hex encoded pubkey hash */
295 CFDataRef public_key_hash = pubkeyhash(realPublicKey);
296 CFDataRef public_key_hash_hex = hexencode(public_key_hash);
297 CFReleaseSafe(public_key_hash);
298 CFDataRef transid_oid_data = scep_oid(transId);
299 CFDataRef transid_data = printable_string_data(CFDataGetLength(public_key_hash_hex),
300 (const char *)CFDataGetBytePtr(public_key_hash_hex));
301 CFReleaseSafe(public_key_hash_hex);
302
303 CFDictionarySetValue(simple_attr, transid_oid_data, transid_data);
304 CFReleaseNull(transid_oid_data);
305 CFReleaseNull(transid_data);
306
307 /* message type: PKCSReq (19) */
308 msgtype_value_data = NULL;
309 msgtype_oid_data = NULL;
310 require(msgtype_oid_data = scep_oid(messageType), out);
311 require(msgtype_value_data = printable_string_data(strlen(PKCSReq), PKCSReq), out);
312
313 CFDictionarySetValue(simple_attr, msgtype_oid_data, msgtype_value_data);
314 CFReleaseNull(msgtype_oid_data);
315 CFReleaseNull(msgtype_value_data);
316
317 /* random sender nonce, to be verified against recipient nonce in reply */
318 require(generate_sender_nonce(simple_attr) == errSecSuccess, out);
319
320 /* XXX/cs remove auto-generation once managedconfig is no longer using this */
321 if (signer) {
322 self_signed_identity = signer;
323 CFRetain(self_signed_identity);
324 } else {
325 self_signed_identity = SecSCEPCreateTemporaryIdentity(realPublicKey, privateKey);
326
327 /* Add our temporary cert to the keychain for CMS decryption of
328 the reply. If we happened to have picked an existing UUID
329 we fail. We should pick a different UUID and try again. */
330 require(self_signed_identity, out);
331 CFDictionaryRef identity_add = CFDictionaryCreate(NULL,
332 (const void **)&kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL);
333 require_noerr_action(SecItemAdd(identity_add, NULL), out,
334 CFReleaseSafe(identity_add));
335 CFReleaseSafe(identity_add);
336 }
337 require(self_signed_identity, out);
338
339 signed_request = CFDataCreateMutable(kCFAllocatorDefault, 0);
340 require_noerr_action(SecCMSCreateSignedData(self_signed_identity, enveloped_data,
341 parameters, simple_attr, signed_request), out, CFReleaseNull(signed_request));
342
343
344 out:
345 CFReleaseSafe(simple_attr);
346 CFReleaseSafe(msgtype_oid_data);
347 CFReleaseSafe(msgtype_value_data);
348 CFReleaseSafe(self_signed_identity);
349 CFReleaseSafe(enveloped_data);
350 CFReleaseSafe(csr);
351 CFReleaseNull(realPublicKey);
352 CFReleaseSafe(recipientKey);
353 return signed_request;
354 }
355
356 CFDataRef
357 SecSCEPCertifyRequest(CFDataRef request, SecIdentityRef ca_identity, CFDataRef serialno, bool pend_request) {
358 return SecSCEPCertifyRequestWithAlgorithms(request, ca_identity, serialno, pend_request, NULL, NULL);
359 }
360
361
362 CFDataRef
363 SecSCEPCertifyRequestWithAlgorithms(CFDataRef request, SecIdentityRef ca_identity, CFDataRef serialno, bool pend_request,
364 CFStringRef hashingAlgorithm, CFStringRef encryptionAlgorithm)
365 {
366 CFDictionaryRef simple_attr = NULL;
367 SecCertificateRef ca_certificate = NULL;
368 SecKeyRef ca_public_key = NULL;
369 SecCertificateRef cert = NULL;
370 SecPolicyRef policy = NULL;
371 CFDataRef cert_pkcs7 = NULL;
372 CFMutableDataRef cert_msg = NULL;
373 CFMutableDataRef signed_reply = NULL;
374 SecTrustRef trust = NULL;
375 CFDataRef signed_content = NULL;
376 CFDictionaryRef signed_attributes = NULL;
377 SecCertificateRef signer_cert = NULL;
378 CFDataRef transid_oid_data = NULL, senderNonce_oid_data = NULL, transid_value = NULL;
379 CFDataRef subject = NULL, extensions = NULL, senderNonce_value = NULL;
380 CFStringRef challenge = NULL;
381 SecKeyRef tbsPublicKey = NULL;
382 CFMutableDataRef encrypted_content = NULL;
383 SecCertificateRef recipient = NULL;
384 CFMutableDictionaryRef parameters = NULL;
385
386 require_noerr(SecIdentityCopyCertificate(ca_identity, &ca_certificate), out);
387 #if TARGET_OS_IPHONE
388 ca_public_key = SecCertificateCopyPublicKey(ca_certificate); /*@@@*/
389 #else
390 ca_public_key = SecCertificateCopyPublicKey_ios(ca_certificate);
391 #endif
392
393 /* unwrap outer layer: */
394 policy = SecPolicyCreateBasicX509();
395
396 require_noerr(SecCMSVerifyCopyDataAndAttributes(request, NULL,
397 policy, &trust, &signed_content, &signed_attributes), out);
398 /* remember signer: is signer certified by us, then re-certify, no challenge needed */
399 SecTrustResultType result;
400 require_noerr(SecTrustEvaluate(trust, &result), out);
401 require (signer_cert = SecTrustGetCertificateAtIndex(trust, 0), out);
402 bool recertify = !SecCertificateIsSignedBy(signer_cert, ca_public_key);
403
404 /* msgType should be certreq msg */
405 require(scep_attr_has_val(signed_attributes, messageType, PKCSReq), out);
406
407 /* remember transaction id just for reuse */
408 require(transid_oid_data = scep_oid(transId), out);
409 require(transid_value =
410 dictionary_array_value_1(signed_attributes, transid_oid_data), out);
411
412 /* senderNonce becomes recipientNonce */
413 require(senderNonce_oid_data = scep_oid(senderNonce), out);
414 require(senderNonce_value =
415 dictionary_array_value_1(signed_attributes, senderNonce_oid_data), out);
416
417 /* decrypt the request */
418 encrypted_content = CFDataCreateMutable(kCFAllocatorDefault, 0);
419 require_noerr(SecCMSDecryptEnvelopedData(signed_content, encrypted_content, &recipient), out);
420 require(recipient && CFEqual(ca_certificate, recipient), out);
421
422 /* verify CSR */
423 require(SecVerifyCertificateRequest(encrypted_content, &tbsPublicKey, &challenge, &subject, &extensions), out);
424 CFReleaseNull(encrypted_content);
425
426 /* @@@
427 // alternatively send a pending message
428 // pkistatus {{id-attributes pkiStatus(3)} "FAILURE"}
429 // failInfo {{id-attributes failInfo(4)} "the reason to reject"}
430 */
431
432 /* verify challenge - this would need to be a callout that can determine
433 the challenge appropriate for the subject */
434 if (!recertify)
435 require( challenge && (CFStringGetTypeID() == CFGetTypeID(challenge)) &&
436 CFEqual(CFSTR("magic"), challenge), out);
437
438 require(cert_msg = CFDataCreateMutable(kCFAllocatorDefault, 0), out);
439
440 if (!pend_request) {
441 /* We can't yet support EC recipients for SCEP, so reject now. */
442 require (SecKeyGetAlgorithmId(tbsPublicKey) == kSecRSAAlgorithmID, out);
443
444 /* sign cert */
445 cert = SecIdentitySignCertificateWithAlgorithm(ca_identity, serialno,
446 tbsPublicKey, subject, extensions, hashingAlgorithm);
447
448 /* degenerate cms with cert */
449 require (cert_pkcs7 = SecCMSCreateCertificatesOnlyMessage(cert), out);
450 CFReleaseNull(cert);
451
452 /* envelope for client */
453 CFDictionaryRef encryption_params = NULL;
454 if (encryptionAlgorithm) {
455 encryption_params = CFDictionaryCreate(NULL, (const void **)&kSecCMSBulkEncryptionAlgorithm,
456 (const void **)&encryptionAlgorithm, 1,
457 &kCFTypeDictionaryKeyCallBacks,
458 &kCFTypeDictionaryValueCallBacks);
459 }
460 require_noerr(SecCMSCreateEnvelopedData(signer_cert, encryption_params, cert_pkcs7, cert_msg), out);
461 CFReleaseNull(cert_pkcs7);
462 CFReleaseNull(encryption_params);
463 }
464
465 CFDataRef pki_status_oid = scep_oid(pkiStatus);
466 CFDataRef pki_status_value = pend_request ? scep_result(PKIStatusPENDING) : scep_result(PKIStatusSUCCESS);
467 CFDataRef message_type_oid = scep_oid(messageType), message_type_value = scep_result(CertRep);
468 const void *oid[] = { transid_oid_data, pki_status_oid, message_type_oid };
469 const void *value[] = { transid_value, pki_status_value, message_type_value };
470 simple_attr = CFDictionaryCreate(kCFAllocatorDefault, oid, value, array_size(oid),
471 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
472 CFReleaseSafe(pki_status_oid); CFReleaseSafe(pki_status_value);
473 CFReleaseSafe(message_type_oid); CFReleaseSafe(message_type_value);
474
475 /* sign with ra/ca cert and add attributes */
476 signed_reply = CFDataCreateMutable(kCFAllocatorDefault, 0);
477
478 parameters = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
479 CFDictionaryAddValue(parameters, kSecCMSCertChainMode, kSecCMSCertChainModeNone);
480 if (hashingAlgorithm) {
481 CFDictionaryAddValue(parameters, kSecCMSSignHashAlgorithm, hashingAlgorithm);
482 }
483 require_noerr_action(SecCMSCreateSignedData(ca_identity, cert_msg, parameters, simple_attr, signed_reply), out, CFReleaseNull(signed_reply));
484
485 out:
486 CFReleaseSafe(ca_certificate);
487 CFReleaseSafe(ca_public_key);
488 CFReleaseSafe(cert);
489 CFReleaseSafe(cert_pkcs7);
490 CFReleaseSafe(cert_msg);
491 CFReleaseSafe(trust);
492 CFReleaseSafe(policy);
493 CFReleaseSafe(signed_content);
494 CFReleaseSafe(signed_attributes);
495 CFReleaseSafe(transid_oid_data);
496 CFReleaseSafe(senderNonce_oid_data);
497 CFReleaseSafe(subject);
498 CFReleaseSafe(extensions);
499 CFReleaseSafe(challenge);
500 CFReleaseSafe(tbsPublicKey);
501 CFReleaseSafe(encrypted_content);
502 CFReleaseSafe(simple_attr);
503 CFReleaseSafe(recipient);
504 CFReleaseSafe(parameters);
505
506 return signed_reply;
507 }
508
509 static CFStringRef
510 copy_signed_attr_printable_string_value(CFDictionaryRef signed_attributes, scep_attr_t attr)
511 {
512 CFStringRef printable_string = NULL;
513 CFDataRef key_oid = NULL;
514
515 key_oid = scep_oid(attr);
516 require(key_oid, out);
517
518 CFArrayRef values = (CFArrayRef)CFDictionaryGetValue(signed_attributes, key_oid);
519 require_quiet(values && (CFGetTypeID(values) == CFArrayGetTypeID())
520 && (CFArrayGetCount(values) == 1), out);
521 CFDataRef value = CFArrayGetValueAtIndex(values, 0);
522 const uint8_t *bytes = CFDataGetBytePtr(value);
523 size_t length = CFDataGetLength(value);
524 require(length >= 2, out);
525 require(bytes[0] == 0x13, out);
526 /* no scep responses defined that are longer */
527 require(!(bytes[1] & 0x80) && (bytes[1] == length-2), out);
528 printable_string = CFStringCreateWithBytes(kCFAllocatorDefault,
529 bytes + 2, length - 2, kCFStringEncodingASCII, false);
530 out:
531 CFReleaseSafe(key_oid);
532
533 return printable_string;
534 }
535
536 CFArrayRef
537 SecSCEPVerifyReply(CFDataRef request, CFDataRef reply, CFTypeRef ca_certificates,
538 CFErrorRef *server_error)
539 {
540 SecKeyRef ca_public_key = NULL;
541 SecCertificateRef cert = NULL;
542 SecPolicyRef policy = NULL;
543 CFDataRef cert_msg = NULL;
544 CFMutableDataRef enc_cert_msg = NULL;
545 SecTrustRef trust = NULL;
546 CFDataRef signed_content = NULL;
547 CFDictionaryRef signed_attributes = NULL;
548 CFDictionaryRef attributes = NULL;
549 SecCertificateRef signer_cert = NULL;
550
551 CFMutableDataRef encrypted_content = NULL;
552 SecCertificateRef recipient = NULL;
553 CFArrayRef certificates = NULL;
554
555 SecCertificateRef reply_signer = NULL;
556
557 CFStringRef msg_type = NULL;
558 CFStringRef pki_status = NULL;
559
560 if (CFGetTypeID(ca_certificates) == SecCertificateGetTypeID()) {
561 reply_signer = (SecCertificateRef)ca_certificates;
562 } else if (CFGetTypeID(ca_certificates) == CFArrayGetTypeID()) {
563 CFIndex reply_signer_count = CFArrayGetCount(ca_certificates);
564 if (reply_signer_count > 1) {
565 /* get the signer cert */
566 reply_signer = (SecCertificateRef)CFArrayGetValueAtIndex(ca_certificates, 1);
567 } else if (reply_signer_count == 1) {
568 /* if there is at least one we'll assume it's sign+encrypt */
569 reply_signer = (SecCertificateRef)CFArrayGetValueAtIndex(ca_certificates, 0);
570 }
571 }
572 require(reply_signer, out);
573
574 /* unwrap outer layer */
575 policy = SecPolicyCreateBasicX509();
576 CFArrayRef additional_certificates = CFArrayCreate(kCFAllocatorDefault, (const void **)&reply_signer, 1, &kCFTypeArrayCallBacks);
577 require_noerr(SecCMSVerifySignedData(reply, NULL,
578 policy, &trust, additional_certificates, &signed_content, &attributes), out);
579 CFReleaseSafe(additional_certificates);
580 if (attributes)
581 signed_attributes = CFDictionaryGetValue(attributes, kSecCMSSignedAttributes);
582
583 /* response should be signed by ra */
584 SecTrustResultType result;
585 require_noerr(SecTrustEvaluate(trust, &result), out);
586 require(signer_cert = SecTrustGetCertificateAtIndex(trust, 0), out);
587 require(CFEqual(reply_signer, signer_cert), out);
588
589 /* msgType should be certreq msg */
590 require(signed_attributes, out);
591 msg_type = copy_signed_attr_printable_string_value(signed_attributes, messageType);
592 pki_status = copy_signed_attr_printable_string_value(signed_attributes, pkiStatus);
593
594 if (msg_type || pki_status) {
595 require(msg_type && CFEqual(msg_type, CFSTR("3")), out);
596
597 require(pki_status, out);
598 if (CFEqual(pki_status, CFSTR("2"))) {
599 goto out; // FAILURE, the end (return NULL)
600 } else if (CFEqual(pki_status, CFSTR("3"))) {
601 CFDataRef transid_oid_data = NULL, transid_value = NULL;
602 CFDictionaryRef err_dict = NULL;
603 require(transid_oid_data = scep_oid(transId), inner_out);
604 require(transid_value = dictionary_array_value_1(signed_attributes, transid_oid_data), inner_out);
605 err_dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&transid_oid_data, (const void **)&transid_value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
606 if (server_error)
607 *server_error = CFErrorCreate(kCFAllocatorDefault, CFSTR("PENDING"), 3, err_dict);
608 inner_out:
609 CFReleaseSafe(err_dict);
610 CFReleaseSafe(transid_oid_data);
611 goto out;
612 }
613 require(CFEqual(pki_status, CFSTR("0")), out);
614 }
615
616 // can we decode the request?
617 encrypted_content = CFDataCreateMutable(kCFAllocatorDefault, 0);
618 require_noerr(SecCMSDecryptEnvelopedData(signed_content, encrypted_content, &recipient), out);
619 require(recipient, out);
620 // verify recipient belongs with our private key
621
622 // verify CSR:
623 require(certificates = SecCMSCertificatesOnlyMessageCopyCertificates(encrypted_content), out);
624
625 // recipient is either our temporary self-signed cert or the old cert we just used
626 // to recertify. if we have new certificates and have stored them successfully we
627 // can now get rid of the cert.
628 /* XXX/cs
629 This should move outside of thise function when we force a signer
630 to be passed in */
631 CFDictionaryRef cert_delete = CFDictionaryCreate(NULL,
632 (const void **)&kSecValueRef, (const void **)&recipient, 1, NULL, NULL);
633 require_noerr_action(SecItemDelete(cert_delete), out,
634 CFReleaseSafe(cert_delete));
635 CFReleaseSafe(cert_delete);
636
637 out:
638 CFReleaseSafe(ca_public_key);
639 CFReleaseSafe(cert);
640 CFReleaseSafe(cert_msg);
641 CFReleaseSafe(enc_cert_msg);
642 CFReleaseSafe(trust);
643 CFReleaseSafe(policy);
644 CFReleaseSafe(signed_content);
645 CFReleaseSafe(encrypted_content);
646 CFReleaseSafe(recipient);
647 CFReleaseSafe(msg_type);
648 CFReleaseSafe(pki_status);
649 CFReleaseSafe(attributes);
650
651 return certificates;
652 }
653
654 OSStatus SecSCEPValidateCACertMessage(CFArrayRef certs,
655 CFDataRef ca_fingerprint,
656 SecCertificateRef *ca_certificate,
657 SecCertificateRef *ra_signing_certificate,
658 SecCertificateRef *ra_encryption_certificate)
659 {
660 OSStatus status = errSecParam;
661 SecCertificateRef _ca_certificate = NULL, _ra_signing_certificate = NULL,
662 _ra_encryption_certificate = NULL, _ra_certificate = NULL;
663 CFDataRef ca_cert_data = NULL;
664 CFDataRef ca_hash_cfdata = NULL;
665 CFIndex j, count = CFArrayGetCount(certs);
666 CFMutableArrayRef chain = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
667 SecPolicyRef policy = SecPolicyCreateBasicX509();
668 SecTrustRef trust = NULL;
669 require(chain, out);
670 for (j=0; j<count; j++) {
671 const void *candidate_leaf = CFArrayGetValueAtIndex(certs, j);
672 CFArrayRemoveAllValues(chain);
673 CFArraySetValueAtIndex(chain, 0, candidate_leaf);
674 CFArrayAppendArray(chain, certs, CFRangeMake(0, count));
675 CFArrayRemoveValueAtIndex(chain, 1 + j);
676 require_noerr(SecTrustCreateWithCertificates(chain,
677 policy, &trust), out);
678 SecTrustResultType trust_result;
679 SecTrustEvaluate(trust, &trust_result);
680 CFIndex chain_count = SecTrustGetCertificateCount(trust);
681 secdebug("scep", "candidate leaf: %@ forms chain of length %" PRIdCFIndex, candidate_leaf, chain_count);
682 if (chain_count > 1) {
683 SecCertificateRef leaf = SecTrustGetCertificateAtIndex(trust, 0);
684 SecCertificateRef ca_leaf = SecTrustGetCertificateAtIndex(trust, chain_count - 1);
685 if (!_ca_certificate) {
686 if (ca_fingerprint) {
687 secdebug("scep", "checking ca %@ against fingerprint %@", ca_leaf, ca_fingerprint);
688 uint8_t ca_hash[CC_SHA1_DIGEST_LENGTH]; /*max(md5,sha-1)*/
689 ca_cert_data = SecCertificateCopyData(ca_leaf);
690 require(ca_cert_data, out);
691 size_t ca_data_len = CFDataGetLength(ca_cert_data);
692 size_t ca_fingerprint_len = CFDataGetLength(ca_fingerprint);
693 const uint8_t *ca_data = CFDataGetBytePtr(ca_cert_data);
694 require(ca_data_len && ca_data, out);
695 require(ca_data_len<UINT32_MAX, out);
696 switch (ca_fingerprint_len) {
697 case CC_MD5_DIGEST_LENGTH:
698 CC_MD5(ca_data, (CC_LONG)ca_data_len, ca_hash);
699 break;
700
701 case CC_SHA1_DIGEST_LENGTH:
702 CCDigest(kCCDigestSHA1, ca_data, (CC_LONG)ca_data_len, ca_hash);
703 break;
704
705 default:
706 goto out;
707 }
708 CFReleaseNull(ca_cert_data);
709 ca_hash_cfdata = CFDataCreate(kCFAllocatorDefault, ca_hash, ca_fingerprint_len);
710 require(ca_hash_cfdata, out);
711 require(CFEqual(ca_fingerprint, ca_hash_cfdata), out);
712 CFReleaseNull(ca_hash_cfdata);
713 }
714 _ca_certificate = ca_leaf;
715 CFRetain(ca_leaf);
716 } else {
717 // if ca_certificate is already set, this should be the same
718 require(CFEqual(_ca_certificate, ca_leaf), out);
719 }
720
721 // is leaf allowed to sign and/or encrypt?
722 SecKeyUsage key_usage = SecCertificateGetKeyUsage(leaf);
723 bool can_sign = (key_usage & kSecKeyUsageDigitalSignature);
724 bool can_enc = (key_usage & kSecKeyUsageKeyEncipherment);
725 if (!_ra_certificate && can_sign && can_enc) {
726 _ra_certificate = leaf;
727 CFRetain(leaf);
728 }
729 else if (!_ra_encryption_certificate && !can_sign && can_enc) {
730 _ra_encryption_certificate = leaf;
731 CFRetain(leaf);
732 }
733 else if (!_ra_signing_certificate && !can_enc && can_sign) {
734 _ra_signing_certificate = leaf;
735 CFRetain(leaf);
736 }
737 }
738 if (trust) { CFRelease(trust); trust = NULL; }
739 }
740
741 // we should have both a ca certificate and at least one ra certificate now
742 require(_ca_certificate, out);
743 require(_ra_certificate ||
744 (_ra_signing_certificate && _ra_encryption_certificate), out);
745
746 if (ca_certificate) {
747 *ca_certificate = _ca_certificate;
748 _ca_certificate = NULL;
749 }
750 if (_ra_signing_certificate && _ra_encryption_certificate) {
751 if (ra_signing_certificate) {
752 *ra_signing_certificate = _ra_signing_certificate;
753 _ra_signing_certificate = NULL;
754 }
755 if (ra_encryption_certificate) {
756 *ra_encryption_certificate = _ra_encryption_certificate;
757 _ra_encryption_certificate = NULL;
758 }
759 } else if (_ra_certificate) {
760 if (ra_signing_certificate) {
761 *ra_signing_certificate = _ra_certificate;
762 _ra_certificate = NULL;
763 }
764 }
765
766 status = errSecSuccess;
767
768 out:
769 CFReleaseSafe(_ra_encryption_certificate);
770 CFReleaseSafe(_ra_signing_certificate);
771 CFReleaseSafe(_ra_certificate);
772 CFReleaseSafe(_ca_certificate);
773 CFReleaseSafe(ca_cert_data);
774 CFReleaseSafe(ca_hash_cfdata);
775 CFReleaseSafe(policy);
776 CFReleaseSafe(trust);
777 CFReleaseSafe(chain);
778 return status;
779
780 }
781
782
783 /*!
784 @function SecSCEPGetCertInitial
785 @abstract generate a scep cert initial request, to be presented to
786 a scep server, in case the first request timed out
787 */
788
789 // XXX/cs pass CA/RA certificates as a CFTypeRef: one or more certificates for ca_certificate and recipient
790
791 CF_RETURNS_RETAINED CFDataRef
792 SecSCEPGetCertInitial(SecCertificateRef ca_certificate, CFArrayRef subject, CFDictionaryRef parameters,
793 CFDictionaryRef signed_attrs, SecIdentityRef signer, CFTypeRef recipient)
794 {
795 CFMutableDataRef signed_request = NULL;
796 CFMutableDictionaryRef simple_attr = NULL;
797 CFDataRef pki_message_contents = NULL;
798 CFMutableDataRef enveloped_data = NULL;
799 CFDataRef msgtype_value_data = NULL;
800 CFDataRef msgtype_oid_data = NULL;
801
802 require(signed_attrs, out);
803 require(pki_message_contents = SecGenerateCertificateRequestSubject(ca_certificate, subject), out);
804 require(enveloped_data = CFDataCreateMutable(kCFAllocatorDefault, 0), out);
805 require_noerr(SecCMSCreateEnvelopedData(recipient, parameters, pki_message_contents, enveloped_data), out);
806
807 /* remember transaction id just for reuse */
808 simple_attr = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 3, signed_attrs);
809
810 /* message type: GetCertInitial (20) */
811 require(msgtype_oid_data = scep_oid(messageType), out);
812 require(msgtype_value_data = printable_string_data(sizeof(GetCertInitial) - 1, GetCertInitial), out);
813 CFDictionarySetValue(simple_attr, msgtype_oid_data, msgtype_value_data);
814 CFReleaseNull(msgtype_oid_data);
815 CFReleaseNull(msgtype_value_data);
816
817 /* random sender nonce, to be verified against recipient nonce in reply */
818 generate_sender_nonce(simple_attr);
819 signed_request = CFDataCreateMutable(kCFAllocatorDefault, 0);
820 require_noerr_action(SecCMSCreateSignedData(signer, enveloped_data,
821 parameters, simple_attr, signed_request), out, CFReleaseNull(signed_request));
822
823 out:
824 CFReleaseSafe(simple_attr);
825 CFReleaseSafe(pki_message_contents);
826 CFReleaseSafe(enveloped_data);
827 CFReleaseSafe(msgtype_oid_data);
828 CFReleaseSafe(msgtype_value_data);
829 return signed_request;
830 }
831
832
833 /*
834 +----------------+-----------------+---------------------------+
835 | Attribute | Encoding | Comment |
836 +----------------+-----------------+---------------------------+
837 | transactionID | PrintableString | Decimal value as a string |
838 | messageType | PrintableString | Decimal value as a string |
839 | pkiStatus | PrintableString | Decimal value as a string |
840 | failInfo | PrintableString | Decimal value as a string |
841 | senderNonce | OctetString | |
842 | recipientNonce | OctetString | |
843 +----------------+-----------------+---------------------------+
844
845 4.2.1. transactionID
846
847 The transactionID is an attribute which uniquely identifies a
848 transaction. This attribute is required in all PKI messages.
849
850 Because the enrollment transaction could be interrupted by various
851 errors, including network connection errors or client reboot, the
852 SCEP client generates a transaction identifier by calculating a hash
853 on the public key value for which the enrollment is requested. This
854 retains the same transaction identifier throughout the enrollment
855 transaction, even if the client has rebooted or timed out, and issues
856 a new enrollment request for the same key pair.
857
858 It also provides the way for the CA to uniquely identify a
859 transaction in its database. At the requester side, it generates a
860 transaction identifier which is included in PKCSReq. If the CA
861 returns a response of PENDING, the requester will poll by
862 periodically sending out GetCertInitial with the same transaction
863 identifier until either a response other than PENDING is obtained, or
864 the configured maximum time has elapsed.
865
866 For non-enrollment message (for example GetCert and GetCRL), the
867 transactionID should be a number unique to the client.
868
869
870 4.2.2. messageType
871
872 The messageType attribute specify the type of operation performed by
873 the transaction. This attribute is required in all PKI messages.
874 Currently, the following message types are defined:
875
876 o PKCSReq (19) -- PKCS#10 [RFC2986] certificate request
877
878 o CertRep (3) -- Response to certificate or CRL request
879
880 o GetCertInitial (20) -- Certificate polling in manual enrollment
881
882 o GetCert (21) -- Retrieve a certificate
883
884 o GetCRL (22) -- Retrieve a CRL
885
886 4.2.3. pkiStatus
887
888 All response message will include transaction status information
889 which is defined as pkiStatus attribute:
890
891 o SUCCESS (0) -- request granted
892
893 o FAILURE (2) -- request rejected. This also requires a failInfo
894 attribute to be present, as defined in section 4.2.4.
895
896 o PENDING (3) -- request pending for manual approval
897
898
899 4.2.4. failInfo
900
901 The failInfo attribute will contain one of the following failure
902 reasons:
903
904 o badAlg (0) -- Unrecognized or unsupported algorithm ident
905
906 o badMessageCheck (1) -- integrity check failed
907
908 o badRequest (2) -- transaction not permitted or supported
909
910 o badTime (3) -- Message time field was not sufficiently close to
911 the system time
912
913 o badCertId (4) -- No certificate could be identified matching the
914 provided criteria
915
916 4.2.5. senderNonce and responderNonce
917
918 The attributes of senderNonce and recipientNonce are the 16 byte
919 random numbers generated for each transaction to prevent the replay
920 attack.
921
922 When a requester sends a PKI message to the server, a senderNonce is
923 included in the message. After the server processes the request, it
924 will send back the requester senderNonce as the recipientNonce and
925 generates another nonce as the senderNonce in the response message.
926 Because the proposed PKI protocol is a two-way communication
927 protocol, it is clear that the nonce can only be used by the
928 requester to prevent the replay. The server has to employ extra
929 state related information to prevent a replay attack.
930
931 */