]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecKeyProxy.m
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / sec / Security / SecKeyProxy.m
1 /*
2 * Copyright (c) 2006-2015 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 /*
25 * SecKeyProxy.m - Remote access to SecKey instance
26 */
27
28 #import <Foundation/Foundation.h>
29 #import <Foundation/NSXPCConnection_Private.h>
30
31 #include <Security/SecBasePriv.h>
32 #include <Security/SecKeyInternal.h>
33 #include <Security/SecIdentityPriv.h>
34 #include <utilities/debugging.h>
35 #include <utilities/SecCFError.h>
36
37 #include <Security/SecKeyProxy.h>
38
39 // MARK: XPC-level protocol for communication between server and client.
40 @protocol SecKeyProxyProtocol
41 - (void)initializeWithReply:(void (^)(NSData * _Nullable certificate))reply;
42 - (void)getBlockSizeWithReply:(void (^)(size_t blockSize))reply;
43 - (void)getAttributesWithReply:(void (^)(NSDictionary *attributes))reply;
44 - (void)getExternalRepresentationWithReply:(void (^)(NSData *data, NSError *error))reply;
45 - (void)getDescriptionWithReply:(void (^)(NSString *description))reply;
46 - (void)getAlgorithmIDWithReply:(void (^)(NSInteger algorithmID))reply;
47 - (void)getPublicKey:(void (^)(NSXPCListenerEndpoint *endpoint))reply;
48 - (void)performOperation:(SecKeyOperationType)operation algorithm:(NSString *)algorithm parameters:(NSArray *)parameters reply:(void (^)(NSArray *result, NSError *error))reply;
49 @end
50
51 // MARK: XPC target object for SecKeyProxy side
52 // Logically could be embedded in SecKeyProxy, but that would cause ownership cycles, since target object is always owned by its associated XPC connection.
53 @interface SecKeyProxyTarget : NSObject<SecKeyProxyProtocol> {
54 id _key;
55 NSData *_certificate;
56 SecKeyProxy *_publicKeyProxy;
57 }
58 - (instancetype)initWithKey:(id)key certificate:(nullable NSData *)certificate;
59 @property (readonly, nonatomic) SecKeyRef key;
60 @end
61
62 @implementation SecKeyProxyTarget
63 - (instancetype)initWithKey:(id)key certificate:(nullable NSData *)certificate {
64 if (self = [super init]) {
65 _key = key;
66 _certificate = certificate;
67 }
68 return self;
69 }
70
71 - (SecKeyRef)key {
72 return (__bridge SecKeyRef)_key;
73 }
74
75 - (void)initializeWithReply:(void (^)(NSData *_Nullable))reply {
76 return reply(_certificate);
77 }
78
79 - (void)getBlockSizeWithReply:(void (^)(size_t))reply {
80 return reply(SecKeyGetBlockSize(self.key));
81 }
82
83 - (void)getAttributesWithReply:(void (^)(NSDictionary *))reply {
84 return reply(CFBridgingRelease(SecKeyCopyAttributes(self.key)));
85 }
86
87 - (void)getExternalRepresentationWithReply:(void (^)(NSData *, NSError *))reply {
88 NSError *error;
89 NSData *data = CFBridgingRelease(SecKeyCopyExternalRepresentation(self.key, (void *)&error));
90 return reply(data, error);
91 }
92
93 - (void)getDescriptionWithReply:(void (^)(NSString *))reply {
94 NSString *description = CFBridgingRelease(CFCopyDescription(self.key));
95
96 // Strip wrapping "<SecKeyRef " and ">" if present.
97 if ([description hasPrefix:@"<SecKeyRef "] && [description hasSuffix:@">"]) {
98 description = [description substringWithRange:NSMakeRange(11, description.length - 12)];
99 } else if ([description hasPrefix:@"<SecKeyRef: "] && [description hasSuffix:@">"]) {
100 description = [description substringWithRange:NSMakeRange(12, description.length - 13)];
101 }
102
103 return reply(description);
104 }
105
106 - (void)getAlgorithmIDWithReply:(void (^)(NSInteger))reply {
107 return reply(SecKeyGetAlgorithmId(self.key));
108 }
109
110 - (void)getPublicKey:(void (^)(NSXPCListenerEndpoint *endpoint))reply {
111 if (_publicKeyProxy == nil) {
112 id publicKey = CFBridgingRelease(SecKeyCopyPublicKey(self.key));
113 if (publicKey == nil) {
114 return reply(nil);
115 }
116 _publicKeyProxy = [[SecKeyProxy alloc] initWithKey:(__bridge SecKeyRef)publicKey];
117 }
118 return reply(_publicKeyProxy.endpoint);
119 }
120
121 - (void)performOperation:(SecKeyOperationType)operation algorithm:(NSString *)algorithm parameters:(NSArray *)parameters reply:(void (^)(NSArray *, NSError *))reply {
122 NSMutableArray *algorithms = @[algorithm].mutableCopy;
123 CFTypeRef in1 = (__bridge CFTypeRef)(parameters.count > 0 ? parameters[0] : nil);
124 CFTypeRef in2 = (__bridge CFTypeRef)(parameters.count > 1 ? parameters[1] : nil);
125 NSError *error;
126 SecKeyOperationContext context = { self.key, operation, (__bridge CFMutableArrayRef)algorithms };
127 id result = CFBridgingRelease(SecKeyRunAlgorithmAndCopyResult(&context, in1, in2, (void *)&error));
128 return reply(result ? @[result] : @[], error);
129 }
130 @end
131
132 // MARK: SecKeyProxy implementation
133 @interface SecKeyProxy() <NSXPCListenerDelegate>
134 @end
135
136 @implementation SecKeyProxy
137 - (instancetype)initWithKey:(SecKeyRef)key certificate:(nullable NSData *)certificate {
138 if (self = [super init]) {
139 _key = CFBridgingRelease(CFRetain(key));
140 _certificate = certificate;
141 _listener = [NSXPCListener anonymousListener];
142 _listener.delegate = self;
143
144 // All connections created to this proxy instance are serialized to this single queue.
145 [_listener _setQueue: dispatch_queue_create("SecKeyProxy", NULL)];
146 [_listener resume];
147 }
148
149 return self;
150 }
151
152 - (instancetype)initWithKey:(SecKeyRef)key {
153 return [self initWithKey:key certificate:nil];
154 }
155
156 - (instancetype)initWithIdentity:(SecIdentityRef)identity {
157 id key;
158 id certificate;
159 SecIdentityCopyPrivateKey(identity, (void *)&key);
160 SecIdentityCopyCertificate(identity, (void *)&certificate);
161 if (key == nil && certificate == nil) {
162 return nil;
163 }
164
165 // Extract data from the certificate.
166 NSData *certificateData = CFBridgingRelease(SecCertificateCopyData((SecCertificateRef)certificate));
167 if (certificateData == nil) {
168 return nil;
169 }
170
171 return [self initWithKey:(__bridge SecKeyRef)key certificate:certificateData];
172 }
173
174 - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
175 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SecKeyProxyProtocol)];
176 newConnection.exportedObject = [[SecKeyProxyTarget alloc] initWithKey:_key certificate:_certificate];
177 [newConnection _setQueue:[_listener _queue]];
178 [newConnection resume];
179 return YES;
180 }
181
182 - (void)invalidate {
183 [_listener invalidate];
184 }
185
186 - (void)dealloc {
187 [self invalidate];
188 }
189
190 - (NSXPCListenerEndpoint *)endpoint {
191 return _listener.endpoint;
192 }
193
194 // MARK: Client side: remote-connected SecKey instance.
195 static OSStatus SecRemoteKeyInit(SecKeyRef key, const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
196 // keyData and key->key are both actually type-punned NSXPCConnection owning pointers.
197 key->key = (void *)keyData;
198 return errSecSuccess;
199 }
200
201 static void SecRemoteKeyDestroy(SecKeyRef key) {
202 NSXPCConnection *conn = CFBridgingRelease(key->key);
203 [conn invalidate];
204 }
205
206 + (id<SecKeyProxyProtocol>)targetForKey:(SecKeyRef)key error:(CFErrorRef *)error {
207 NSXPCConnection *connection = (__bridge NSXPCConnection *)key->key;
208 id<SecKeyProxyProtocol> result = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull _error) {
209 if (error != NULL) {
210 *error = (__bridge_retained CFErrorRef)_error;
211 }
212 }];
213 return result;
214 }
215
216 static size_t SecRemoteKeyBlockSize(SecKeyRef key) {
217 __block size_t localBlockSize = 0;
218 [[SecKeyProxy targetForKey:key error:NULL] getBlockSizeWithReply:^(size_t blockSize) {
219 localBlockSize = blockSize;
220 }];
221 return localBlockSize;
222 }
223
224 static CFDictionaryRef SecRemoteKeyCopyAttributeDictionary(SecKeyRef key) {
225 __block NSDictionary *localAttributes;
226 [[SecKeyProxy targetForKey:key error:NULL] getAttributesWithReply:^(NSDictionary *attributes) {
227 localAttributes = attributes;
228 }];
229 return CFBridgingRetain(localAttributes);
230 }
231
232 static CFDataRef SecRemoteKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error) {
233 __block NSData *localData;
234 __block NSError *localError;
235 [[SecKeyProxy targetForKey:key error:error] getExternalRepresentationWithReply:^(NSData *data, NSError *error) {
236 localData = data;
237 localError = error;
238 }];
239 if (localData == nil && error != NULL) {
240 *error = (__bridge_retained CFErrorRef)localError;
241 }
242 return CFBridgingRetain(localData);
243 }
244
245 static CFStringRef SecRemoteKeyCopyDescription(SecKeyRef key) {
246 __block NSString *localDescription;
247 [[SecKeyProxy targetForKey:key error:NULL] getDescriptionWithReply:^(NSString *description) {
248 localDescription = [NSString stringWithFormat:@"<SecKeyRef remoteKey: %@>", description];
249 }];
250 return CFBridgingRetain(localDescription);
251 }
252
253 static CFIndex SecRemoteKeyGetAlgorithmID(SecKeyRef key) {
254 __block CFIndex localAlgorithmID = kSecNullAlgorithmID;
255 [[SecKeyProxy targetForKey:key error:NULL] getAlgorithmIDWithReply:^(NSInteger algorithmID) {
256 localAlgorithmID = algorithmID;
257 }];
258 return localAlgorithmID;
259 }
260
261 static SecKeyRef SecRemoteKeyCopyPublicKey(SecKeyRef key) {
262 __block id publicKey;
263 [[SecKeyProxy targetForKey:key error:NULL] getPublicKey:^(NSXPCListenerEndpoint *endpoint) {
264 if (endpoint != nil) {
265 publicKey = CFBridgingRelease([SecKeyProxy createKeyFromEndpoint:endpoint error:nil]);
266 }
267 }];
268 return (__bridge_retained SecKeyRef)publicKey;
269 }
270
271 static CFTypeRef SecRemoteKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm, CFArrayRef algorithms, SecKeyOperationMode mode, CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
272 NSMutableArray *parameters = @[].mutableCopy;
273 if (in1 != NULL) {
274 [parameters addObject:(__bridge id)in1];
275 if (in2 != NULL) {
276 [parameters addObject:(__bridge id)in2];
277 }
278 }
279 __block id localResult;
280 [[SecKeyProxy targetForKey:key error:error] performOperation:operation algorithm:(__bridge NSString *)algorithm parameters:parameters reply:^(NSArray *result, NSError *_error) {
281 if (result.count > 0) {
282 localResult = result[0];
283 }
284 else if (error != NULL) {
285 *error = (__bridge_retained CFErrorRef)_error;
286 }
287 }];
288 return CFBridgingRetain(localResult);
289 }
290
291 static const SecKeyDescriptor SecRemoteKeyDescriptor = {
292 .version = kSecKeyDescriptorVersion,
293 .name = "RemoteKey",
294 .init = SecRemoteKeyInit,
295 .destroy = SecRemoteKeyDestroy,
296 .blockSize = SecRemoteKeyBlockSize,
297 .copyDictionary = SecRemoteKeyCopyAttributeDictionary,
298 .copyExternalRepresentation = SecRemoteKeyCopyExternalRepresentation,
299 .describe = SecRemoteKeyCopyDescription,
300 .getAlgorithmID = SecRemoteKeyGetAlgorithmID,
301 .copyPublicKey = SecRemoteKeyCopyPublicKey,
302 .copyOperationResult = SecRemoteKeyCopyOperationResult,
303 };
304
305 + (SecKeyRef)createItemFromEndpoint:(NSXPCListenerEndpoint *)endpoint certificate:(NSData **)certificate error:(NSError * _Nullable __autoreleasing *)error {
306 // Connect to the server proxy object.
307 NSXPCConnection *connection = [[NSXPCConnection alloc] initWithListenerEndpoint:endpoint];
308 connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SecKeyProxyProtocol)];
309 [connection resume];
310
311 // Initialize remote object.
312 __block NSError *localError;
313 __block NSData *localCertificate;
314 [[connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
315 localError = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecItemNotFound userInfo:@{NSUnderlyingErrorKey: error}];
316 }] initializeWithReply:^(NSData *_Nullable _certificate){
317 localCertificate = _certificate;
318 }];
319 if (localError == nil) {
320 if (certificate != nil) {
321 *certificate = localCertificate;
322 }
323 } else {
324 [connection invalidate];
325 if (error != NULL) {
326 *error = localError;
327 }
328 return NULL;
329 }
330
331 // Wrap returned connection in SecKeyRef instance.
332 return SecKeyCreate(kCFAllocatorDefault, &SecRemoteKeyDescriptor, CFBridgingRetain(connection), 0, kSecKeyEncodingRaw);
333 }
334
335 + (SecKeyRef)createKeyFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
336 return [self createItemFromEndpoint:endpoint certificate:nil error:error];
337 }
338
339 + (SecIdentityRef)createIdentityFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
340 NSData *certificateData;
341 id key = CFBridgingRelease([self createItemFromEndpoint:endpoint certificate:&certificateData error:error]);
342 if (key == nil) {
343 return NULL;
344 }
345 if (certificateData == nil) {
346 if (error != NULL) {
347 *error = [NSError errorWithDomain:(NSString *)kSecErrorDomain code:errSecParam userInfo:@{(id)NSLocalizedDescriptionKey: @"Attempt to create remote identity from key-only proxy"}];
348 }
349 return NULL;
350 }
351
352 id certificate = CFBridgingRelease(SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData));
353 return SecIdentityCreate(kCFAllocatorDefault, (__bridge SecCertificateRef)certificate, (__bridge SecKeyRef)key);
354 }
355 @end