summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2021-09-22 09:29:20 -0400
committerGitHub <noreply@github.com>2021-09-22 09:29:20 -0400
commit4c1fe3f7b15c11763adc88b2078b609df068e031 (patch)
tree6c3ff179667bc30fd23d5e70c3208ab430c8d80f
parent40088891f16dd3c01074c02aa340ee5417c9a0f3 (diff)
parent0d18a560258c7079722b38ef731007705f688d90 (diff)
downloadsdl_ios-4c1fe3f7b15c11763adc88b2078b609df068e031.tar.gz
Merge pull request #2026 from smartdevicelink/bugfix/issue-2024-security-queries-are-not-implemented-to-spec
Security queries are not implemented to spec
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj28
-rw-r--r--SmartDeviceLink/private/SDLProtocol.m56
-rw-r--r--SmartDeviceLink/private/SDLSecurityQueryErrorCode.h63
-rw-r--r--SmartDeviceLink/private/SDLSecurityQueryErrorCode.m48
-rw-r--r--SmartDeviceLink/private/SDLSecurityQueryPayload.h83
-rw-r--r--SmartDeviceLink/private/SDLSecurityQueryPayload.m166
-rw-r--r--SmartDeviceLinkTests/RPCSpecs/PayloadSpecs/SDLSecurityQueryPayloadSpec.m80
7 files changed, 511 insertions, 13 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index c605c0c52..0f3a0641a 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -1736,6 +1736,8 @@
B3DF19F3251225AA0090D7BA /* TestSmartConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = B3DF19F1251225A90090D7BA /* TestSmartConnection.m */; };
B3EC9E6E2579AA010039F3AA /* SDLClimateDataSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3EC9E6D2579AA010039F3AA /* SDLClimateDataSpec.m */; };
B3F7918324E062C200DB5CAF /* SDLGetVehicleDataSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 162E824C1A9BDE8A00906325 /* SDLGetVehicleDataSpec.m */; };
+ C93193DC26B1B57C008203EC /* SDLSecurityQueryPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = C93193DA26B1B57B008203EC /* SDLSecurityQueryPayload.h */; };
+ C93193DD26B1B57C008203EC /* SDLSecurityQueryPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = C93193DB26B1B57B008203EC /* SDLSecurityQueryPayload.m */; };
C9707D1825DEE786009D00F2 /* NSArray+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9707D1625DEE786009D00F2 /* NSArray+Extensions.h */; };
C9707D1925DEE786009D00F2 /* NSArray+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9707D1725DEE786009D00F2 /* NSArray+Extensions.m */; };
C9707D3025E0444D009D00F2 /* SDLMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = C9707D2E25E0444D009D00F2 /* SDLMacros.h */; };
@@ -1745,6 +1747,9 @@
C971E3F02649D12D00FC24D6 /* NSMutableDictionary+StoreSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = BB3C600D221AEF37007DD4CA /* NSMutableDictionary+StoreSpec.m */; };
C975877F257AEFDB0066F271 /* SDLSeekIndicatorTypeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = C975877E257AEFDB0066F271 /* SDLSeekIndicatorTypeSpec.m */; };
C9758785257F4C570066F271 /* SDLSeekStreamingIndicatorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = C9758784257F4C570066F271 /* SDLSeekStreamingIndicatorSpec.m */; };
+ C99BE00926C53E7F00DB0B54 /* SDLSecurityQueryErrorCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C99BE00726C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.h */; };
+ C99BE00A26C53E7F00DB0B54 /* SDLSecurityQueryErrorCode.m in Sources */ = {isa = PBXBuildFile; fileRef = C99BE00826C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.m */; };
+ C99BE00D26C5B23000DB0B54 /* SDLSecurityQueryPayloadSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = C99BE00C26C5B23000DB0B54 /* SDLSecurityQueryPayloadSpec.m */; };
C9AA62C4261E5035000F49BC /* SDLVoiceCommandManagerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DF40B27208FDA9700DD6FDA /* SDLVoiceCommandManagerSpec.m */; };
C9DFFE78257ACE0000F7D57A /* SDLSeekStreamingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = C9DFFE76257ACE0000F7D57A /* SDLSeekStreamingIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; };
C9DFFE79257ACE0000F7D57A /* SDLSeekStreamingIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = C9DFFE77257ACE0000F7D57A /* SDLSeekStreamingIndicator.m */; };
@@ -3649,12 +3654,17 @@
B3DF19F2251225A90090D7BA /* TestSmartConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestSmartConnection.h; sourceTree = "<group>"; };
B3EC9E6D2579AA010039F3AA /* SDLClimateDataSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLClimateDataSpec.m; sourceTree = "<group>"; };
BB3C600D221AEF37007DD4CA /* NSMutableDictionary+StoreSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSMutableDictionary+StoreSpec.m"; path = "DevAPISpecs/NSMutableDictionary+StoreSpec.m"; sourceTree = "<group>"; };
+ C93193DA26B1B57B008203EC /* SDLSecurityQueryPayload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLSecurityQueryPayload.h; path = private/SDLSecurityQueryPayload.h; sourceTree = "<group>"; };
+ C93193DB26B1B57B008203EC /* SDLSecurityQueryPayload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLSecurityQueryPayload.m; path = private/SDLSecurityQueryPayload.m; sourceTree = "<group>"; };
C9707D1625DEE786009D00F2 /* NSArray+Extensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSArray+Extensions.h"; path = "private/NSArray+Extensions.h"; sourceTree = "<group>"; };
C9707D1725DEE786009D00F2 /* NSArray+Extensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSArray+Extensions.m"; path = "private/NSArray+Extensions.m"; sourceTree = "<group>"; };
C9707D2E25E0444D009D00F2 /* SDLMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMacros.h; path = private/SDLMacros.h; sourceTree = "<group>"; };
C9707D2F25E0444D009D00F2 /* SDLMacros.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMacros.m; path = private/SDLMacros.m; sourceTree = "<group>"; };
C975877E257AEFDB0066F271 /* SDLSeekIndicatorTypeSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSeekIndicatorTypeSpec.m; sourceTree = "<group>"; };
C9758784257F4C570066F271 /* SDLSeekStreamingIndicatorSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSeekStreamingIndicatorSpec.m; sourceTree = "<group>"; };
+ C99BE00726C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSecurityQueryErrorCode.h; path = private/SDLSecurityQueryErrorCode.h; sourceTree = "<group>"; };
+ C99BE00826C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSecurityQueryErrorCode.m; path = private/SDLSecurityQueryErrorCode.m; sourceTree = "<group>"; };
+ C99BE00C26C5B23000DB0B54 /* SDLSecurityQueryPayloadSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSecurityQueryPayloadSpec.m; sourceTree = "<group>"; };
C9DFFE76257ACE0000F7D57A /* SDLSeekStreamingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSeekStreamingIndicator.h; path = public/SDLSeekStreamingIndicator.h; sourceTree = "<group>"; };
C9DFFE77257ACE0000F7D57A /* SDLSeekStreamingIndicator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSeekStreamingIndicator.m; path = public/SDLSeekStreamingIndicator.m; sourceTree = "<group>"; };
C9DFFE7C257AD07E00F7D57A /* SDLSeekIndicatorType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSeekIndicatorType.h; path = public/SDLSeekIndicatorType.h; sourceTree = "<group>"; };
@@ -3972,6 +3982,7 @@
isa = PBXGroup;
children = (
162E823C1A9BDE8A00906325 /* SDLRPCPayloadSpec.m */,
+ C99BE00C26C5B23000DB0B54 /* SDLSecurityQueryPayloadSpec.m */,
);
path = PayloadSpecs;
sourceTree = "<group>";
@@ -5808,6 +5819,7 @@
5D5935011A851D7E00687FB9 /* Header */ = {
isa = PBXGroup;
children = (
+ C904F1DD26D919EF00E073DA /* SecurityQuery */,
4A8BD32824F9431B000945E3 /* SDLProtocolHeader.h */,
4A8BD32924F9431B000945E3 /* SDLProtocolHeader.m */,
4A8BD32724F9431B000945E3 /* SDLV1ProtocolHeader.h */,
@@ -7058,6 +7070,17 @@
name = Frameworks;
sourceTree = "<group>";
};
+ C904F1DD26D919EF00E073DA /* SecurityQuery */ = {
+ isa = PBXGroup;
+ children = (
+ C93193DA26B1B57B008203EC /* SDLSecurityQueryPayload.h */,
+ C93193DB26B1B57B008203EC /* SDLSecurityQueryPayload.m */,
+ C99BE00726C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.h */,
+ C99BE00826C53E7E00DB0B54 /* SDLSecurityQueryErrorCode.m */,
+ );
+ name = SecurityQuery;
+ sourceTree = "<group>";
+ };
DA1166D71D14601C00438CEA /* Touches */ = {
isa = PBXGroup;
children = (
@@ -7273,6 +7296,7 @@
4ABB28D524F82A6A0061BF55 /* SDLOnButtonEvent.h in Headers */,
4ABB2B7024F84FE50061BF55 /* SDLFuelRange.h in Headers */,
4ABB25D624F7E7630061BF55 /* SDLImageField+ScreenManagerExtensions.h in Headers */,
+ C99BE00926C53E7F00DB0B54 /* SDLSecurityQueryErrorCode.h in Headers */,
4ABB299324F845440061BF55 /* SDLSetAppIcon.h in Headers */,
4ABB2A3B24F847980061BF55 /* SDLDeleteInteractionChoiceSetResponse.h in Headers */,
4ABB275324F7FD9C0061BF55 /* SDLECallConfirmationStatus.h in Headers */,
@@ -7679,6 +7703,7 @@
4ABB2B5424F84EF50061BF55 /* SDLClimateControlData.h in Headers */,
4A8BD2B624F935BC000945E3 /* SDLSystemCapability.h in Headers */,
4ABB2AA624F847F40061BF55 /* SDLSetInteriorVehicleDataResponse.h in Headers */,
+ C93193DC26B1B57C008203EC /* SDLSecurityQueryPayload.h in Headers */,
4ABB264F24F7F5720061BF55 /* SDLConfiguration.h in Headers */,
4ABB252224F7E3FC0061BF55 /* SDLLifecycleSyncPDataHandler.h in Headers */,
4ABB29FD24F8477F0061BF55 /* SDLAlertResponse.h in Headers */,
@@ -8372,6 +8397,7 @@
4ABB2BA424F850AE0061BF55 /* SDLLightCapabilities.m in Sources */,
B3A9DB0625D497FB00CDFD21 /* SDLAppCapabilityType.m in Sources */,
4ABB272224F7FCAE0061BF55 /* SDLDefrostZone.m in Sources */,
+ C93193DD26B1B57C008203EC /* SDLSecurityQueryPayload.m in Sources */,
4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */,
4ABB25AA24F7E6E10061BF55 /* SDLMenuManager.m in Sources */,
4ABB25DD24F7E77C0061BF55 /* SDLScreenManager.m in Sources */,
@@ -8449,6 +8475,7 @@
4ABB282F24F824E70061BF55 /* SDLTextAlignment.m in Sources */,
4A8BD31124F938D6000945E3 /* SDLWeatherServiceManifest.m in Sources */,
4ABB271524F7FC4E0061BF55 /* SDLCompassDirection.m in Sources */,
+ C99BE00A26C53E7F00DB0B54 /* SDLSecurityQueryErrorCode.m in Sources */,
4ABB254424F7E48D0061BF55 /* SDLLockScreenRootViewController.m in Sources */,
4ABB265F24F7F5F20061BF55 /* SDLNotificationDispatcher.m in Sources */,
4A8BD31624F938D6000945E3 /* SDLWindowTypeCapabilities.m in Sources */,
@@ -8856,6 +8883,7 @@
1680B11D1A9CD7AD00DBD79E /* SDLProtocolMessageDisassemblerSpec.m in Sources */,
8BBEA6091F324832003EEA26 /* SDLMetadataTypeSpec.m in Sources */,
5DAD5F8B20508F140025624C /* SDLSoftButtonObjectSpec.m in Sources */,
+ C99BE00D26C5B23000DB0B54 /* SDLSecurityQueryPayloadSpec.m in Sources */,
DA9F7E9E1DCC05B900ACAE48 /* SDLWaypointTypeSpec.m in Sources */,
5D76E31C1D3805FF00647CFA /* SDLLockScreenManagerSpec.m in Sources */,
162E82DA1A9BDE8B00906325 /* SDLDriverDistractionStateSpec.m in Sources */,
diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m
index ea2cc84e0..f2ef868c5 100644
--- a/SmartDeviceLink/private/SDLProtocol.m
+++ b/SmartDeviceLink/private/SDLProtocol.m
@@ -28,6 +28,8 @@
#import "SDLRPCRequest.h"
#import "SDLRPCResponse.h"
#import "SDLSecurityType.h"
+#import "SDLSecurityQueryErrorCode.h"
+#import "SDLSecurityQueryPayload.h"
#import "SDLSystemInfo.h"
#import "SDLTimer.h"
#import "SDLVersion.h"
@@ -775,7 +777,42 @@ NS_ASSUME_NONNULL_BEGIN
// Misformatted handshake message, something went wrong
if (clientHandshakeMessage.payload.length <= 12) {
- SDLLogE(@"Security message is malformed, less than 12 bytes. It does not have a protocol header.");
+ SDLLogE(@"Security message is malformed, less than 12 bytes. It does not have a security payload header.");
+ }
+
+ // Check the client's message header for any internal errors
+ // NOTE: Before Core v8.0.0, all these messages will be notifications. In Core v8.0.0 and later, received messages will have the proper query type. Therefore, we cannot do things based only on the query type being request or response.
+ SDLSecurityQueryPayload *clientSecurityQueryPayload = [SDLSecurityQueryPayload securityPayloadWithData:clientHandshakeMessage.payload];
+ if (clientSecurityQueryPayload == nil) {
+ SDLLogE(@"Module Security Query could not convert to object.");
+ return;
+ }
+
+ // If the query is of type `Notification` and the id represents a client internal error, we abort the response message and the encryptionManager will not be in state ready.
+ if (clientSecurityQueryPayload.queryID == SDLSecurityQueryIdSendInternalError && clientSecurityQueryPayload.queryType == SDLSecurityQueryTypeNotification) {
+ NSError *jsonDecodeError = nil;
+ NSDictionary<NSString *, id> *securityQueryErrorDictionary = [NSJSONSerialization JSONObjectWithData:clientSecurityQueryPayload.jsonData options:kNilOptions error:&jsonDecodeError];
+ if (jsonDecodeError != nil) {
+ SDLLogE(@"Error decoding module security query response JSON: %@", jsonDecodeError);
+ } else {
+ if (securityQueryErrorDictionary[@"text"] != nil) {
+ SDLSecurityQueryErrorCode errorCodeString = [SDLSecurityQueryError convertErrorIdToStringEnum:securityQueryErrorDictionary[@"id"]];
+ SDLLogE(@"Security Query module internal error: %@, code: %@", securityQueryErrorDictionary[@"text"], errorCodeString);
+ } else {
+ SDLLogE(@"Security Query module error: No information provided");
+ }
+ }
+ return;
+ }
+
+ if (clientSecurityQueryPayload.queryID != SDLSecurityQueryIdSendHandshake) {
+ SDLLogE(@"Security Query module error: Message is not a SEND_HANDSHAKE_DATA REQUEST");
+ return;
+ }
+
+ if (clientSecurityQueryPayload.queryType == SDLSecurityQueryTypeResponse) {
+ SDLLogE(@"Security Query module error: Message is a response, which is not supported");
+ return;
}
// Tear off the binary header of the client protocol message to get at the actual TLS handshake
@@ -812,14 +849,10 @@ NS_ASSUME_NONNULL_BEGIN
serverMessageHeader.sessionID = clientHeader.sessionID;
serverMessageHeader.messageID = messageId;
- // For a control service packet, we need a binary header with a function ID corresponding to what type of packet we're sending.
- SDLRPCPayload *serverTLSPayload = [[SDLRPCPayload alloc] init];
- serverTLSPayload.functionID = 0x01; // TLS Handshake message
- serverTLSPayload.rpcType = 0x00;
- serverTLSPayload.correlationID = 0x00;
- serverTLSPayload.binaryData = data;
+ // Assemble a security query payload header for our response
+ SDLSecurityQueryPayload *serverTLSPayload = [[SDLSecurityQueryPayload alloc] initWithQueryType:SDLSecurityQueryTypeResponse queryID:SDLSecurityQueryIdSendHandshake sequenceNumber:0x00 jsonData:nil binaryData:data];
- NSData *binaryData = serverTLSPayload.data;
+ NSData *binaryData = [serverTLSPayload convertToData];
return [SDLProtocolMessage messageWithHeader:serverMessageHeader andPayload:binaryData];
}
@@ -835,12 +868,9 @@ NS_ASSUME_NONNULL_BEGIN
serverMessageHeader.messageID = messageId;
// For a control service packet, we need a binary header with a function ID corresponding to what type of packet we're sending.
- SDLRPCPayload *serverTLSPayload = [[SDLRPCPayload alloc] init];
- serverTLSPayload.functionID = 0x02; // TLS Error message
- serverTLSPayload.rpcType = 0x02;
- serverTLSPayload.correlationID = 0x00;
+ SDLSecurityQueryPayload *serverTLSPayload = [[SDLSecurityQueryPayload alloc] initWithQueryType:SDLSecurityQueryTypeNotification queryID:SDLSecurityQueryIdSendInternalError sequenceNumber:0x00 jsonData:nil binaryData:nil];
- NSData *binaryData = serverTLSPayload.data;
+ NSData *binaryData = [serverTLSPayload convertToData];
// TODO: (Joel F.)[2016-02-15] This is supposed to have some JSON data and json data size
return [SDLProtocolMessage messageWithHeader:serverMessageHeader andPayload:binaryData];
diff --git a/SmartDeviceLink/private/SDLSecurityQueryErrorCode.h b/SmartDeviceLink/private/SDLSecurityQueryErrorCode.h
new file mode 100644
index 000000000..7f516c7d3
--- /dev/null
+++ b/SmartDeviceLink/private/SDLSecurityQueryErrorCode.h
@@ -0,0 +1,63 @@
+//
+// SDLSecurityQueryErrorCode.h
+// SmartDeviceLink
+//
+// Created by Frank Elias on 8/12/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import "SDLEnum.h"
+
+typedef SDLEnum SDLSecurityQueryErrorCode NS_TYPED_ENUM;
+
+///Internal Security Manager value
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeSuccess;
+
+///Wrong size of query data
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidQuerySize;
+
+///Unknown Query ID
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidQueryID;
+
+///SDL does not support encryption
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeNotSupported;
+
+///Received request to protect a service that was protected before
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeServiceAlreadyProtected;
+
+///Received handshake or encrypted data for not protected service
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeServiceNotProtected;
+
+///Decryption failed
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeDecryptionFailed;
+
+///Encryption failed
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeEncryptionFailed;
+
+///SSL invalid data
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeSSLInvalidData;
+
+///In case of all other handshake errors
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeHandshakeFailed;
+
+///Handshake failed because certificate is invalid
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidCertificate;
+
+///Handshake failed because certificate is expired
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeExpiredCertificate;
+
+///Internal error
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInternal;
+
+///Error value for testing
+extern SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeUnknownInternalError;
+
+
+@interface SDLSecurityQueryError : NSObject
+
+/**
+ Compare the internal error ID with the App's security query error codes
+ */
++ (SDLSecurityQueryErrorCode)convertErrorIdToStringEnum:(NSNumber *)errorId;
+
+@end
diff --git a/SmartDeviceLink/private/SDLSecurityQueryErrorCode.m b/SmartDeviceLink/private/SDLSecurityQueryErrorCode.m
new file mode 100644
index 000000000..a1a55ea6c
--- /dev/null
+++ b/SmartDeviceLink/private/SDLSecurityQueryErrorCode.m
@@ -0,0 +1,48 @@
+//
+// SDLSecurityQueryErrorCode.m
+// SmartDeviceLink
+//
+// Created by Frank Elias on 8/12/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import "SDLSecurityQueryErrorCode.h"
+
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeSuccess = @"Success";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidQuerySize = @"Wrong size of query data";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidQueryID = @"Unknown Query ID";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeNotSupported = @"SDL does not support encryption";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeServiceAlreadyProtected = @"Received request to protect a service that was protected before";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeServiceNotProtected = @"Received handshake or encrypted data for not protected service";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeDecryptionFailed = @"Decryption failed";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeEncryptionFailed = @"Encryption failed";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeSSLInvalidData = @"SSL invalid data";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeHandshakeFailed = @"In case of all other handshake errors";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInvalidCertificate = @"Handshake failed because certificate is invalid";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeExpiredCertificate = @"Handshake failed because certificate is expired";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeInternal = @"Internal error";
+SDLSecurityQueryErrorCode const SDLSecurityQueryErrorCodeUnknownInternalError = @"Error value for testing";
+
+
+@implementation SDLSecurityQueryError
+
++ (SDLSecurityQueryErrorCode)convertErrorIdToStringEnum:(NSNumber *)errorId {
+ switch (errorId.unsignedIntegerValue) {
+ case 0x00: return SDLSecurityQueryErrorCodeSuccess;
+ case 0x01: return SDLSecurityQueryErrorCodeInvalidQuerySize;
+ case 0x02: return SDLSecurityQueryErrorCodeInvalidQueryID;
+ case 0x03: return SDLSecurityQueryErrorCodeNotSupported;
+ case 0x04: return SDLSecurityQueryErrorCodeServiceAlreadyProtected;
+ case 0x05: return SDLSecurityQueryErrorCodeServiceNotProtected;
+ case 0x06: return SDLSecurityQueryErrorCodeDecryptionFailed;
+ case 0x07: return SDLSecurityQueryErrorCodeEncryptionFailed;
+ case 0x08: return SDLSecurityQueryErrorCodeSSLInvalidData;
+ case 0x09: return SDLSecurityQueryErrorCodeHandshakeFailed;
+ case 0x0A: return SDLSecurityQueryErrorCodeInvalidCertificate;
+ case 0x0B: return SDLSecurityQueryErrorCodeExpiredCertificate;
+ case 0xFF: return SDLSecurityQueryErrorCodeInternal;
+ default: return SDLSecurityQueryErrorCodeUnknownInternalError;
+ }
+}
+
+@end
diff --git a/SmartDeviceLink/private/SDLSecurityQueryPayload.h b/SmartDeviceLink/private/SDLSecurityQueryPayload.h
new file mode 100644
index 000000000..27354a47c
--- /dev/null
+++ b/SmartDeviceLink/private/SDLSecurityQueryPayload.h
@@ -0,0 +1,83 @@
+//
+// SDLSecurityQueryPayload.h
+// SmartDeviceLink
+//
+// Created by Frank Elias on 7/28/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "SDLRPCMessageType.h"
+
+/// Enum for different SDL security query types
+typedef NS_ENUM(Byte, SDLSecurityQueryType) {
+ /// A request that will require a response
+ SDLSecurityQueryTypeRequest = 0x00,
+
+ /// A response to a request
+ SDLSecurityQueryTypeResponse = 0x10,
+
+ /// A message that does not have a response
+ SDLSecurityQueryTypeNotification = 0x20,
+
+ /// An invalid query Type
+ SDLSecurityQueryTypeInvalid = 0xFF
+};
+
+/// Enum for each type of SDL security query IDs
+typedef NS_ENUM(UInt32, SDLSecurityQueryId) {
+ /// Send handshake data
+ SDLSecurityQueryIdSendHandshake = 0x000001,
+
+ /// Send internal error
+ SDLSecurityQueryIdSendInternalError = 0x000002,
+
+ /// Invalid query id
+ SDLSecurityQueryIdInvalid = 0xFFFFFF
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLSecurityQueryPayload : NSObject
+
+/// The security query's type, could be of type request, response, or notification
+@property (assign, nonatomic) SDLSecurityQueryType queryType;
+
+/// The security query's ID.
+@property (assign, nonatomic) SDLSecurityQueryId queryID;
+
+/// The message ID is set by the Mobile libraries to track security messages.
+@property (assign, nonatomic) UInt32 sequenceNumber;
+
+/// The JSON data following the binary query header.
+@property (nullable, strong, nonatomic) NSData *jsonData;
+
+/// The binary data that is after the header (96 bits) and the JSON data.
+@property (nullable, strong, nonatomic) NSData *binaryData;
+
+/// Create a security query object from raw data
+/// @param data The data to convert into an SDLSecurityQueryPayload object
+/// @return The SDLSecurityQueryPayload object, or nil if the data is malformed
+- (nullable instancetype)initWithData:(NSData *)data;
+
+/// Create a security query object from security query properties
+/// @param queryType The security query type to be sent
+/// @param queryID The security query ID
+/// @param sequenceNumber The security query sequence number
+/// @param jsonData The JSON data to be set in the security query
+/// @param binaryData The binary data that's after the header and the JSON data
+/// @return The SDLSecurityQueryPayload non-nullable object
+- (instancetype)initWithQueryType:(SDLSecurityQueryType)queryType queryID:(SDLSecurityQueryId)queryID sequenceNumber:(UInt32)sequenceNumber jsonData:(nullable NSData *)jsonData binaryData:(nullable NSData *)binaryData;
+
+/// Create a security query object from raw data
+/// @param data The data to convert into an SDLSecurityQueryPayload object
+/// @return The SDLSecurityQueryPayload object, or nil if the data is malformed
++ (nullable id)securityPayloadWithData:(NSData *)data;
+
+/// Convert the object into raw NSData
+/// @return The raw NSData of the object
+- (NSData *)convertToData;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/private/SDLSecurityQueryPayload.m b/SmartDeviceLink/private/SDLSecurityQueryPayload.m
new file mode 100644
index 000000000..744e1770a
--- /dev/null
+++ b/SmartDeviceLink/private/SDLSecurityQueryPayload.m
@@ -0,0 +1,166 @@
+//
+// SDLSecurityQueryPayload.m
+// SmartDeviceLink
+//
+// Created by Frank Elias on 7/28/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import "SDLSecurityQueryPayload.h"
+
+#import "SDLLogMacros.h"
+
+const NSUInteger SECURITY_QUERY_HEADER_SIZE = 12;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation SDLSecurityQueryPayload
+
+- (nullable instancetype)initWithData:(NSData *)data {
+ if (data == nil || data.length < SECURITY_QUERY_HEADER_SIZE) {
+ SDLLogE(@"Security Payload error: not enough data to form Security Query header. Data length: %lu", (unsigned long)data.length);
+ return nil;
+ }
+
+ self = [super init];
+ if (!self) { return nil; }
+
+ @try {
+ // Setup our pointers for data access
+ Byte *bytePointer = (UInt8 *)data.bytes;
+ UInt32 *ui32Pointer = (UInt32 *)data.bytes;
+
+ // Extract the parts
+ UInt8 queryType = bytePointer[0];
+
+ self.queryType = queryType;
+
+ // Extract the 24 bit query ID in the last 24 bits of the first 32 bits.
+ UInt32 queryID = ui32Pointer[0];
+ queryID = CFSwapInt32BigToHost(queryID) & 0x00FFFFFF;
+ self.queryID = queryID;
+
+ // Extract the 32 bit sequence number from the data after the first 32 bits.
+ UInt32 sequenceNumber = ui32Pointer[1];
+ sequenceNumber = CFSwapInt32BigToHost(sequenceNumber);
+ self.sequenceNumber = sequenceNumber;
+
+ // Extract the 32 bit json data size from the data after the first 64 bits
+ UInt32 jsonDataSize = ui32Pointer[2];
+ jsonDataSize = CFSwapInt32BigToHost(jsonDataSize);
+
+ // Extract the JSON data after the header (96 bits) based on the JSON data size
+ NSData *jsonData = nil;
+ NSUInteger offsetOfJSONData = SECURITY_QUERY_HEADER_SIZE;
+ if (jsonDataSize > 0 && jsonDataSize <= (data.length - SECURITY_QUERY_HEADER_SIZE)) {
+ jsonData = [data subdataWithRange:NSMakeRange(offsetOfJSONData, jsonDataSize)];
+ }
+ self.jsonData = jsonData;
+
+ // Extract the binary data after the header (96 bits) and the JSON data to the end
+ NSData *binaryData = nil;
+ NSUInteger offsetOfBinaryData = SECURITY_QUERY_HEADER_SIZE + jsonDataSize;
+ NSUInteger binaryDataSize = data.length - jsonDataSize - SECURITY_QUERY_HEADER_SIZE;
+ if (binaryDataSize > 0) {
+ binaryData = [data subdataWithRange:NSMakeRange(offsetOfBinaryData, binaryDataSize)];
+ }
+ self.binaryData = binaryData;
+
+ } @catch (NSException *e) {
+ SDLLogE(@"SDLSecurityQueryPayload init error: %@", e);
+ return nil;
+ }
+
+ return self;
+}
+
+- (instancetype)initWithQueryType:(SDLSecurityQueryType)queryType queryID:(SDLSecurityQueryId)queryID sequenceNumber:(UInt32)sequenceNumber jsonData:(nullable NSData *)jsonData binaryData:(nullable NSData *)binaryData {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _queryType = queryType;
+ _queryID = queryID;
+ _sequenceNumber = sequenceNumber;
+ _jsonData = jsonData;
+ _binaryData = binaryData;
+
+ return self;
+}
+
++ (nullable id)securityPayloadWithData:(NSData *)data {
+ return [[SDLSecurityQueryPayload alloc] initWithData:data];
+}
+
+- (NSData *)convertToData {
+ // From the properties, create a data buffer
+ // Query Type - first 8 bits
+ // Query ID - next 24 bits
+ // Sequence Number - next 32 bits
+ // JSON size - next 32 bits
+ UInt8 headerBuffer[SECURITY_QUERY_HEADER_SIZE];
+ *(UInt32 *)&headerBuffer[0] = CFSwapInt32HostToBig(self.queryID);
+ *(UInt32 *)&headerBuffer[4] = CFSwapInt32HostToBig(self.sequenceNumber);
+ *(UInt32 *)&headerBuffer[8] = CFSwapInt32HostToBig((UInt32)self.jsonData.length);
+ headerBuffer[0] &= 0xFF;
+ headerBuffer[0] |= self.queryType;
+
+ // Serialize the header. Append the json data, then the binary data
+ NSUInteger jsonDataSize = self.jsonData.length;
+ NSUInteger binaryDataSize = self.binaryData.length;
+ NSUInteger size = SECURITY_QUERY_HEADER_SIZE + jsonDataSize + binaryDataSize;
+ NSMutableData *dataOut = [NSMutableData dataWithCapacity:size];
+ [dataOut appendBytes:&headerBuffer length:12];
+ [dataOut appendData:self.jsonData];
+ [dataOut appendData:self.binaryData];
+
+ return dataOut;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"Security Query Header: %@, %@, sequenceNumber: %lu, json size: %lu bytes, binary size: %lu bytes", [self descriptionForQueryID], [self descriptionForQueryType], (unsigned long)self.sequenceNumber, (unsigned long)self.jsonData.length, (unsigned long)self.binaryData.length];
+}
+
+- (NSString *)descriptionForQueryID {
+ NSString *queryIdDescription;
+ switch (self.queryID) {
+ case SDLSecurityQueryIdSendHandshake:
+ queryIdDescription = @"Send Handshake Data";
+ break;
+ case SDLSecurityQueryIdSendInternalError:
+ queryIdDescription = @"Send Internal Error";
+ break;
+ case SDLSecurityQueryIdInvalid:
+ queryIdDescription = @"Invalid Query ID";
+ break;
+ default:
+ queryIdDescription = @"Unknown Query ID";
+ break;
+ }
+ return [NSString stringWithFormat:@"queryID: %lu - %@", (unsigned long)self.queryID, queryIdDescription];
+}
+
+- (NSString *)descriptionForQueryType {
+ NSString *queryTypeDescription;
+ switch (self.queryType) {
+ case SDLSecurityQueryTypeRequest:
+ queryTypeDescription = @"Request";
+ break;
+ case SDLSecurityQueryTypeResponse:
+ queryTypeDescription = @"Response";
+ break;
+ case SDLSecurityQueryTypeNotification:
+ queryTypeDescription = @"Notification";
+ break;
+ case SDLSecurityQueryTypeInvalid:
+ queryTypeDescription = @"Invalid Query Type";
+ break;
+ default:
+ queryTypeDescription = @"Unknown Query Type";
+ break;
+ }
+ return [NSString stringWithFormat:@"queryType: %lu - %@", (unsigned long)self.queryType, queryTypeDescription];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLinkTests/RPCSpecs/PayloadSpecs/SDLSecurityQueryPayloadSpec.m b/SmartDeviceLinkTests/RPCSpecs/PayloadSpecs/SDLSecurityQueryPayloadSpec.m
new file mode 100644
index 000000000..5ebe9ce28
--- /dev/null
+++ b/SmartDeviceLinkTests/RPCSpecs/PayloadSpecs/SDLSecurityQueryPayloadSpec.m
@@ -0,0 +1,80 @@
+//
+// SDLSecurityQueryPayloadSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Frank Elias on 8/12/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+
+#import <Foundation/Foundation.h>
+
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+#import "SDLRPCParameterNames.h"
+#import "SDLRPCFunctionNames.h"
+#import "SDLSecurityQueryPayload.h"
+#import "SDLSecurityQueryErrorCode.h"
+
+QuickSpecBegin(SDLSecurityQueryPayloadSpec)
+
+__block SDLSecurityQueryPayload* testPayload;
+__block NSDictionary* dict = @{@"id": @"3", @"text": @"SDL does not support encryption"};
+
+NSData* (^testData)(void) = ^NSData* {
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:0];
+ NSData* binaryData = [NSData dataWithBytes:"PrimitiveString" length:strlen("PrimitiveString")];
+
+ Byte header[12] = {0x20, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0x43, 0x00, 0x00, 0x00, 0x00};
+ *(UInt32 *)&header[8] = CFSwapInt32HostToBig((unsigned int)jsonData.length);
+
+ NSMutableData *data = [NSMutableData dataWithCapacity:12 + jsonData.length];
+ [data appendBytes:&header length:12];
+ [data appendData:jsonData];
+ [data appendData:binaryData];
+
+ return data;
+};
+
+beforeSuite(^{
+ testPayload = [[SDLSecurityQueryPayload alloc] init];
+
+ testPayload.queryType = 0x20;
+ testPayload.queryID = 0x02;
+ testPayload.sequenceNumber = 0x1443;
+ testPayload.jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:0];
+ testPayload.binaryData = [NSData dataWithBytes:"PrimitiveString" length:strlen("PrimitiveString")];
+});
+
+describe(@"Getter/Setter Tests", ^ {
+ it(@"should set and get correctly", ^ {
+ expect(@(testPayload.queryType)).to(equal(SDLSecurityQueryTypeNotification));
+ expect(@(testPayload.queryID)).to(equal(SDLSecurityQueryIdSendInternalError));
+ expect(@(testPayload.sequenceNumber)).to(equal(@0x1443));
+ expect([NSJSONSerialization JSONObjectWithData:testPayload.jsonData options:0 error:0]).to(equal(dict));
+ expect([NSString stringWithUTF8String:[testPayload binaryData].bytes]).to(equal(@"PrimitiveString"));
+ });
+});
+
+describe(@"Data Tests", ^ {
+ it(@"should convert to byte data correctly", ^ {
+ expect(testPayload.convertToData).to(equal(testData()));
+ });
+});
+
+describe(@"RPCPayloadWithData Test", ^ {
+ it(@"should convert from byte data correctly", ^ {
+ SDLSecurityQueryPayload* constructedPayload = [SDLSecurityQueryPayload securityPayloadWithData:testData()];
+
+ expect(@(constructedPayload.queryType)).to(equal(SDLSecurityQueryTypeNotification));
+ expect(@(constructedPayload.queryID)).to(equal(SDLSecurityQueryIdSendInternalError));
+ expect(@(constructedPayload.sequenceNumber)).to(equal(@0x1443));
+ NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:constructedPayload.jsonData options:0 error:0];
+ expect(jsonDict).to(equal(dict));
+ expect(jsonDict[@"text"]).to(equal(SDLSecurityQueryErrorCodeNotSupported));
+ expect([NSString stringWithUTF8String:[constructedPayload binaryData].bytes]).to(equal(@"PrimitiveString"));
+ });
+});
+
+QuickSpecEnd