Skip to content

Commit

Permalink
Properly wait for TLS handshake to occur before RPC can be encrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
SatbirTanda committed Jul 1, 2019
1 parent c8053d7 commit eb6e81d
Show file tree
Hide file tree
Showing 15 changed files with 441 additions and 80 deletions.
1 change: 1 addition & 0 deletions SmartDeviceLink-iOS.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ ss.public_header_files = [
'SmartDeviceLink/SDLECallConfirmationStatus.h',
'SmartDeviceLink/SDLECallInfo.h',
'SmartDeviceLink/SDLElectronicParkBrakeStatus.h',
'SmartDeviceLink/SDLEncryptionManager.h',
'SmartDeviceLink/SDLEncryptionConfiguration.h',
'SmartDeviceLink/SDLEmergencyEvent.h',
'SmartDeviceLink/SDLEmergencyEventType.h',
Expand Down
20 changes: 18 additions & 2 deletions SmartDeviceLink-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
objects = {

/* Begin PBXBuildFile section */
005DF3C122C59176006E01A9 /* SDLEncryptionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 005DF3BF22C59176006E01A9 /* SDLEncryptionManager.h */; };
005DF3C122C59176006E01A9 /* SDLEncryptionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 005DF3BF22C59176006E01A9 /* SDLEncryptionManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
005DF3C222C59176006E01A9 /* SDLEncryptionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 005DF3C022C59176006E01A9 /* SDLEncryptionManager.m */; };
005DF3C522C59191006E01A9 /* SDLEncryptionLifecycleManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 005DF3C322C59191006E01A9 /* SDLEncryptionLifecycleManager.h */; };
005DF3C622C59191006E01A9 /* SDLEncryptionLifecycleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 005DF3C422C59191006E01A9 /* SDLEncryptionLifecycleManager.m */; };
005DF3CA22C62E00006E01A9 /* SDLEncryptionManagerConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 005DF3C822C62E00006E01A9 /* SDLEncryptionManagerConstants.h */; };
005DF3CB22C62E00006E01A9 /* SDLEncryptionManagerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 005DF3C922C62E00006E01A9 /* SDLEncryptionManagerConstants.m */; };
00E22CEC22C2F1B300BC6B08 /* SDLEncryptionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E22CEA22C2F1B200BC6B08 /* SDLEncryptionConfiguration.m */; };
00E22CED22C2F1B300BC6B08 /* SDLEncryptionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 00E22CEB22C2F1B300BC6B08 /* SDLEncryptionConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
00E22CF022C2F1D300BC6B08 /* SDLAsynchronousEncryptedRPCRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 00E22CEE22C2F1D300BC6B08 /* SDLAsynchronousEncryptedRPCRequestOperation.h */; };
Expand Down Expand Up @@ -1606,6 +1608,8 @@
005DF3C022C59176006E01A9 /* SDLEncryptionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLEncryptionManager.m; sourceTree = "<group>"; };
005DF3C322C59191006E01A9 /* SDLEncryptionLifecycleManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLEncryptionLifecycleManager.h; sourceTree = "<group>"; };
005DF3C422C59191006E01A9 /* SDLEncryptionLifecycleManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLEncryptionLifecycleManager.m; sourceTree = "<group>"; };
005DF3C822C62E00006E01A9 /* SDLEncryptionManagerConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLEncryptionManagerConstants.h; sourceTree = "<group>"; };
005DF3C922C62E00006E01A9 /* SDLEncryptionManagerConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLEncryptionManagerConstants.m; sourceTree = "<group>"; };
00E22CEA22C2F1B200BC6B08 /* SDLEncryptionConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLEncryptionConfiguration.m; sourceTree = "<group>"; };
00E22CEB22C2F1B300BC6B08 /* SDLEncryptionConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLEncryptionConfiguration.h; sourceTree = "<group>"; };
00E22CEE22C2F1D300BC6B08 /* SDLAsynchronousEncryptedRPCRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLAsynchronousEncryptedRPCRequestOperation.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3209,11 +3213,21 @@
name = Lifecycle;
sourceTree = "<group>";
};
005DF3C722C62DDA006E01A9 /* Utilities */ = {
isa = PBXGroup;
children = (
005DF3C822C62E00006E01A9 /* SDLEncryptionManagerConstants.h */,
005DF3C922C62E00006E01A9 /* SDLEncryptionManagerConstants.m */,
);
name = Utilities;
sourceTree = "<group>";
};
00E22CE822C2F19700BC6B08 /* Encryption */ = {
isa = PBXGroup;
children = (
005DF3BE22C590FB006E01A9 /* Lifecycle */,
00E22CE922C2F1A400BC6B08 /* Configuration */,
005DF3BE22C590FB006E01A9 /* Lifecycle */,
005DF3C722C62DDA006E01A9 /* Utilities */,
005DF3BF22C59176006E01A9 /* SDLEncryptionManager.h */,
005DF3C022C59176006E01A9 /* SDLEncryptionManager.m */,
);
Expand Down Expand Up @@ -6265,6 +6279,7 @@
880D267D220DE5DF00B3F496 /* SDLWeatherServiceManifest.h in Headers */,
DAA41D551DF66B2000BC7337 /* SDLH264VideoEncoder.h in Headers */,
8B7B319A1F2F7B5700BDC38D /* SDLVideoStreamingCodec.h in Headers */,
005DF3CA22C62E00006E01A9 /* SDLEncryptionManagerConstants.h in Headers */,
5D61FCAF1A84238C00846EE7 /* SDLGenericResponse.h in Headers */,
885468352225C1F800994D8D /* SDLCloudAppProperties.h in Headers */,
5D61FC4F1A84238C00846EE7 /* SDLBodyInformation.h in Headers */,
Expand Down Expand Up @@ -6891,6 +6906,7 @@
5D61FC9D1A84238C00846EE7 /* SDLEmergencyEventType.m in Sources */,
5D61FCAC1A84238C00846EE7 /* SDLFuelCutoffStatus.m in Sources */,
5D61FC871A84238C00846EE7 /* SDLDeviceStatus.m in Sources */,
005DF3CB22C62E00006E01A9 /* SDLEncryptionManagerConstants.m in Sources */,
5D61FD561A84238C00846EE7 /* SDLPutFile.m in Sources */,
1EAA474E20356B2D000FE74B /* SDLDisplayMode.m in Sources */,
5D61FCE71A84238C00846EE7 /* SDLKeypressMode.m in Sources */,
Expand Down
1 change: 1 addition & 0 deletions SmartDeviceLink.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ sdefault.public_header_files = [
'SmartDeviceLink/SDLECallConfirmationStatus.h',
'SmartDeviceLink/SDLECallInfo.h',
'SmartDeviceLink/SDLElectronicParkBrakeStatus.h',
'SmartDeviceLink/SDLEncryptionManager.h',
'SmartDeviceLink/SDLEncryptionConfiguration.h',
'SmartDeviceLink/SDLEmergencyEvent.h',
'SmartDeviceLink/SDLEmergencyEventType.h',
Expand Down
45 changes: 44 additions & 1 deletion SmartDeviceLink/SDLEncryptionLifecycleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,53 @@
//

#import <Foundation/Foundation.h>
#import "SDLProtocol.h"
#import "SDLConnectionManagerType.h"
#import "SDLEncryptionConfiguration.h"
#import "SDLProtocolListener.h"
#import "SDLPermissionManager.h"

@class SDLProtocol;
@class SDLStateMachine;

NS_ASSUME_NONNULL_BEGIN

@interface SDLEncryptionLifecycleManager : NSObject
@interface SDLEncryptionLifecycleManager : NSObject <SDLProtocolListener>

/**
* Whether or not the encryption session is connected.
*/
@property (assign, nonatomic, readonly, getter=isEncryptionReady) BOOL encryptionReady;


- (instancetype)init NS_UNAVAILABLE;

/**
Create a new streaming media manager for navigation and VPM apps with a specified configuration
@param connectionManager The pass-through for RPCs
@param configuration This session's configuration
@return A new streaming manager
*/
- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager configuration:(SDLEncryptionConfiguration *)configuration permissionManager:(SDLPermissionManager *)permissionManager rpcOperationQueue:(NSOperationQueue *)rpcOperationQueue;

/**
* Start the manager with a completion block that will be called when startup completes. This is used internally. To use an SDLStreamingMediaManager, you should use the manager found on `SDLManager`.
*/
- (void)startWithProtocol:(SDLProtocol *)protocol;

/**
* Stop the manager. This method is used internally.
*/
- (void)stop;

/**
* Send an Encrypted RPC request and set a completion handler that will be called with the response when the response returns.
*
* @param request The RPC request to send
* @param handler The handler that will be called when the response returns
*/
- (void)sendEncryptedRequest:(__kindof SDLRPCMessage *)request withResponseHandler:(nullable SDLResponseHandler)handler;

@end

Expand Down
208 changes: 208 additions & 0 deletions SmartDeviceLink/SDLEncryptionLifecycleManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,215 @@
//

#import "SDLEncryptionLifecycleManager.h"
#import "SDLEncryptionManagerConstants.h"
#import "SDLLogMacros.h"
#import "SDLStateMachine.h"
#import "SDLAsynchronousEncryptedRPCRequestOperation.h"
#import "SDLProtocolMessage.h"
#import "SDLRPCNotificationNotification.h"
#import "SDLOnHMIStatus.h"

@interface SDLEncryptionLifecycleManager() <SDLProtocolListener>

@property (strong, nonatomic, readonly) NSOperationQueue *rpcOperationQueue;
@property (strong, nonatomic, readonly) SDLPermissionManager *permissionManager;
@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
@property (weak, nonatomic) SDLProtocol *protocol;

@property (strong, nonatomic, readwrite) SDLStateMachine *encryptionStateMachine;
@property (copy, nonatomic, nullable) SDLHMILevel hmiLevel;

@end

@implementation SDLEncryptionLifecycleManager

- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager configuration:(SDLEncryptionConfiguration *)configuration permissionManager:(SDLPermissionManager *)permissionManager rpcOperationQueue:(NSOperationQueue *)rpcOperationQueue {
self = [super init];
if (!self) {
return nil;
}

SDLLogV(@"Creating EncryptionLifecycleManager");
_hmiLevel = SDLHMILevelNone;
_connectionManager = connectionManager;
_permissionManager = permissionManager;
_rpcOperationQueue = rpcOperationQueue;
_encryptionStateMachine = [[SDLStateMachine alloc] initWithTarget:self initialState:SDLEncryptionManagerStateStopped states:[self.class sdl_encryptionStateTransitionDictionary]];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusDidChange:) name:SDLDidChangeHMIStatusNotification object:nil];

return self;
}

