2 * Copyright (c) 2006-2015 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@
25 * SecKeyProxy.m - Remote access to SecKey instance
28 #import <Foundation/Foundation.h>
29 #import <Foundation/NSXPCConnection_Private.h>
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>
37 #include <Security/SecKeyProxy.h>
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;
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> {
56 SecKeyProxy *_publicKeyProxy;
58 - (instancetype)initWithKey:(id)key certificate:(nullable NSData *)certificate;
59 @property (readonly, nonatomic) SecKeyRef key;
62 @implementation SecKeyProxyTarget
63 - (instancetype)initWithKey:(id)key certificate:(nullable NSData *)certificate {
64 if (self = [super init]) {
66 _certificate = certificate;
72 return (__bridge SecKeyRef)_key;
75 - (void)initializeWithReply:(void (^)(NSData *_Nullable))reply {
76 return reply(_certificate);
79 - (void)getBlockSizeWithReply:(void (^)(size_t))reply {
80 return reply(SecKeyGetBlockSize(self.key));
83 - (void)getAttributesWithReply:(void (^)(NSDictionary *))reply {
84 return reply(CFBridgingRelease(SecKeyCopyAttributes(self.key)));
87 - (void)getExternalRepresentationWithReply:(void (^)(NSData *, NSError *))reply {
89 NSData *data = CFBridgingRelease(SecKeyCopyExternalRepresentation(self.key, (void *)&error));
90 return reply(data, error);
93 - (void)getDescriptionWithReply:(void (^)(NSString *))reply {
94 NSString *description = CFBridgingRelease(CFCopyDescription(self.key));
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)];
103 return reply(description);
106 - (void)getAlgorithmIDWithReply:(void (^)(NSInteger))reply {
107 return reply(SecKeyGetAlgorithmId(self.key));
110 - (void)getPublicKey:(void (^)(NSXPCListenerEndpoint *endpoint))reply {
111 if (_publicKeyProxy == nil) {
112 id publicKey = CFBridgingRelease(SecKeyCopyPublicKey(self.key));
113 if (publicKey == nil) {
116 _publicKeyProxy = [[SecKeyProxy alloc] initWithKey:(__bridge SecKeyRef)publicKey];
118 return reply(_publicKeyProxy.endpoint);
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);
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);
132 // MARK: SecKeyProxy implementation
133 @interface SecKeyProxy() <NSXPCListenerDelegate>
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;
144 // All connections created to this proxy instance are serialized to this single queue.
145 [_listener _setQueue: dispatch_queue_create("SecKeyProxy", NULL)];
152 - (instancetype)initWithKey:(SecKeyRef)key {
153 return [self initWithKey:key certificate:nil];
156 - (instancetype)initWithIdentity:(SecIdentityRef)identity {
159 SecIdentityCopyPrivateKey(identity, (void *)&key);
160 SecIdentityCopyCertificate(identity, (void *)&certificate);
161 if (key == nil && certificate == nil) {
165 // Extract data from the certificate.
166 NSData *certificateData = CFBridgingRelease(SecCertificateCopyData((SecCertificateRef)certificate));
167 if (certificateData == nil) {
171 return [self initWithKey:(__bridge SecKeyRef)key certificate:certificateData];
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];
183 [_listener invalidate];
190 - (NSXPCListenerEndpoint *)endpoint {
191 return _listener.endpoint;
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;
201 static void SecRemoteKeyDestroy(SecKeyRef key) {
202 NSXPCConnection *conn = CFBridgingRelease(key->key);
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) {
210 *error = (__bridge_retained CFErrorRef)_error;
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;
221 return localBlockSize;
224 static CFDictionaryRef SecRemoteKeyCopyAttributeDictionary(SecKeyRef key) {
225 __block NSDictionary *localAttributes;
226 [[SecKeyProxy targetForKey:key error:NULL] getAttributesWithReply:^(NSDictionary *attributes) {
227 localAttributes = attributes;
229 return CFBridgingRetain(localAttributes);
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) {
239 if (localData == nil && error != NULL) {
240 *error = (__bridge_retained CFErrorRef)localError;
242 return CFBridgingRetain(localData);
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];
250 return CFBridgingRetain(localDescription);
253 static CFIndex SecRemoteKeyGetAlgorithmID(SecKeyRef key) {
254 __block CFIndex localAlgorithmID = kSecNullAlgorithmID;
255 [[SecKeyProxy targetForKey:key error:NULL] getAlgorithmIDWithReply:^(NSInteger algorithmID) {
256 localAlgorithmID = algorithmID;
258 return localAlgorithmID;
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]);
268 return (__bridge_retained SecKeyRef)publicKey;
271 static CFTypeRef SecRemoteKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm, CFArrayRef algorithms, SecKeyOperationMode mode, CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
272 NSMutableArray *parameters = @[].mutableCopy;
274 [parameters addObject:(__bridge id)in1];
276 [parameters addObject:(__bridge id)in2];
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];
284 else if (error != NULL) {
285 *error = (__bridge_retained CFErrorRef)_error;
288 return CFBridgingRetain(localResult);
291 static const SecKeyDescriptor SecRemoteKeyDescriptor = {
292 .version = kSecKeyDescriptorVersion,
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,
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)];
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;
319 if (localError == nil) {
320 if (certificate != nil) {
321 *certificate = localCertificate;
324 [connection invalidate];
331 // Wrap returned connection in SecKeyRef instance.
332 return SecKeyCreate(kCFAllocatorDefault, &SecRemoteKeyDescriptor, CFBridgingRetain(connection), 0, kSecKeyEncodingRaw);
335 + (SecKeyRef)createKeyFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
336 return [self createItemFromEndpoint:endpoint certificate:nil error:error];
339 + (SecIdentityRef)createIdentityFromEndpoint:(NSXPCListenerEndpoint *)endpoint error:(NSError * _Nullable __autoreleasing *)error {
340 NSData *certificateData;
341 id key = CFBridgingRelease([self createItemFromEndpoint:endpoint certificate:&certificateData error:error]);
345 if (certificateData == nil) {
347 *error = [NSError errorWithDomain:(NSString *)kSecErrorDomain code:errSecParam userInfo:@{(id)NSLocalizedDescriptionKey: @"Attempt to create remote identity from key-only proxy"}];
352 id certificate = CFBridgingRelease(SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData));
353 return SecIdentityCreate(kCFAllocatorDefault, (__bridge SecCertificateRef)certificate, (__bridge SecKeyRef)key);