]> git.saurik.com Git - apple/security.git/blame - OSX/sec/Security/SecKeyProxy.m
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / sec / Security / SecKeyProxy.m
CommitLineData
79b9da22
A
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]) {
d64be36e
A
139 if (key != nil) {
140 _key = CFBridgingRelease(CFRetainSafe(key));
141 } else {
142 _key = nil;
143 }
79b9da22
A
144 _certificate = certificate;
145 _listener = [NSXPCListener anonymousListener];
146 _listener.delegate = self;
147
148 // All connections created to this proxy instance are serialized to this single queue.
149 [_listener _setQueue: dispatch_queue_create("SecKeyProxy", NULL)];
150 [_listener resume];
151 }
152
153 return self;
154}
155
156- (instancetype)initWithKey:(SecKeyRef)key {
157 return [self initWithKey:key certificate:nil];
158}
159
160- (instancetype)initWithIdentity:(SecIdentityRef)identity {
161 id key;
162 id certificate;
163 SecIdentityCopyPrivateKey(identity, (void *)&key);
164 SecIdentityCopyCertificate(identity, (void *)&certificate);
165 if (key == nil && certificate == nil) {
166 return nil;
167 }
168
169 // Extract data from the certificate.
170 NSData *certificateData = CFBridgingRelease(SecCertificateCopyData((SecCertificateRef)certificate));
171 if (certificateData == nil) {
172 return nil;
173 }
174
175 return [self initWithKey:(__bridge SecKeyRef)key certificate:certificateData];
176}
177
178- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
179 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SecKeyProxyProtocol)];
180 newConnection.exportedObject = [[SecKeyProxyTarget alloc] initWithKey:_key certificate:_certificate];
181 [newConnection _setQueue:[_listener _queue]];
182 [newConnection resume];
183 return YES;
184}
185
186- (void)invalidate {
187 [_listener invalidate];
188}
189
190- (void)dealloc {
191 [self invalidate];
192}
193
194- (NSXPCListenerEndpoint *)endpoint {
195 return _listener.endpoint;
196}
197
198// MARK: Client side: remote-connected SecKey instance.
199static OSStatus SecRemoteKeyInit(SecKeyRef key, const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
200 // keyData and key->key are both actually type-punned NSXPCConnection owning pointers.
201 key->key = (void *)keyData;
202 return errSecSuccess;
203}
204
205static void SecRemoteKeyDestroy(SecKeyRef key) {
206 NSXPCConnection *conn = CFBridgingRelease(key->key);
207 [conn invalidate];
208}
209
210+ (id<SecKeyProxyProtocol>)targetForKey:(SecKeyRef)key error:(CFErrorRef *)error {
211 NSXPCConnection *connection = (__bridge NSXPCConnection *)key->key;
212 id<SecKeyProxyProtocol> result = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull _error) {
213 if (error != NULL) {
214 *error = (__bridge_retained CFErrorRef)_error;
215 }
216 }];
217 return result;
218}
219
220static size_t SecRemoteKeyBlockSize(SecKeyRef key) {
221 __block size_t localBlockSize = 0;
222 [[SecKeyProxy targetForKey:key error:NULL] getBlockSizeWithReply:^(size_t blockSize) {
223 localBlockSize = blockSize;
224 }];
225 return localBlockSize;
226}
227
228static CFDictionaryRef SecRemoteKeyCopyAttributeDictionary(SecKeyRef key) {
229 __block NSDictionary *localAttributes;
230 [[SecKeyProxy targetForKey:key error:NULL] getAttributesWithReply:^(NSDictionary *attributes) {
231 localAttributes = attributes;
232 }];
233 return CFBridgingRetain(localAttributes);
234}
235
236static CFDataRef SecRemoteKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error) {
237 __block NSData *localData;
238 __block NSError *localError;
239 [[SecKeyProxy targetForKey:key error:error] getExternalRepresentationWithReply:^(NSData *data, NSError *error) {
240 localData = data;
241 localError = error;
242 }];
243 if (localData == nil && error != NULL) {
244 *error = (__bridge_retained CFErrorRef)localError;
245 }
246 return CFBridgingRetain(localData);
247}
248
249static CFStringRef SecRemoteKeyCopyDescription(SecKeyRef key) {
250 __block NSString *localDescription;
251 [[SecKeyProxy targetForKey:key error:NULL] getDescriptionWithReply:^(NSString *description) {
252 localDescription = [NSString stringWithFormat:@"<SecKeyRef remoteKey: %@>", description];
253 }];
254 return CFBridgingRetain(localDescription);
255}
256
257static CFIndex SecRemoteKeyGetAlgorithmID(SecKeyRef key) {
258 __block CFIndex localAlgorithmID = kSecNullAlgorithmID;
259 [[SecKeyProxy targetForKey:key error:NULL] getAlgorithmIDWithReply:^(NSInteger algorithmID) {
260 localAlgorithmID = algorithmID;
261 }];
262 return localAlgorithmID;
263}
264
265static SecKeyRef SecRemoteKeyCopyPublicKey(SecKeyRef key) {
266 __block id publicKey;
267 [[SecKeyProxy targetForKey:key error:NULL] getPublicKey:^(NSXPCListenerEndpoint *endpoint) {
268 if (endpoint != nil) {
269 publicKey = CFBridgingRelease([SecKeyProxy createKeyFromEndpoint:endpoint error:nil]);
270 }
271 }];
272 return (__bridge_retained SecKeyRef)publicKey;
273}
274
275static CFTypeRef SecRemoteKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm, CFArrayRef algorithms, SecKeyOperationMode mode, CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
276 NSMutableArray *parameters = @[].mutableCopy;
277 if (in1 != NULL) {
278 [parameters addObject:(__bridge id)in1];
279 if (in2 != NULL) {
280 [parameters addObject:(__bridge id)in2];
281 }
282 }
283 __block id localResult;
284 [[SecKeyProxy targetForKey:key error:error] performOperation:operation algorithm:(__bridge NSString *)algorithm parameters:parameters reply:^(NSArray *result, NSError *_error) {
285 if (result.count > 0) {
286 localResult = result[0];
287 }
288 else if (error != NULL) {
289 *error = (__bridge_retained CFErrorRef)_error;
290 }
291 }];
292 return CFBridgingRetain(localResult);
293}
294
295static const SecKeyDescriptor SecRemoteKeyDescriptor = {
296 .version = kSecKeyDescriptorVersion,
297 .name = "RemoteKey",
298 .init = SecRemoteKeyInit,
299 .destroy = SecRemoteKeyDestroy,
300 .blockSize = SecRemoteKeyBlockSize,
301 .copyDictionary = SecRemoteKeyCopyAttributeDictionary,
302 .copyExternalRepresentation = SecRemoteKeyCopyExternalRepresentation,
303 .describe = SecRemoteKeyCopyDescription,
304 .getAlgorithmID = SecRemoteKeyGetAlgorithmID,
305 .copyPublicKey = SecRemoteKeyCopyPublicKey,
306 .copyOperationResult = SecRemoteKeyCopyOperationResult,
307};
308
309+ (SecKeyRef)createItemFromEndpoint:(NSXPCListenerEndpoint *)endpoint certificate:(NSData **)certificate error:(NSError * _Nullable __autoreleasing *)error {
310 // Connect to the server proxy object.
311 NSXPCConnection *connection = [[NSXPCConnection alloc] initWithListenerEndpoint:endpoint];
312 connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SecKeyProxyProtocol)];
313 [connection resume];
314
315 // Initialize remote object.
316 __block NSError *localError;
317 __block NSData *localCertificate;
318 [[connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
319 localError = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecItemNotFound userInfo:@{NSUnderlyingErrorKey: error}];
320 }] initializeWithReply:^(NSData *_Nullable _certificate){
321 localCertificate = _certificate;
322 }];
323 if (localError == nil) {
324 if (certificate != nil) {
325 *certificate = localCertificate;
326 }
327 } else {
328 [connection invalidate];
329 if (error != NULL) {
330 *error = localError;
331 }
332 return NULL;
333 }
334
335 // Wrap returned connection in SecKeyRef instance.
336 return SecKeyCreate(kCFAllocatorDefault, &SecRemoteKeyDescriptor, CFBridgingRetain(connection), 0, kSecKeyEncodingRaw);
337}
338
339+ (SecKeyRef)createKeyFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
340 return [self createItemFromEndpoint:endpoint certificate:nil error:error];
341}
342
343+ (SecIdentityRef)createIdentityFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
344 NSData *certificateData;
345 id key = CFBridgingRelease([self createItemFromEndpoint:endpoint certificate:&certificateData error:error]);
346 if (key == nil) {
347 return NULL;
348 }
349 if (certificateData == nil) {
350 if (error != NULL) {
351 *error = [NSError errorWithDomain:(NSString *)kSecErrorDomain code:errSecParam userInfo:@{(id)NSLocalizedDescriptionKey: @"Attempt to create remote identity from key-only proxy"}];
352 }
353 return NULL;
354 }
355
356 id certificate = CFBridgingRelease(SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData));
357 return SecIdentityCreate(kCFAllocatorDefault, (__bridge SecCertificateRef)certificate, (__bridge SecKeyRef)key);
358}
359@end