- (void)startWithProtocol:(SDLProtocol *)protocol {
_protocol = protocol;

@synchronized(self.protocol.protocolDelegateTable) {
if (![self.protocol.protocolDelegateTable containsObject:self]) {
[self.protocol.protocolDelegateTable addObject:self];
}
}

[self sdl_startEncryptionService];
}

- (void)stop {
_hmiLevel = SDLHMILevelNone;
_protocol = nil;

SDLLogD(@"Stopping encryption manager");
[self sdl_stopEncryptionService];
}

- (void)sendEncryptedRequest:(SDLRPCRequest *)request withResponseHandler:(SDLResponseHandler)handler {
if (!self.protocol || !self.isEncryptionReady) {
SDLLogV(@"Encryption manager is not yet ready, wait until after proxy is opened");
return;
}

SDLAsynchronousEncryptedRPCRequestOperation *op = [[SDLAsynchronousEncryptedRPCRequestOperation alloc] initWithConnectionManager:self.connectionManager requestToEncrypt:request responseHandler:handler];

[self.rpcOperationQueue addOperation:op];
}

- (BOOL)isEncryptionReady {
return [self.encryptionStateMachine isCurrentState:SDLEncryptionManagerStateReady];
}

- (void)sdl_startEncryptionService {
SDLLogV(@"Attempting to start Encryption Service");
if (!self.protocol) {
SDLLogV(@"Encryption manager is not yet started");
return;
}

if (!self.permissionManager || !self.permissionManager.currentHMILevel || !self.permissionManager.permissions) {
SDLLogV(@"Permission Manager is not ready to encrypt.");
return;
}

// TODO: check if permissionManager has requireEncyrption flag in any RPC or itself
if (![self.permissionManager.currentHMILevel isEqualToEnum:SDLHMILevelNone]) {
[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStarting];
} else {
SDLLogE(@"Unable to send encryption start service request\n"
"permissionManager: %@\n"
"HMI state must be LIMITED, FULL, BACKGROUND: %@\n",
self.permissionManager.permissions, self.permissionManager.currentHMILevel);
}
}

