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