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