- (void)sdl_sendEncryptionStartService {
SDLLogD(@"Sending secure rpc start service");
[self.protocol startSecureServiceWithType:SDLServiceTypeRPC payload:nil completionHandler:^(BOOL success, NSError *error) {
if (error) {
SDLLogE(@"TLS setup error: %@", error);
[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStopped];
}
}];
}

- (void)sdl_stopEncryptionService {
_protocol = nil;

[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStopped];
}

#pragma mark Encryption
+ (NSDictionary<SDLState *, SDLAllowableStateTransitions *> *)sdl_encryptionStateTransitionDictionary {
return @{
SDLEncryptionManagerStateStopped : @[SDLEncryptionManagerStateStarting],
SDLEncryptionManagerStateStarting : @[SDLEncryptionManagerStateStopped, SDLEncryptionManagerStateReady],
SDLEncryptionManagerStateReady : @[SDLEncryptionManagerStateShuttingDown, SDLEncryptionManagerStateStopped],
SDLEncryptionManagerStateShuttingDown : @[SDLEncryptionManagerStateStopped]
};
}

#pragma mark - State Machine
- (void)didEnterStateEncryptionStarting {
SDLLogD(@"Encryption starting");

[self sdl_sendEncryptionStartService];
}

- (void)didEnterStateEncryptionStopped {
SDLLogD(@"Encryption stopped");

[[NSNotificationCenter defaultCenter] postNotificationName:SDLEncryptionDidStopNotification object:nil];
}

