testcert.c   [plain text]


/*
 * Copyright (c) 2009 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 * testcert.c
 */

#include <TargetConditionals.h>

#if TARGET_OS_IPHONE

#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecItem.h>
#include <Security/SecCertificateRequest.h>
#include <Security/SecInternal.h>
#include <utilities/array_size.h>

#include <AssertMacros.h>

#include "testcert.h"

static inline CFMutableArrayRef maa(CFMutableArrayRef array, CFTypeRef a) {
	CFMutableArrayRef ma = array;
	if (!ma)
		ma = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
	if (ma) {
		CFArrayAppendValue(ma, a);
		CFRelease(a);
	}
	return ma;
}


CFArrayRef test_cert_string_to_subject(CFStringRef subject)
{
	CFMutableArrayRef subject_array = NULL;
	char buffer[1024];

	if (!CFStringGetCString(subject, buffer, sizeof(buffer), kCFStringEncodingASCII))
		goto out;

	char *s = buffer, *e = NULL;
	while ( (e = strchr(s, ',')) || (e = strchr(s, '\0')) ) {
		if (s == e)
			break;
		if (*e && (*(e-1) == '\\'))
			continue;
		char *k;
		while ((k = strchr(s, '=')) &&
				(*(k-1) == '\\'));
		if ( ((k - s) > 0) && ((e - k) > 1) ) {
			CFStringRef key = CFStringCreateWithBytes(kCFAllocatorDefault, (uint8_t *)s, k - s, kCFStringEncodingASCII, false);
			CFStringRef value = CFStringCreateWithBytes(kCFAllocatorDefault, (uint8_t *)k + 1, e - k - 1, kCFStringEncodingASCII, false);
			subject_array = maa(subject_array, maa(NULL, maa(maa(NULL, key), value)));
		}
        if (*e == '\0')
            break;
		s = e + 1;
	}

out:
	return subject_array;
}


static void test_cert_key_usage(CFMutableDictionaryRef extensions_dict, unsigned int key_usage)
{
	int key_usage_int = key_usage;
	CFNumberRef key_usage_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage_int);
	CFDictionarySetValue(extensions_dict, kSecCertificateKeyUsage, key_usage_num);
	CFRelease(key_usage_num);
}


static void test_cert_path_length(CFMutableDictionaryRef extensions_dict, unsigned int path_length)
{
	int path_len_int = path_length;
	CFNumberRef path_len_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &path_len_int);
	CFDictionarySetValue(extensions_dict, kSecCSRBasicContraintsPathLen, path_len_num);
	CFRelease(path_len_num);
}


SecIdentityRef test_cert_create_root_certificate(CFStringRef subject, SecKeyRef public_key, SecKeyRef private_key)
{
	SecCertificateRef ca_cert = NULL;
	SecIdentityRef ca_identity = NULL;
    CFMutableDictionaryRef extensions = NULL;

	CFArrayRef ca_subject = NULL;
	require(ca_subject = test_cert_string_to_subject(subject), out);
	extensions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	test_cert_key_usage(extensions, kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign);
	test_cert_path_length(extensions, 0);
	ca_cert = SecGenerateSelfSignedCertificate(ca_subject, extensions, public_key, private_key);
	if (private_key && ca_cert)
		ca_identity = SecIdentityCreate(kCFAllocatorDefault, ca_cert, private_key);

out:
	CFReleaseSafe(extensions);
	CFReleaseSafe(ca_subject);
	CFReleaseSafe(ca_cert);

	return ca_identity;
}

SecCertificateRef test_cert_issue_certificate(SecIdentityRef ca_identity,
	SecKeyRef public_key, CFStringRef subject,
	unsigned int serial_no, unsigned int key_usage)
{
	SecCertificateRef cert = NULL;
	CFArrayRef cert_subject = NULL;
	CFDataRef serialno = NULL;
	CFMutableDictionaryRef extensions = NULL;

	unsigned int serial = htonl(serial_no);
	unsigned int serial_length = sizeof(serial);
	uint8_t *serial_non_zero = (uint8_t*)&serial;
	while (!*serial_non_zero && serial_length)
		{ serial_non_zero++; serial_length--; }
    serialno = CFDataCreate(kCFAllocatorDefault,
		serial_non_zero, serial_length);
	require(cert_subject = test_cert_string_to_subject(subject), out);
	//extensions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	//require(extensions, out);
	//test_cert_key_usage(extensions, key_usage);

	cert = SecIdentitySignCertificate(ca_identity, serialno,
	    public_key, cert_subject, NULL);

out:
	CFReleaseSafe(extensions);
	CFReleaseSafe(cert_subject);
	CFReleaseSafe(serialno);

	return cert;
}

OSStatus
test_cert_generate_key(uint32_t key_size_in_bits, CFTypeRef sec_attr_key_type,
                       SecKeyRef *private_key, SecKeyRef *public_key)
{
	CFNumberRef key_size = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_size_in_bits);
	const void *keygen_keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
	const void *keygen_vals[] = { sec_attr_key_type, key_size };
	CFDictionaryRef parameters = CFDictionaryCreate(kCFAllocatorDefault,
                                                    keygen_keys, keygen_vals, array_size(keygen_vals),
                                                    &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	CFRelease(key_size);

	return SecKeyGeneratePair(parameters, public_key, private_key);
}

#endif /* TARGET_OS_IPHONE */