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