/ OSX / sec / Security / SecKeyProxy.m
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          if (key != nil) {
140              _key = CFBridgingRelease(CFRetainSafe(key));
141          } else {
142              _key = nil;
143          }
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.
199  static 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  
205  static 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  
220  static 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  
228  static 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  
236  static 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  
249  static 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  
257  static 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  
265  static 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  
275  static 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  
295  static 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