#include <TargetConditionals.h>
#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 CF_RETURNS_RETAINED CFMutableArrayRef maa(CFMutableArrayRef array CF_CONSUMED, CFTypeRef a CF_CONSUMED) {
CFMutableArrayRef ma = array;
if (!ma)
ma = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (ma) {
CFArrayAppendValue(ma, a);
}
CFRelease(a);
return ma;
}
CF_RETURNS_RETAINED
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);
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);
}