2 * Copyright (c) 2012-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // si-68-secmatchissuer.c
31 #include <CoreFoundation/CoreFoundation.h>
32 #include <Security/Security.h>
33 #include <libDER/libDER.h>
34 #include <libDER/DER_Decode.h>
35 #include <libDER/asn1Types.h>
36 #include <Security/SecCertificateInternal.h>
37 #include <Security/SecCertificatePriv.h>
38 #include <Security/SecIdentityPriv.h>
39 #include <Security/SecItem.h>
40 #include <Security/SecInternal.h>
41 #include <utilities/array_size.h>
43 #include "shared_regressions.h"
44 #include <test/testcert.h>
47 static OSStatus add_item_to_keychain(CFTypeRef item, CFDataRef * persistent_ref)
49 const void *keys[] = { kSecValueRef, kSecReturnPersistentRef };
50 const void *vals[] = { item, kCFBooleanTrue };
51 CFDictionaryRef add_query = CFDictionaryCreate(NULL, keys, vals, array_size(keys), NULL, NULL);
52 OSStatus status = errSecAllocate;
54 status = SecItemAdd(add_query, (CFTypeRef *)persistent_ref);
60 static OSStatus remove_item_from_keychain(CFTypeRef item, CFDataRef * persistent_ref)
62 const void *keys[] = { kSecValueRef, kSecReturnPersistentRef };
63 const void *vals[] = { item, kCFBooleanTrue };
64 CFDictionaryRef add_query = CFDictionaryCreate(NULL, keys, vals, array_size(keys), NULL, NULL);
65 OSStatus status = errSecAllocate;
67 status = SecItemAdd(add_query, (CFTypeRef *)persistent_ref);
74 static OSStatus
add_item(CFTypeRef item
)
76 CFDictionaryRef add_query
= CFDictionaryCreate(NULL
, (const void **)&kSecValueRef
, &item
, 1, NULL
, NULL
);
77 OSStatus status
= SecItemAdd(add_query
, NULL
);
82 static OSStatus
remove_item(CFTypeRef item
)
84 CFDictionaryRef remove_query
= CFDictionaryCreate(NULL
, (const void **)&kSecValueRef
, &item
, 1, NULL
, NULL
);
85 OSStatus status
= SecItemDelete(remove_query
);
86 CFRelease(remove_query
);
90 static void tests(void)
93 // MARK: test SecDistinguishedNameCopyNormalizedContent
95 unsigned char example_dn
[] = {
96 0x30, 0x4f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
97 0x02, 0x43, 0x5a, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0a,
98 0x0c, 0x1e, 0x4d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x65, 0x72, 0x73, 0x74,
99 0x76, 0x6f, 0x20, 0x73, 0x70, 0x72, 0x61, 0x76, 0x65, 0x64, 0x6c, 0x6e,
100 0x6f, 0x73, 0x74, 0x69, 0x20, 0xc4, 0x8c, 0x52, 0x31, 0x17, 0x30, 0x15,
101 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0e, 0x4d, 0x53, 0x70, 0x20, 0x52,
102 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x30, 0x31
104 unsigned int example_dn_len
= 81;
106 CFDataRef normalized_dn
= NULL
, dn
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, example_dn
, example_dn_len
, kCFAllocatorNull
);
107 ok(dn
, "got dn as data");
108 ok(normalized_dn
= SecDistinguishedNameCopyNormalizedContent(dn
), "convert to normalized form");
110 //CFShow(normalized_dn);
112 CFReleaseNull(normalized_dn
);
114 // MARK: generate certificate hierarchy
116 SecKeyRef public_key
= NULL
, private_key
= NULL
;
117 ok_status(test_cert_generate_key(2048, kSecAttrKeyTypeRSA
, &private_key
, &public_key
), "generate keypair");
118 // make organization random uuid to avoid previous run to spoil the fun
120 CFUUIDRef UUID
= CFUUIDCreate(kCFAllocatorDefault
);
121 CFStringRef uuidString
= CFUUIDCreateString(kCFAllocatorDefault
, UUID
);
122 CFStringRef root_authority_name
= CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("O=%@,CN=Root CA"), uuidString
);
123 CFStringRef intermediate_authority_name
= CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("O=%@,CN=Intermediate CA"), uuidString
);
124 CFStringRef leaf_name
= CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("O=%@,CN=Client"), uuidString
);
125 CFRelease(uuidString
);
128 SecIdentityRef ca_identity
=
129 test_cert_create_root_certificate(root_authority_name
, public_key
, private_key
);
130 CFRelease(root_authority_name
);
132 SecCertificateRef ca_cert
= NULL
;
133 SecIdentityCopyCertificate(ca_identity
, &ca_cert
);
136 SecCertificateRef intermediate_cert
=
137 test_cert_issue_certificate(ca_identity
, public_key
, intermediate_authority_name
, 42, kSecKeyUsageKeyCertSign
);
138 CFRelease(intermediate_authority_name
);
139 SecIdentityRef intermediate_identity
= SecIdentityCreate(kCFAllocatorDefault
, intermediate_cert
, private_key
);
141 ok_status(add_item(intermediate_cert
), "add intermediate");
142 //CFShow(intermediate_cert);
144 SecCertificateRef leaf_cert
= test_cert_issue_certificate(intermediate_identity
, public_key
,
145 leaf_name
, 4242, kSecKeyUsageDigitalSignature
);
146 CFRelease(leaf_name
);
147 SecIdentityRef leaf_identity
= SecIdentityCreate(kCFAllocatorDefault
, leaf_cert
, private_key
);
149 ok_status(add_item(leaf_identity
), "add leaf");
152 // this is already canonical - see if we can get the raw one
153 CFDataRef issuer
= SecCertificateGetNormalizedIssuerContent(intermediate_cert
);
154 ok(CFDataGetLength(issuer
) < 128, "max 127 bytes of content - or else you'll need to properly encode issuer sequence");
155 CFMutableDataRef canonical_issuer
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuer
) + 2);
156 CFDataSetLength(canonical_issuer
, CFDataGetLength(issuer
) + 2);
157 uint8_t * ptr
= CFDataGetMutableBytePtr(canonical_issuer
);
158 memcpy(ptr
+2, CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
160 ptr
[1] = CFDataGetLength(issuer
);
162 CFMutableArrayRef all_distinguished_names
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
163 CFArrayAppendValue(all_distinguished_names
, canonical_issuer
);
166 CFReleaseNull(canonical_issuer
);
167 const void *keys
[] = { kSecClass
, kSecReturnRef
, kSecMatchLimit
, kSecMatchIssuers
};
168 const void *vals
[] = { kSecClassIdentity
, kCFBooleanTrue
, kSecMatchLimitAll
, all_distinguished_names
};
169 CFDictionaryRef all_identities_query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, array_size(keys
), NULL
, NULL
);
170 CFTypeRef all_matching_identities
= NULL
;
171 ok_status(SecItemCopyMatching(all_identities_query
, &all_matching_identities
), "find all identities matching");
172 CFReleaseNull(all_identities_query
);
173 ok(((CFArrayGetTypeID() == CFGetTypeID(all_matching_identities
)) && (CFArrayGetCount(all_matching_identities
) == 2)), "return 2");
174 CFReleaseNull(all_matching_identities
);
175 //CFShow(all_matching_identities);
180 int limit
= 0x7fff; // To regress-test <rdar://problem/14603111>
181 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
182 const void *keys
[] = { kSecClass
, kSecReturnRef
, kSecMatchLimit
, kSecMatchIssuers
};
183 const void *vals
[] = { kSecClassCertificate
, kCFBooleanTrue
, cfLimit
, all_distinguished_names
};
184 CFDictionaryRef all_identities_query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, array_size(keys
), NULL
, NULL
);
185 CFTypeRef all_matching_certificates
= NULL
;
186 ok_status(SecItemCopyMatching(all_identities_query
, &all_matching_certificates
), "find all certificates matching");
187 CFReleaseNull(all_identities_query
);
188 ok(((CFArrayGetTypeID() == CFGetTypeID(all_matching_certificates
)) && (CFArrayGetCount(all_matching_certificates
) == 2)), "return 2");
189 //CFShow(all_matching_certificates);
190 CFReleaseSafe(cfLimit
);
191 CFReleaseNull(all_matching_certificates
);
194 /* On macOS, we don't allow kSecMatchIssuers to be used with kSecClassCertificate because performance is bad. */
196 int limit
= 0x7fff; // To regress-test <rdar://problem/14603111>
197 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
198 const void *keys
[] = { kSecClass
, kSecReturnRef
, kSecMatchLimit
, kSecMatchIssuers
};
199 const void *vals
[] = { kSecClassCertificate
, kCFBooleanTrue
, cfLimit
, all_distinguished_names
};
200 CFDictionaryRef all_identities_query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, array_size(keys
), NULL
, NULL
);
201 CFTypeRef all_matching_certificates
= NULL
;
202 is(errSecParam
, SecItemCopyMatching(all_identities_query
, &all_matching_certificates
), "find all certificates matching");
203 CFReleaseNull(all_identities_query
);
204 CFReleaseSafe(cfLimit
);
205 CFReleaseNull(all_matching_certificates
);
209 remove_item(leaf_identity
);
210 CFRelease(leaf_identity
);
211 CFRelease(leaf_cert
);
213 remove_item(intermediate_cert
);
214 CFRelease(intermediate_cert
);
215 CFRelease(intermediate_identity
);
218 CFRelease(ca_identity
);
220 CFRelease(public_key
);
221 CFRelease(private_key
);
225 int si_68_secmatchissuer(int argc
, char *const *argv
)