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