#pragma mark - SDLProtocolListener
#pragma mark Encryption Start Service ACK

- (void)handleProtocolStartServiceACKMessage:(SDLProtocolMessage *)startServiceACK {
switch (startServiceACK.header.serviceType) {
case SDLServiceTypeRPC: {
[self sdl_handleEncryptionStartServiceAck:startServiceACK];
} break;
default: break;
}
}

- (void)sdl_handleEncryptionStartServiceAck:(SDLProtocolMessage *)encryptionStartServiceAck {
SDLLogD(@"Encryption service started");

[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateReady];
}

#pragma mark Encryption Start Service NAK

- (void)handleProtocolStartServiceNAKMessage:(SDLProtocolMessage *)startServiceNAK {
switch (startServiceNAK.header.serviceType) {
case SDLServiceTypeRPC: {
[self sdl_handleEncryptionStartServiceNak:startServiceNAK];
} break;
default: break;
}
}

- (void)sdl_handleEncryptionStartServiceNak:(SDLProtocolMessage *)audioStartServiceNak {
SDLLogW(@"Encryption service failed to start due to NAK");
[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStopped];
}

#pragma mark Encryption End Service

- (void)handleProtocolEndServiceACKMessage:(SDLProtocolMessage *)endServiceACK {
switch (endServiceACK.header.serviceType) {
case SDLServiceTypeRPC: {
SDLLogW(@"%@ service ended with end service ACK", (endServiceACK.header.serviceType == SDLServiceTypeRPC ? @"RPC Encryption" : @"RPC Encryption"));
[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStopped];
} break;
default: break;
}
}

- (void)handleProtocolEndServiceNAKMessage:(SDLProtocolMessage *)endServiceNAK {
switch (endServiceNAK.header.serviceType) {
case SDLServiceTypeRPC: {
SDLLogW(@"%@ service ended with end service NAK", (endServiceNAK.header.serviceType == SDLServiceTypeRPC ? @"RPC Encryption" : @"RPC Encryption"));
[self.encryptionStateMachine transitionToState:SDLEncryptionManagerStateStopped];
} break;
default: break;
}
}

#pragma mark - SDL RPC Notification callbacks
- (void)sdl_hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
NSAssert([notification.notification isKindOfClass:[SDLOnHMIStatus class]], @"A notification was sent with an unanticipated object");
if (![notification.notification isKindOfClass:[SDLOnHMIStatus class]]) {
return;
}

SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus*)notification.notification;
self.hmiLevel = hmiStatus.hmiLevel;

// if startWithProtocol has not been called yet, abort here
if (!self.protocol) { return; }

if (!self.isEncryptionReady) {
[self sdl_startEncryptionService];
}
}

@end
33 changes: 33 additions & 0 deletions SmartDeviceLink/SDLEncryptionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,43 @@

#import <Foundation/Foundation.h>

@class SDLProtocol;
@class SDLPermissionManager;
@protocol SDLConnectionManagerType;

NS_ASSUME_NONNULL_BEGIN

@interface SDLEncryptionManager : NSObject

- (instancetype)init NS_UNAVAILABLE;

/**
Create a new streaming media manager for navigation and VPM apps with a specified configuration
@param connectionManager The pass-through for RPCs
@param configuration This session's configuration
@return A new streaming manager
*/
- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager configuration:(SDLEncryptionConfiguration *)configuration permissionManager:(SDLPermissionManager *)permissionManager rpcOperationQueue:(NSOperationQueue *)rpcOperationQueue NS_DESIGNATED_INITIALIZER;

/**
* Start the manager with a completion block that will be called when startup completes. This is used internally. To use an SDLStreamingMediaManager, you should use the manager found on `SDLManager`.
*/
- (void)startWithProtocol:(SDLProtocol *)protocol;

/**
* Send an Encrypted RPC request and set a completion handler that will be called with the response when the response returns.
*
* @param request The RPC request to send
* @param handler The handler that will be called when the response returns
*/
- (void)sendEncryptedRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler NS_SWIFT_NAME(send(encryptedRequest:responseHandler:));

/**
* Stop the manager. This method is used internally.
*/
- (void)stop;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit eb6e81d

Please sign in to comment.