From 3025be0b3b040aa94c931114cbe1890887990c31 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 31 Mar 2021 15:19:02 -0400 Subject: [PATCH 01/19] Support TLS max size when encrypted * See https://github.com/smartdevicelink/sdl_java_suite/pull/1644/ --- SmartDeviceLink/private/SDLProtocol.m | 8 +++++--- .../private/SDLProtocolMessageDisassembler.m | 15 +++++---------- SmartDeviceLink/public/SDLProtocolConstants.h | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 348543801..f3f5eb62c 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -39,6 +39,8 @@ typedef NSNumber SDLServiceTypeBox; +static NSUInteger TLSMaxDataToEncryptSize = 16384 /*TLS Max Record Size*/ - 5 /*TLS Record Header Size*/ - 32 /*TLS MES Auth CDE Size*/ - 256 /*TLS Max Record Padding Size*/; + NS_ASSUME_NONNULL_BEGIN @interface SDLProtocol () { @@ -340,7 +342,6 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr // TODO: (Joel F.)[2016-02-09] We should assert if the service isn't setup for encryption. See [#350](https://github.com/smartdevicelink/sdl_ios/issues/350) if (encryption) { NSError *encryptError = nil; - messagePayload = [self.securityManager encryptData:rpcPayload.data withError:&encryptError]; if (encryptError) { @@ -376,11 +377,12 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr SDLProtocolMessage *protocolMessage = [SDLProtocolMessage messageWithHeader:header andPayload:messagePayload]; // See if the message is small enough to send in one transmission. If not, break it up into smaller messages and send. - if (protocolMessage.size < [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]) { + NSUInteger mtuSize = (encryption ? TLSMaxDataToEncryptSize : [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]); + if (protocolMessage.size < mtuSize) { SDLLogV(@"Sending protocol message: %@", protocolMessage); [self sdl_sendDataToTransport:protocolMessage.data onService:SDLServiceTypeRPC]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withLimit:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]]; + NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withLimit:mtuSize]; for (SDLProtocolMessage *smallerMessage in messages) { SDLLogV(@"Sending protocol message: %@", smallerMessage); [self sdl_sendDataToTransport:smallerMessage.data onService:SDLServiceTypeRPC]; diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m index 21ab55f63..c538eb689 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m @@ -20,9 +20,7 @@ @implementation SDLProtocolMessageDisassembler // How big is the message header? NSUInteger headerSize = incomingMessage.header.size; - // The size of the message is too big to send in one chunk. - // So lets break it up. - // Just how big IS this message? + // The size of the message is too big to send in one chunk. So let's break it up. Just how big IS this message? NSUInteger incomingPayloadSize = (incomingMessage.data.length - headerSize); // How many messages do we need to create to hold that many bytes? @@ -35,10 +33,10 @@ @implementation SDLProtocolMessageDisassembler // Create the outgoing array to hold the messages we will create. NSMutableArray *outgoingMessages = [NSMutableArray arrayWithCapacity:numberOfMessagesRequired + 1]; - - // Create the first message + // Create the first message, which cannot be encrypted because it needs to be exactly 8 bytes SDLProtocolHeader *firstFrameHeader = [incomingMessage.header copy]; firstFrameHeader.frameType = SDLFrameTypeFirst; + firstFrameHeader.encrypted = NO; UInt32 payloadData[2]; payloadData[0] = CFSwapInt32HostToBig((UInt32)incomingMessage.payload.length); @@ -46,13 +44,11 @@ @implementation SDLProtocolMessageDisassembler NSMutableData *firstFramePayload = [NSMutableData dataWithBytes:payloadData length:sizeof(payloadData)]; SDLProtocolMessage *firstMessage = [SDLProtocolMessage messageWithHeader:firstFrameHeader andPayload:firstFramePayload]; - outgoingMessages[0] = firstMessage; - + [outgoingMessages addObject:firstMessage]; // Create the middle messages (the ones carrying the actual data). for (NSUInteger n = 0; n < numberOfMessagesRequired - 1; n++) { - // Frame # after 255 must cycle back to 1, not 0. - // A 0 signals last frame. + // Frame # after 255 must cycle back to 1, not 0. A 0 signals last frame (SDLFrameInfoConsecutiveLastFrame). UInt8 frameNumber = (n % 255) + 1; SDLProtocolHeader *nextFrameHeader = [incomingMessage.header copy]; @@ -66,7 +62,6 @@ @implementation SDLProtocolMessageDisassembler outgoingMessages[n + 1] = nextMessage; } - // Create the last message SDLProtocolHeader *lastFrameHeader = [incomingMessage.header copy]; lastFrameHeader.frameType = SDLFrameTypeConsecutive; diff --git a/SmartDeviceLink/public/SDLProtocolConstants.h b/SmartDeviceLink/public/SDLProtocolConstants.h index 6da05c02e..b994ef437 100644 --- a/SmartDeviceLink/public/SDLProtocolConstants.h +++ b/SmartDeviceLink/public/SDLProtocolConstants.h @@ -94,5 +94,5 @@ typedef NS_ENUM(UInt8, SDLFrameInfo) { SDLFrameInfoFirstFrame = 0x00, // If frameType == First (0x02) /// Frame in a multiple frame payload. - SDLFrameInfoConsecutiveLastFrame = 0x00 // If frametype == Consecutive (0x03) + SDLFrameInfoConsecutiveLastFrame = 0x00 // If frameType == Consecutive (0x03) }; From 50882b44677ee7826dd2703f2ed6c4e4afb14513 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 31 Mar 2021 15:50:55 -0400 Subject: [PATCH 02/19] Move constant --- SmartDeviceLink/private/SDLProtocol.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index f3f5eb62c..f89040176 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -33,14 +33,12 @@ #import "SDLV2ProtocolHeader.h" NSString *const SDLProtocolSecurityErrorDomain = @"com.sdl.protocol.security"; - +static const NSUInteger TLSMaxDataToEncryptSize = 16384 /*TLS Max Record Size*/ - 5 /*TLS Record Header Size*/ - 32 /*TLS MES Auth CDE Size*/ - 256 /*TLS Max Record Padding Size*/; #pragma mark - SDLProtocol Private Interface typedef NSNumber SDLServiceTypeBox; -static NSUInteger TLSMaxDataToEncryptSize = 16384 /*TLS Max Record Size*/ - 5 /*TLS Record Header Size*/ - 32 /*TLS MES Auth CDE Size*/ - 256 /*TLS Max Record Padding Size*/; - NS_ASSUME_NONNULL_BEGIN @interface SDLProtocol () { From 65fdd9178f67fb80125039a1610bff992203b118 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 1 Apr 2021 09:01:40 -0400 Subject: [PATCH 03/19] Cleanup some comments --- .../private/SDLProtocolMessageDisassembler.m | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m index c538eb689..a1ba3637e 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m @@ -17,15 +17,13 @@ @implementation SDLProtocolMessageDisassembler // What message IDs does the current system use? Same messageIDs? Same CorrelationIDs? // What gets simply copied from incoming header to created headers; and what needs adjustment? - // How big is the message header? + // How big is the message header and payload? NSUInteger headerSize = incomingMessage.header.size; - - // The size of the message is too big to send in one chunk. So let's break it up. Just how big IS this message? - NSUInteger incomingPayloadSize = (incomingMessage.data.length - headerSize); + NSUInteger payloadSize = (incomingMessage.data.length - headerSize); // How many messages do we need to create to hold that many bytes? - // Note: this does NOT count the special first message which acts as a descriptor. - NSUInteger numberOfMessagesRequired = (NSUInteger)ceil((float)incomingPayloadSize / (float)(mtu - headerSize)); + // Note: This does NOT count the special first message which acts as a descriptor. + NSUInteger numberOfMessagesRequired = (NSUInteger)ceilf((float)payloadSize / (float)(mtu - headerSize)); // And how many data bytes go in each message? NSUInteger numberOfDataBytesPerMessage = mtu - headerSize; From 234a40f7e9ad1b61d4c19ff39ca725a4efa93871 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 1 Apr 2021 09:54:11 -0400 Subject: [PATCH 04/19] Updates to protocol message disassembler * Update naming for clarity * Update comments * Update tests --- SmartDeviceLink/private/SDLProtocol.m | 4 +- .../private/SDLProtocolMessageDisassembler.h | 6 +- .../private/SDLProtocolMessageDisassembler.m | 29 ++- .../SDLProtocolMessageDisassemblerSpec.m | 166 ++++++++++-------- 4 files changed, 117 insertions(+), 88 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index f89040176..ae859589b 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -380,7 +380,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr SDLLogV(@"Sending protocol message: %@", protocolMessage); [self sdl_sendDataToTransport:protocolMessage.data onService:SDLServiceTypeRPC]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withLimit:mtuSize]; + NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withPayloadSizeLimit:mtuSize]; for (SDLProtocolMessage *smallerMessage in messages) { SDLLogV(@"Sending protocol message: %@", smallerMessage); [self sdl_sendDataToTransport:smallerMessage.data onService:SDLServiceTypeRPC]; @@ -431,7 +431,7 @@ - (void)sdl_sendRawData:(NSData *)data onService:(SDLServiceType)service encrypt SDLLogV(@"Sending protocol message: %@", message); [self sdl_sendDataToTransport:message.data onService:header.serviceType]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:message withLimit:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:service]]; + NSArray *messages = [SDLProtocolMessageDisassembler disassemble:message withPayloadSizeLimit:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:service]]; for (SDLProtocolMessage *smallerMessage in messages) { SDLLogV(@"Sending protocol message: %@", smallerMessage); [self sdl_sendDataToTransport:smallerMessage.data onService:header.serviceType]; diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h index 644b1c1de..fd8b939a5 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h @@ -9,7 +9,11 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLProtocolMessageDisassembler : NSObject -+ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withLimit:(NSUInteger)mtu; +/// Use to break up a large message into a sequence of smaller messages, each of which is less than 'mtu' number of bytes total size. +/// +/// @param message The message to break up +/// @param mtu The MTU size to use to determine where to break up the message payload ++ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withPayloadSizeLimit:(NSUInteger)mtu; @end diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m index a1ba3637e..25384b01c 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m @@ -9,17 +9,14 @@ @implementation SDLProtocolMessageDisassembler - -// Use to break up a large message into a sequence of smaller messages, -// each of which is less than 'mtu' number of bytes total size. -+ (NSArray *)disassemble:(SDLProtocolMessage *)incomingMessage withLimit:(NSUInteger)mtu { - // Questions: - // What message IDs does the current system use? Same messageIDs? Same CorrelationIDs? - // What gets simply copied from incoming header to created headers; and what needs adjustment? ++ (NSArray *)disassemble:(SDLProtocolMessage *)message withPayloadSizeLimit:(NSUInteger)mtu { + if (message.size < mtu) { + return @[message]; + } // How big is the message header and payload? - NSUInteger headerSize = incomingMessage.header.size; - NSUInteger payloadSize = (incomingMessage.data.length - headerSize); + NSUInteger headerSize = message.header.size; + NSUInteger payloadSize = (message.data.length - headerSize); // How many messages do we need to create to hold that many bytes? // Note: This does NOT count the special first message which acts as a descriptor. @@ -32,12 +29,12 @@ @implementation SDLProtocolMessageDisassembler NSMutableArray *outgoingMessages = [NSMutableArray arrayWithCapacity:numberOfMessagesRequired + 1]; // Create the first message, which cannot be encrypted because it needs to be exactly 8 bytes - SDLProtocolHeader *firstFrameHeader = [incomingMessage.header copy]; + SDLProtocolHeader *firstFrameHeader = [message.header copy]; firstFrameHeader.frameType = SDLFrameTypeFirst; firstFrameHeader.encrypted = NO; UInt32 payloadData[2]; - payloadData[0] = CFSwapInt32HostToBig((UInt32)incomingMessage.payload.length); + payloadData[0] = CFSwapInt32HostToBig((UInt32)message.payload.length); payloadData[1] = CFSwapInt32HostToBig((UInt32)numberOfMessagesRequired); NSMutableData *firstFramePayload = [NSMutableData dataWithBytes:payloadData length:sizeof(payloadData)]; @@ -49,27 +46,27 @@ @implementation SDLProtocolMessageDisassembler // Frame # after 255 must cycle back to 1, not 0. A 0 signals last frame (SDLFrameInfoConsecutiveLastFrame). UInt8 frameNumber = (n % 255) + 1; - SDLProtocolHeader *nextFrameHeader = [incomingMessage.header copy]; + SDLProtocolHeader *nextFrameHeader = [message.header copy]; nextFrameHeader.frameType = SDLFrameTypeConsecutive; nextFrameHeader.frameData = frameNumber; NSUInteger offsetOfDataForThisFrame = headerSize + (n * numberOfDataBytesPerMessage); - NSData *nextFramePayload = [incomingMessage.data subdataWithRange:NSMakeRange(offsetOfDataForThisFrame, numberOfDataBytesPerMessage)]; + NSData *nextFramePayload = [message.data subdataWithRange:NSMakeRange(offsetOfDataForThisFrame, numberOfDataBytesPerMessage)]; SDLProtocolMessage *nextMessage = [SDLProtocolMessage messageWithHeader:nextFrameHeader andPayload:nextFramePayload]; outgoingMessages[n + 1] = nextMessage; } // Create the last message - SDLProtocolHeader *lastFrameHeader = [incomingMessage.header copy]; + SDLProtocolHeader *lastFrameHeader = [message.header copy]; lastFrameHeader.frameType = SDLFrameTypeConsecutive; lastFrameHeader.frameData = SDLFrameInfoConsecutiveLastFrame; NSUInteger numberOfMessagesCreatedSoFar = numberOfMessagesRequired - 1; NSUInteger numberOfDataBytesSentSoFar = numberOfMessagesCreatedSoFar * numberOfDataBytesPerMessage; - NSUInteger numberOfDataBytesInLastMessage = incomingPayloadSize - numberOfDataBytesSentSoFar; + NSUInteger numberOfDataBytesInLastMessage = payloadSize - numberOfDataBytesSentSoFar; NSUInteger offsetOfDataForLastFrame = headerSize + numberOfDataBytesSentSoFar; - NSData *lastFramePayload = [incomingMessage.data subdataWithRange:NSMakeRange(offsetOfDataForLastFrame, numberOfDataBytesInLastMessage)]; + NSData *lastFramePayload = [message.data subdataWithRange:NSMakeRange(offsetOfDataForLastFrame, numberOfDataBytesInLastMessage)]; SDLProtocolMessage *lastMessage = [SDLProtocolMessage messageWithHeader:lastFrameHeader andPayload:lastFramePayload]; outgoingMessages[numberOfMessagesRequired] = lastMessage; diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m index 9c211ba05..94c5121e7 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m @@ -18,78 +18,106 @@ QuickSpecBegin(SDLProtocolMessageDisassemblerSpec) -describe(@"Disassemble Tests", ^ { - it(@"Should assemble the message properly", ^ { - //Allocate 2000 bytes, and use it as sample data - const NSUInteger dataLength = 2000; - char dummyBytes[dataLength]; - - SDLGlobals *globals = [[SDLGlobals alloc] init]; - globals.maxHeadUnitProtocolVersion = [SDLVersion versionWithString:@"2.0.0"]; - - const char testPayloadHeader[12] = {0x20, 0x55, 0x64, 0x73, 0x12, 0x34, 0x43, 0x21, (dataLength >> 24) & 0xFF, (dataLength >> 16) & 0xFF, (dataLength >> 8) & 0xFF, dataLength & 0xFF}; - - NSMutableData* payloadData = [NSMutableData dataWithBytes:testPayloadHeader length:12]; - [payloadData appendBytes:dummyBytes length:dataLength]; - - SDLV2ProtocolMessage* testMessage = [[SDLV2ProtocolMessage alloc] init]; - SDLV2ProtocolHeader* testHeader = [[SDLV2ProtocolHeader alloc] init]; - - testHeader.frameType = SDLFrameTypeSingle; - testHeader.serviceType = SDLServiceTypeBulkData; - testHeader.frameData = SDLFrameInfoSingleFrame; - testHeader.sessionID = 0x84; - testHeader.bytesInPayload = (UInt32)payloadData.length; - - testMessage.header = testHeader; - testMessage.payload = payloadData; - - NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withLimit:[globals mtuSizeForServiceType:testHeader.serviceType]]; - - //Payload length per message - UInt32 payloadLength = 1012; // v1/2 MTU(1024) - header length(12) - - const char firstPayload[8] = {(payloadData.length >> 24) & 0xFF, (payloadData.length >> 16) & 0xFF, (payloadData.length >> 8) & 0xFF, payloadData.length & 0xFF, 0x00, 0x00, 0x00, ceil(1.0 * payloadData.length / payloadLength)}; - - SDLProtocolMessage* message = messageList[0]; - - //First frame - expect(message.payload).to(equal([NSData dataWithBytes:firstPayload length:8])); - - expect(@(message.header.frameType)).to(equal(@(SDLFrameTypeFirst))); - expect(@(message.header.serviceType)).to(equal(@(SDLServiceTypeBulkData))); - expect(@(message.header.frameData)).to(equal(@(SDLFrameInfoFirstFrame))); - expect(@(message.header.sessionID)).to(equal(@0x84)); - expect(@(message.header.bytesInPayload)).to(equal(@8)); - - NSUInteger offset = 0; - for (int i = 1; i < messageList.count - 1; i++) { - message = messageList[i]; - - //Consecutive frames - expect(message.payload).to(equal([NSData dataWithData:[payloadData subdataWithRange:NSMakeRange(offset, payloadLength)]])); - +describe(@"SDLProtocolMessageDisassembler Tests", ^ { + context(@"when the MTU size is larger than the payload size", ^{ + it(@"should disassemble the message properly", ^{ + const NSUInteger dataLength = 400; + char dummyBytes[dataLength]; + + const char testPayloadHeader[12] = {0x20, 0x55, 0x64, 0x73, 0x12, 0x34, 0x43, 0x21, (dataLength >> 24) & 0xFF, (dataLength >> 16) & 0xFF, (dataLength >> 8) & 0xFF, dataLength & 0xFF}; + + NSMutableData* payloadData = [NSMutableData dataWithBytes:testPayloadHeader length:12]; + [payloadData appendBytes:dummyBytes length:dataLength]; + + SDLV2ProtocolMessage *testMessage = [[SDLV2ProtocolMessage alloc] init]; + SDLV2ProtocolHeader *testHeader = [[SDLV2ProtocolHeader alloc] init]; + + testHeader.frameType = SDLFrameTypeSingle; + testHeader.serviceType = SDLServiceTypeBulkData; + testHeader.frameData = SDLFrameInfoSingleFrame; + testHeader.sessionID = 0x84; + testHeader.bytesInPayload = (UInt32)payloadData.length; + + testMessage.header = testHeader; + testMessage.payload = payloadData; + + NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withPayloadSizeLimit:1024]; + expect(messageList.count).to(equal(1)); + expect(messageList[0]).to(equal(testMessage)); + }); + }); + + context(@"when the MTU size is smaller than the payload size", ^{ + it(@"Should disassemble the message properly", ^ { + //Allocate 2000 bytes, and use it as sample data + const NSUInteger dataLength = 4000; + char dummyBytes[dataLength]; + + const char testPayloadHeader[12] = {0x20, 0x55, 0x64, 0x73, 0x12, 0x34, 0x43, 0x21, (dataLength >> 24) & 0xFF, (dataLength >> 16) & 0xFF, (dataLength >> 8) & 0xFF, dataLength & 0xFF}; + + NSMutableData* payloadData = [NSMutableData dataWithBytes:testPayloadHeader length:12]; + [payloadData appendBytes:dummyBytes length:dataLength]; + + SDLV2ProtocolMessage* testMessage = [[SDLV2ProtocolMessage alloc] init]; + SDLV2ProtocolHeader* testHeader = [[SDLV2ProtocolHeader alloc] init]; + + testHeader.frameType = SDLFrameTypeSingle; + testHeader.serviceType = SDLServiceTypeBulkData; + testHeader.frameData = SDLFrameInfoSingleFrame; + testHeader.sessionID = 0x84; + testHeader.bytesInPayload = (UInt32)payloadData.length; + + testMessage.header = testHeader; + testMessage.payload = payloadData; + + NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withPayloadSizeLimit:1024]; + expect(messageList.count).to(equal(5)); + + //Payload length per message + UInt32 payloadLength = 1012; // v1/2 MTU(1024) - header length(12) + + const char firstPayload[8] = {(payloadData.length >> 24) & 0xFF, (payloadData.length >> 16) & 0xFF, (payloadData.length >> 8) & 0xFF, payloadData.length & 0xFF, 0x00, 0x00, 0x00, ceil(1.0 * payloadData.length / payloadLength)}; + + SDLProtocolMessage* message = messageList[0]; + + //First frame + expect(message.payload).to(equal([NSData dataWithBytes:firstPayload length:8])); + + expect(@(message.header.frameType)).to(equal(@(SDLFrameTypeFirst))); + expect(@(message.header.serviceType)).to(equal(@(SDLServiceTypeBulkData))); + expect(@(message.header.frameData)).to(equal(@(SDLFrameInfoFirstFrame))); + expect(@(message.header.sessionID)).to(equal(@0x84)); + expect(@(message.header.bytesInPayload)).to(equal(@8)); + + NSUInteger offset = 0; + for (int i = 1; i < messageList.count - 1; i++) { + message = messageList[i]; + + //Consecutive frames + expect(message.payload).to(equal([NSData dataWithData:[payloadData subdataWithRange:NSMakeRange(offset, payloadLength)]])); + + expect(@(message.header.frameType)).to(equal(@(SDLFrameTypeConsecutive))); + expect(@(message.header.serviceType)).to(equal(@(SDLServiceTypeBulkData))); + expect(@(message.header.frameData)).to(equal(@(i))); + expect(@(message.header.sessionID)).to(equal(@0x84)); + expect(@(message.header.bytesInPayload)).to(equal(@(payloadLength))); + + offset += payloadLength; + } + + message = [messageList lastObject]; + + NSUInteger remaining = payloadData.length - offset; + + //Last frame + expect(message.payload).to(equal([NSData dataWithData:[payloadData subdataWithRange:NSMakeRange(offset, remaining)]])); + expect(@(message.header.frameType)).to(equal(@(SDLFrameTypeConsecutive))); expect(@(message.header.serviceType)).to(equal(@(SDLServiceTypeBulkData))); - expect(@(message.header.frameData)).to(equal(@(i))); + expect(@(message.header.frameData)).to(equal(@(SDLFrameInfoConsecutiveLastFrame))); expect(@(message.header.sessionID)).to(equal(@0x84)); - expect(@(message.header.bytesInPayload)).to(equal(@(payloadLength))); - - offset += payloadLength; - } - - message = [messageList lastObject]; - - NSUInteger remaining = payloadData.length - offset; - - //Last frame - expect(message.payload).to(equal([NSData dataWithData:[payloadData subdataWithRange:NSMakeRange(offset, remaining)]])); - - expect(@(message.header.frameType)).to(equal(@(SDLFrameTypeConsecutive))); - expect(@(message.header.serviceType)).to(equal(@(SDLServiceTypeBulkData))); - expect(@(message.header.frameData)).to(equal(@(SDLFrameInfoConsecutiveLastFrame))); - expect(@(message.header.sessionID)).to(equal(@0x84)); - expect(@(message.header.bytesInPayload)).to(equal(@(remaining))); + expect(@(message.header.bytesInPayload)).to(equal(@(remaining))); + }); }); }); From 3ce4d25b992c23a2253fd1a5cf33b7958d78fc39 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 1 Apr 2021 10:24:11 -0400 Subject: [PATCH 05/19] Fix naming again --- .../private/SDLProtocolMessageDisassembler.h | 2 +- .../private/SDLProtocolMessageDisassembler.m | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h index fd8b939a5..c7d9c9e9c 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN /// Use to break up a large message into a sequence of smaller messages, each of which is less than 'mtu' number of bytes total size. /// -/// @param message The message to break up +/// @param protocolMessage The message to break up /// @param mtu The MTU size to use to determine where to break up the message payload + (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withPayloadSizeLimit:(NSUInteger)mtu; diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m index 25384b01c..1f8422382 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m @@ -9,14 +9,14 @@ @implementation SDLProtocolMessageDisassembler -+ (NSArray *)disassemble:(SDLProtocolMessage *)message withPayloadSizeLimit:(NSUInteger)mtu { - if (message.size < mtu) { - return @[message]; ++ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withPayloadSizeLimit:(NSUInteger)mtu { + if (protocolMessage.size < mtu) { + return @[protocolMessage]; } // How big is the message header and payload? - NSUInteger headerSize = message.header.size; - NSUInteger payloadSize = (message.data.length - headerSize); + NSUInteger headerSize = protocolMessage.header.size; + NSUInteger payloadSize = (protocolMessage.data.length - headerSize); // How many messages do we need to create to hold that many bytes? // Note: This does NOT count the special first message which acts as a descriptor. @@ -29,12 +29,12 @@ @implementation SDLProtocolMessageDisassembler NSMutableArray *outgoingMessages = [NSMutableArray arrayWithCapacity:numberOfMessagesRequired + 1]; // Create the first message, which cannot be encrypted because it needs to be exactly 8 bytes - SDLProtocolHeader *firstFrameHeader = [message.header copy]; + SDLProtocolHeader *firstFrameHeader = [protocolMessage.header copy]; firstFrameHeader.frameType = SDLFrameTypeFirst; firstFrameHeader.encrypted = NO; UInt32 payloadData[2]; - payloadData[0] = CFSwapInt32HostToBig((UInt32)message.payload.length); + payloadData[0] = CFSwapInt32HostToBig((UInt32)protocolMessage.payload.length); payloadData[1] = CFSwapInt32HostToBig((UInt32)numberOfMessagesRequired); NSMutableData *firstFramePayload = [NSMutableData dataWithBytes:payloadData length:sizeof(payloadData)]; @@ -46,19 +46,19 @@ @implementation SDLProtocolMessageDisassembler // Frame # after 255 must cycle back to 1, not 0. A 0 signals last frame (SDLFrameInfoConsecutiveLastFrame). UInt8 frameNumber = (n % 255) + 1; - SDLProtocolHeader *nextFrameHeader = [message.header copy]; + SDLProtocolHeader *nextFrameHeader = [protocolMessage.header copy]; nextFrameHeader.frameType = SDLFrameTypeConsecutive; nextFrameHeader.frameData = frameNumber; NSUInteger offsetOfDataForThisFrame = headerSize + (n * numberOfDataBytesPerMessage); - NSData *nextFramePayload = [message.data subdataWithRange:NSMakeRange(offsetOfDataForThisFrame, numberOfDataBytesPerMessage)]; + NSData *nextFramePayload = [protocolMessage.data subdataWithRange:NSMakeRange(offsetOfDataForThisFrame, numberOfDataBytesPerMessage)]; SDLProtocolMessage *nextMessage = [SDLProtocolMessage messageWithHeader:nextFrameHeader andPayload:nextFramePayload]; outgoingMessages[n + 1] = nextMessage; } // Create the last message - SDLProtocolHeader *lastFrameHeader = [message.header copy]; + SDLProtocolHeader *lastFrameHeader = [protocolMessage.header copy]; lastFrameHeader.frameType = SDLFrameTypeConsecutive; lastFrameHeader.frameData = SDLFrameInfoConsecutiveLastFrame; @@ -66,7 +66,7 @@ @implementation SDLProtocolMessageDisassembler NSUInteger numberOfDataBytesSentSoFar = numberOfMessagesCreatedSoFar * numberOfDataBytesPerMessage; NSUInteger numberOfDataBytesInLastMessage = payloadSize - numberOfDataBytesSentSoFar; NSUInteger offsetOfDataForLastFrame = headerSize + numberOfDataBytesSentSoFar; - NSData *lastFramePayload = [message.data subdataWithRange:NSMakeRange(offsetOfDataForLastFrame, numberOfDataBytesInLastMessage)]; + NSData *lastFramePayload = [protocolMessage.data subdataWithRange:NSMakeRange(offsetOfDataForLastFrame, numberOfDataBytesInLastMessage)]; SDLProtocolMessage *lastMessage = [SDLProtocolMessage messageWithHeader:lastFrameHeader andPayload:lastFramePayload]; outgoingMessages[numberOfMessagesRequired] = lastMessage; From 045cd6ee9bbc4623a56396d8a00491894c7843e2 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 1 Apr 2021 14:06:00 -0400 Subject: [PATCH 06/19] In progress test updates --- SmartDeviceLink/private/SDLProtocol.m | 7 +- .../private/SDLProtocolMessageDisassembler.h | 2 +- .../private/SDLProtocolMessageDisassembler.m | 2 +- .../MessageSpecs/SDLProtocolSpec.m | 132 +++++++++++++----- .../SDLProtocolMessageDisassemblerSpec.m | 4 +- 5 files changed, 102 insertions(+), 45 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index ae859589b..618dd9a54 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -375,12 +375,13 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr SDLProtocolMessage *protocolMessage = [SDLProtocolMessage messageWithHeader:header andPayload:messagePayload]; // See if the message is small enough to send in one transmission. If not, break it up into smaller messages and send. - NSUInteger mtuSize = (encryption ? TLSMaxDataToEncryptSize : [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]); + NSUInteger rpcMTUSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]; + NSUInteger mtuSize = (encryption ? MIN(TLSMaxDataToEncryptSize, rpcMTUSize) : rpcMTUSize); if (protocolMessage.size < mtuSize) { SDLLogV(@"Sending protocol message: %@", protocolMessage); [self sdl_sendDataToTransport:protocolMessage.data onService:SDLServiceTypeRPC]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withPayloadSizeLimit:mtuSize]; + NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withMTULimit:mtuSize]; for (SDLProtocolMessage *smallerMessage in messages) { SDLLogV(@"Sending protocol message: %@", smallerMessage); [self sdl_sendDataToTransport:smallerMessage.data onService:SDLServiceTypeRPC]; @@ -431,7 +432,7 @@ - (void)sdl_sendRawData:(NSData *)data onService:(SDLServiceType)service encrypt SDLLogV(@"Sending protocol message: %@", message); [self sdl_sendDataToTransport:message.data onService:header.serviceType]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:message withPayloadSizeLimit:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:service]]; + NSArray *messages = [SDLProtocolMessageDisassembler disassemble:message withMTULimit:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:service]]; for (SDLProtocolMessage *smallerMessage in messages) { SDLLogV(@"Sending protocol message: %@", smallerMessage); [self sdl_sendDataToTransport:smallerMessage.data onService:header.serviceType]; diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h index c7d9c9e9c..8deabf8db 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN /// /// @param protocolMessage The message to break up /// @param mtu The MTU size to use to determine where to break up the message payload -+ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withPayloadSizeLimit:(NSUInteger)mtu; ++ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withMTULimit:(NSUInteger)mtu; @end diff --git a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m index 1f8422382..a8b77c806 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageDisassembler.m @@ -9,7 +9,7 @@ @implementation SDLProtocolMessageDisassembler -+ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withPayloadSizeLimit:(NSUInteger)mtu { ++ (NSArray *)disassemble:(SDLProtocolMessage *)protocolMessage withMTULimit:(NSUInteger)mtu { if (protocolMessage.size < mtu) { return @[protocolMessage]; } diff --git a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m index 27de73b2a..542a96869 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m @@ -9,18 +9,21 @@ #import #import -#import "SDLTransportType.h" #import "SDLControlFramePayloadAudioStartServiceAck.h" #import "SDLControlFramePayloadRegisterSecondaryTransportNak.h" #import "SDLControlFramePayloadRPCStartServiceAck.h" #import "SDLControlFramePayloadVideoStartServiceAck.h" +#import "SDLDeleteCommand.h" #import "SDLGlobals.h" #import "SDLProtocolHeader.h" #import "SDLProtocol.h" #import "SDLProtocolMessage.h" +#import "SDLProtocolMessageDisassembler.h" #import "SDLProtocolReceivedMessageRouter.h" +#import "SDLRPCFunctionNames.h" #import "SDLRPCRequest.h" #import "SDLRPCParameterNames.h" +#import "SDLTransportType.h" #import "SDLV1ProtocolMessage.h" #import "SDLV2ProtocolMessage.h" #import "SDLV1ProtocolHeader.h" @@ -35,10 +38,12 @@ SDLRPCParameterNameCorrelationId:@0x98765, SDLRPCParameterNameParameters: @{SDLRPCParameterNameCommandId:@55}}}; -NSDictionary* dictionaryV2 = @{SDLRPCParameterNameCommandId:@55}; +// Send StartService Tests describe(@"Send StartService Tests", ^{ + // Insecure context(@"Insecure", ^{ + // Should send the correct data it(@"Should send the correct data", ^{ // Reset max protocol version before test. (This test case expects V1 header. If other test ran // prior to this one, SDLGlobals would keep the max protocol version and this test case would fail.) @@ -110,8 +115,11 @@ }); }); +// Send EndSession Tests describe(@"Send EndSession Tests", ^{ + // During V1 session context(@"During V1 session", ^{ + // Should send the correct data it(@"Should send the correct data", ^{ __block BOOL verified = NO; id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); @@ -168,7 +176,9 @@ }); }); +// Send Register Secondary Transport Tests describe(@"Send Register Secondary Transport Tests", ^{ + // Should send the correct data it(@"Should send the correct data", ^{ __block BOOL verified = NO; id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); @@ -201,13 +211,20 @@ }); }); -describe(@"SendRPCRequest Tests", ^{ +// SendRPC Tests +describe(@"SendRPC Tests", ^{ __block id mockRequest; beforeEach(^{ mockRequest = OCMPartialMock([[SDLRPCRequest alloc] init]); }); - + + afterEach(^{ + [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithMajor:1 minor:0 patch:0]; + }); + + // During V1 session context(@"During V1 session", ^{ + // Should send the correct data it(@"Should send the correct data", ^{ [[[[mockRequest stub] andReturn:dictionaryV1] ignoringNonObjectArgs] serializeAsDictionary:1]; @@ -242,55 +259,81 @@ expect(verified).toEventually(beTrue()); }); }); - + + // During V2 session context(@"During V2 session", ^{ - it(@"Should send the correct data bulk data when bulk data is available", ^{ - [[[[mockRequest stub] andReturn:dictionaryV2] ignoringNonObjectArgs] serializeAsDictionary:2]; - [[[mockRequest stub] andReturn:@0x98765] correlationID]; - [[[mockRequest stub] andReturn:@"DeleteCommand"] name]; - [[[mockRequest stub] andReturn:[NSData dataWithBytes:"COMMAND" length:strlen("COMMAND")]] bulkData]; - - __block BOOL verified = NO; + beforeEach(^{ + [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithMajor:2 minor:0 patch:0]; + }); + + // Should send the correct data bulk data when bulk data is available + it(@"should correctly send a request smaller than the MTU size", ^{ + SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; + deleteRequest.correlationID = @12345; + deleteRequest.bulkData = [NSData dataWithBytes:"COMMAND" length:strlen("COMMAND")]; + + __block NSUInteger numTimesCalled = 0; id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); - [[[transportMock stub] andDo:^(NSInvocation* invocation) { - verified = YES; - - //Without the __unsafe_unretained, a double release will occur. More information: https://github.com/erikdoe/ocmock/issues/123 - __unsafe_unretained NSData* data; - [invocation getArgument:&data atIndex:2]; - NSData* dataSent = [data copy]; - - NSData* jsonTestData = [NSJSONSerialization dataWithJSONObject:dictionaryV2 options:0 error:0]; - NSUInteger dataLength = jsonTestData.length; - - const char testPayloadHeader[12] = {0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x87, 0x65, (dataLength >> 24) & 0xFF, (dataLength >> 16) & 0xFF, (dataLength >> 8) & 0xFF, dataLength & 0xFF}; - - NSMutableData* payloadData = [NSMutableData dataWithBytes:testPayloadHeader length:12]; - [payloadData appendData:jsonTestData]; - [payloadData appendBytes:"COMMAND" length:strlen("COMMAND")]; - - const char testHeader[12] = {0x20 | SDLFrameTypeSingle, SDLServiceTypeBulkData, SDLFrameInfoSingleFrame, 0x01, (payloadData.length >> 24) & 0xFF, (payloadData.length >> 16) & 0xFF,(payloadData.length >> 8) & 0xFF, payloadData.length & 0xFF, 0x00, 0x00, 0x00, 0x01}; - - NSMutableData* testData = [NSMutableData dataWithBytes:testHeader length:12]; - [testData appendData:payloadData]; - - expect(dataSent).to(equal([testData copy])); + [[[transportMock stub] andDo:^(NSInvocation *invocation) { + numTimesCalled++; }] sendData:[OCMArg any]]; + SDLProtocol *testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:nil]; + // Send a start service ack to ensure that it's set up for sending the RPC SDLV2ProtocolHeader *testHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:2]; testHeader.serviceType = SDLServiceTypeRPC; testHeader.sessionID = 0x01; [testProtocol protocol:testProtocol didReceiveStartServiceACK:[SDLProtocolMessage messageWithHeader:testHeader andPayload:nil]]; - + + id disassember = OCMClassMock([SDLProtocolMessageDisassembler class]); + OCMStub([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); + OCMReject([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); + [testProtocol sendRPC:mockRequest]; - expect(verified).toEventually(beTrue()); + expect(numTimesCalled).toEventually(equal(1)); + }); + + it(@"should correctly send a request larger than the MTU size", ^{ + char dummyBytes[2000]; + + SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; + deleteRequest.correlationID = @12345; + deleteRequest.bulkData = [NSData dataWithBytes:dummyBytes length:2000]; + + __block NSUInteger numTimesCalled = 0; + id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); + [[[transportMock stub] andDo:^(NSInvocation *invocation) { + numTimesCalled++; + }] sendData:[OCMArg any]]; + + SDLProtocol *testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:nil]; + + // Send a start service ack to ensure that it's set up for sending the RPC + SDLV2ProtocolHeader *testHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:2]; + testHeader.serviceType = SDLServiceTypeRPC; + testHeader.sessionID = 0x01; + [testProtocol protocol:testProtocol didReceiveStartServiceACK:[SDLProtocolMessage messageWithHeader:testHeader andPayload:nil]]; + + id disassember = OCMClassMock([SDLProtocolMessageDisassembler class]); + OCMStub([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); + OCMReject([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); + + [testProtocol sendRPC:mockRequest]; + + expect(numTimesCalled).toEventually(equal(2)); + }); + + it(@"should correctly adjust the MTU size when the packet is encrypted and the service MTU size is larger than the TLS max size", ^{ + }); }); }); +// HandleBytesFromTransport Tests describe(@"HandleBytesFromTransport Tests", ^{ + // During V1 session context(@"During V1 session", ^{ // it(@"Should parse the data correctly", ^{ // id routerMock = OCMClassMock(SDLProtocolReceivedMessageRouter.class); @@ -337,7 +380,8 @@ // expect(@(verified)).toEventually(beTruthy()); // }); }); - + + // During V2 session context(@"During V2 session", ^{ // it(@"Should parse the data correctly", ^{ // id routerMock = OCMClassMock(SDLProtocolReceivedMessageRouter.class); @@ -397,6 +441,7 @@ }); }); +// HandleProtocolSessionStarted tests describe(@"HandleProtocolSessionStarted tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; @@ -421,7 +466,9 @@ #pragma clang diagnostic pop }); + // For protocol versions 5.0.0 and greater context(@"For protocol versions 5.0.0 and greater", ^{ + // If the service type is RPC context(@"If the service type is RPC", ^{ it(@"Should store the auth token, system info, and the protocol version and pass the start service along to the delegate", ^{ SDLControlFramePayloadRPCStartServiceAck *testPayload = [[SDLControlFramePayloadRPCStartServiceAck alloc] initWithHashId:hashId mtu:testMTU authToken:testAuthToken protocolVersion:@"5.2.0" secondaryTransports:nil audioServiceTransports:nil videoServiceTransports:nil make:testMake model:testModel trim:testTrim modelYear:testModelYear systemSoftwareVersion:testSystemSoftwareVersion systemHardwareVersion:testSystemHardwareVersion]; @@ -580,7 +627,9 @@ }); }); + // For protocol versions below 5.0.0 context(@"For protocol versions below 5.0.0", ^{ + // If the service type is RPC context(@"If the service type is RPC", ^{ it(@"Should store the protocol version and pass the start service along to the delegate", ^{ SDLControlFramePayloadRPCStartServiceAck *testPayload = [[SDLControlFramePayloadRPCStartServiceAck alloc] initWithHashId:hashId mtu:testMTU authToken:nil protocolVersion:@"3.1.0" secondaryTransports:nil audioServiceTransports:nil videoServiceTransports:nil make:nil model:nil trim:nil modelYear:nil systemSoftwareVersion:nil systemHardwareVersion:nil]; @@ -685,6 +734,7 @@ }); }); +// HandleProtocolRegisterSecondaryTransport Tests describe(@"HandleProtocolRegisterSecondaryTransport Tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; @@ -694,6 +744,7 @@ testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:nil]; }); + // Should pass information along to delegate when ACKed it(@"Should pass information along to delegate when ACKed", ^{ id delegateMock = OCMProtocolMock(@protocol(SDLProtocolDelegate)); @@ -714,6 +765,7 @@ OCMVerifyAllWithDelay(delegateMock, 0.1); }); + // Should pass information along to delegate when NAKed it(@"Should pass information along to delegate when NAKed", ^{ id delegateMock = OCMProtocolMock(@protocol(SDLProtocolDelegate)); @@ -737,6 +789,7 @@ }); }); +// HandleHeartbeatForSession Tests describe(@"HandleHeartbeatForSession Tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; @@ -758,6 +811,7 @@ }); }); +// OnProtocolMessageReceived Tests describe(@"OnProtocolMessageReceived Tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; @@ -784,6 +838,7 @@ }); }); +// OnProtocolOpened Tests describe(@"OnProtocolOpened Tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; @@ -805,6 +860,7 @@ }); }); +// OnProtocolClosed Tests describe(@"OnProtocolClosed Tests", ^{ __block id transportMock = nil; __block SDLProtocol *testProtocol = nil; diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m index 94c5121e7..e2df2b5c8 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m @@ -41,7 +41,7 @@ testMessage.header = testHeader; testMessage.payload = payloadData; - NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withPayloadSizeLimit:1024]; + NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withMTULimit:1024]; expect(messageList.count).to(equal(1)); expect(messageList[0]).to(equal(testMessage)); }); @@ -70,7 +70,7 @@ testMessage.header = testHeader; testMessage.payload = payloadData; - NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withPayloadSizeLimit:1024]; + NSArray *messageList = [SDLProtocolMessageDisassembler disassemble:testMessage withMTULimit:1024]; expect(messageList.count).to(equal(5)); //Payload length per message From be47f604d07a57809edfb9358e7f7a217e01c382 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 22 Apr 2021 16:02:40 -0400 Subject: [PATCH 07/19] Start on breaking up messages _then_ encrypting them --- SmartDeviceLink/private/SDLProtocol.m | 49 ++++++++++++++------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 5c7c782f5..009be5377 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -82,7 +82,7 @@ - (instancetype)initWithTransport:(id)transport encryptionMana _transport.delegate = self; _encryptionLifecycleManager = encryptionManager; - + return self; } @@ -309,7 +309,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr *error = jsonError; return NO; } - + NSData *messagePayload = nil; SDLLogV(@"Sending RPC: %@", message); @@ -346,27 +346,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr return NO; } - // If we're trying to encrypt, try to have the security manager encrypt it. Return if it fails. - NSError *encryptError = nil; - if (encryption) { - messagePayload = [self.securityManager encryptData:rpcPayload.data withError:&encryptError]; - - if (encryptError != nil) { - SDLLogE(@"Error encrypting request! %@", encryptError); - } - } else { - messagePayload = rpcPayload.data; - } - - // If the encryption failed, pass back the error and return false - if (!messagePayload) { - if (encryptError != nil) { - *error = encryptError; - } else { - *error = [NSError sdl_encryption_unknown]; - } - return NO; - } + messagePayload = rpcPayload.data; } break; default: { NSAssert(NO, @"Attempting to send an RPC based on an unknown version number: %@, message: %@", @([SDLGlobals sharedGlobals].protocolVersion.major), message); @@ -392,6 +372,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr NSUInteger rpcMTUSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]; NSUInteger mtuSize = (encryption ? MIN(TLSMaxDataToEncryptSize, rpcMTUSize) : rpcMTUSize); if (protocolMessage.size < mtuSize) { + SDLLogV(@"Sending protocol message: %@", protocolMessage); [self sdl_sendDataToTransport:protocolMessage.data onService:SDLServiceTypeRPC]; } else { @@ -405,6 +386,27 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr return YES; } +- (void)sdl_encryptProtocolMessages:(NSArray *)protocolMessages error:(NSError *__autoreleasing *)error { + for (SDLProtocolMessage *message in protocolMessages) { + // If we're trying to encrypt, try to have the security manager encrypt it. Return if it fails. + NSError *encryptError = nil; + NSData *encryptedMessagePayload = [self.securityManager encryptData:message.payload withError:&encryptError]; + + // If the encryption failed, pass back the error and return false + if (encryptedMessagePayload.length == 0 || encryptError != nil) { + if (encryptError != nil) { + *error = encryptError; + } else { + *error = [NSError sdl_encryption_unknown]; + } + + break; + } else { + message.payload = encryptedMessagePayload; + } + } +} + // Use for normal messages - (void)sdl_sendDataToTransport:(NSData *)data onService:(NSInteger)priority { [_prioritizedCollection addObject:data withPriority:priority]; @@ -420,6 +422,7 @@ - (void)sendRawData:(NSData *)data withServiceType:(SDLServiceType)serviceType { } - (void)sendEncryptedRawData:(NSData *)data onService:(SDLServiceType)serviceType { + // TODO: Ensure data is at or below TLS max [self sdl_sendRawData:data onService:serviceType encryption:YES]; } From 6da2c60908c321668d257c54400b4affe6f67648 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 09:46:22 -0400 Subject: [PATCH 08/19] Update encrypting protocol messages --- SmartDeviceLink/private/SDLProtocol.m | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 009be5377..83ce0d138 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -371,16 +371,24 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr // See if the message is small enough to send in one transmission. If not, break it up into smaller messages and send. NSUInteger rpcMTUSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]; NSUInteger mtuSize = (encryption ? MIN(TLSMaxDataToEncryptSize, rpcMTUSize) : rpcMTUSize); + NSArray *protocolMessages = nil; if (protocolMessage.size < mtuSize) { - - SDLLogV(@"Sending protocol message: %@", protocolMessage); - [self sdl_sendDataToTransport:protocolMessage.data onService:SDLServiceTypeRPC]; + protocolMessages = @[protocolMessage]; } else { - NSArray *messages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withMTULimit:mtuSize]; - for (SDLProtocolMessage *smallerMessage in messages) { - SDLLogV(@"Sending protocol message: %@", smallerMessage); - [self sdl_sendDataToTransport:smallerMessage.data onService:SDLServiceTypeRPC]; + protocolMessages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withMTULimit:mtuSize]; + } + + for (SDLProtocolMessage *message in protocolMessages) { + if (encryption) { + [self sdl_encryptProtocolMessages:protocolMessages error:error]; + if (*error != nil) { + SDLLogE(@"Error encrypting protocol messages. Messages will not be sent. Error: %@", error); + return NO; + } } + + SDLLogV(@"Sending protocol message: %@", message); + [self sdl_sendDataToTransport:message.data onService:SDLServiceTypeRPC]; } return YES; From c087ec73976eb580d03a829a10063ccd8c36b3a0 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 09:53:36 -0400 Subject: [PATCH 09/19] Fix warnings / errors --- SmartDeviceLink/private/SDLProtocol.m | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 83ce0d138..783ade32c 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -380,9 +380,9 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr for (SDLProtocolMessage *message in protocolMessages) { if (encryption) { - [self sdl_encryptProtocolMessages:protocolMessages error:error]; - if (*error != nil) { - SDLLogE(@"Error encrypting protocol messages. Messages will not be sent. Error: %@", error); + BOOL success = [self sdl_encryptProtocolMessages:protocolMessages error:error]; + if (!success) { + SDLLogE(@"Error encrypting protocol messages. Messages will not be sent. Error: %@", *error); return NO; } } @@ -394,7 +394,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr return YES; } -- (void)sdl_encryptProtocolMessages:(NSArray *)protocolMessages error:(NSError *__autoreleasing *)error { +- (BOOL)sdl_encryptProtocolMessages:(NSArray *)protocolMessages error:(NSError *__autoreleasing *)error { for (SDLProtocolMessage *message in protocolMessages) { // If we're trying to encrypt, try to have the security manager encrypt it. Return if it fails. NSError *encryptError = nil; @@ -408,11 +408,13 @@ - (void)sdl_encryptProtocolMessages:(NSArray *)protocolMes *error = [NSError sdl_encryption_unknown]; } - break; + return NO; } else { message.payload = encryptedMessagePayload; } } + + return YES; } // Use for normal messages From f8756ae1101b4e36d747e481ff9afc31ecea0f79 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 10:15:23 -0400 Subject: [PATCH 10/19] Fix description methods --- SmartDeviceLink/private/SDLProtocol.m | 2 +- SmartDeviceLink/private/SDLProtocolMessage.m | 2 +- SmartDeviceLink/private/SDLV2ProtocolHeader.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 783ade32c..1024ab8cf 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -185,7 +185,7 @@ - (void)startSecureServiceWithType:(SDLServiceType)serviceType payload:(nullable // TLS initialization succeeded. Build and send the message. SDLProtocolMessage *message = [self sdl_createStartServiceMessageWithType:serviceType encrypted:YES payload:nil]; - SDLLogD(@"TLS initialized, sending start service for message: %@", message); + SDLLogD(@"TLS initialized, sending start service with encryption for message: %@", message); [self sdl_sendDataToTransport:message.data onService:serviceType]; }]; } diff --git a/SmartDeviceLink/private/SDLProtocolMessage.m b/SmartDeviceLink/private/SDLProtocolMessage.m index 6ed86e05d..7e2da7d0c 100644 --- a/SmartDeviceLink/private/SDLProtocolMessage.m +++ b/SmartDeviceLink/private/SDLProtocolMessage.m @@ -55,7 +55,7 @@ - (NSString *)description { [description appendString:self.header.description]; if (self.header.encrypted) { - [description appendString:@", Payload is encrypted - no description can be provided"]; + [description appendString:@"Payload is encrypted - no description can be provided"]; return description; } diff --git a/SmartDeviceLink/private/SDLV2ProtocolHeader.m b/SmartDeviceLink/private/SDLV2ProtocolHeader.m index d89fdeb1f..00567180c 100644 --- a/SmartDeviceLink/private/SDLV2ProtocolHeader.m +++ b/SmartDeviceLink/private/SDLV2ProtocolHeader.m @@ -109,7 +109,7 @@ - (NSString *)description { } NSMutableString *description = [[NSMutableString alloc] init]; - [description appendFormat:@"Version:%i, encrypted:%i, frameType:%@(%i), serviceType:%i, frameData:%@(%i), sessionID:%i, dataSize:%i, messageID:%i ", + [description appendFormat:@"Version:%i, encrypted:%i, frameType:%@(%i), serviceType:%i, frameData:%@(%i), sessionID:%i, dataSize:%i, messageID:%i, ", self.version, self.encrypted, frameTypeString, From a4632c25b04039d9a70413550827b78ad55b989d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 10:35:26 -0400 Subject: [PATCH 11/19] Fix encryption --- SmartDeviceLink/private/SDLProtocol.m | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 1024ab8cf..01a0b6ec2 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -378,15 +378,17 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr protocolMessages = [SDLProtocolMessageDisassembler disassemble:protocolMessage withMTULimit:mtuSize]; } - for (SDLProtocolMessage *message in protocolMessages) { - if (encryption) { - BOOL success = [self sdl_encryptProtocolMessages:protocolMessages error:error]; - if (!success) { - SDLLogE(@"Error encrypting protocol messages. Messages will not be sent. Error: %@", *error); - return NO; - } + // If the message should be encrypted, encrypt the payloads + if (encryption) { + BOOL success = [self sdl_encryptProtocolMessages:protocolMessages error:error]; + if (!success) { + SDLLogE(@"Error encrypting protocol messages. Messages will not be sent. Error: %@", *error); + return NO; } + } + // Send each message + for (SDLProtocolMessage *message in protocolMessages) { SDLLogV(@"Sending protocol message: %@", message); [self sdl_sendDataToTransport:message.data onService:SDLServiceTypeRPC]; } From 7a8cc3acb2a55a28d5ac3a532ee67c1de7976fbe Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 15:52:32 -0400 Subject: [PATCH 12/19] Use the new payload size if we encrypt --- SmartDeviceLink/private/SDLProtocol.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 01a0b6ec2..2f92d41c4 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -413,6 +413,7 @@ - (BOOL)sdl_encryptProtocolMessages:(NSArray *)protocolMes return NO; } else { message.payload = encryptedMessagePayload; + message.header.bytesInPayload = encryptedMessagePayload.length; } } From a9d999eb133085c8ced0e26ddfdc3a281d32501b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 23 Apr 2021 16:00:13 -0400 Subject: [PATCH 13/19] Fix cast warning --- SmartDeviceLink/private/SDLProtocol.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 2f92d41c4..720973980 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -413,7 +413,7 @@ - (BOOL)sdl_encryptProtocolMessages:(NSArray *)protocolMes return NO; } else { message.payload = encryptedMessagePayload; - message.header.bytesInPayload = encryptedMessagePayload.length; + message.header.bytesInPayload = (UInt32)encryptedMessagePayload.length; } } From cece459b34e9ea64d0f58df9bda3cf2a3779a851 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 26 Apr 2021 10:59:28 -0400 Subject: [PATCH 14/19] Don't encrypt first frame payloads --- SmartDeviceLink/private/SDLProtocol.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 720973980..7b94629c4 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -398,6 +398,8 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr - (BOOL)sdl_encryptProtocolMessages:(NSArray *)protocolMessages error:(NSError *__autoreleasing *)error { for (SDLProtocolMessage *message in protocolMessages) { + if (message.header.frameType == SDLFrameTypeFirst) { continue; } + // If we're trying to encrypt, try to have the security manager encrypt it. Return if it fails. NSError *encryptError = nil; NSData *encryptedMessagePayload = [self.securityManager encryptData:message.payload withError:&encryptError]; From 8d45e2e67d5f4ea3e8566066a377406c1ca250e0 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 26 Apr 2021 11:34:41 -0400 Subject: [PATCH 15/19] Ensure encrypted raw data is chunked if larger than TLS max size --- SmartDeviceLink/private/SDLProtocol.m | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 7b94629c4..5f1b8302a 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -34,7 +34,8 @@ #import "SDLV2ProtocolHeader.h" NSString *const SDLProtocolSecurityErrorDomain = @"com.sdl.protocol.security"; -static const NSUInteger TLSMaxDataToEncryptSize = 16384 /*TLS Max Record Size*/ - 5 /*TLS Record Header Size*/ - 32 /*TLS MES Auth CDE Size*/ - 256 /*TLS Max Record Padding Size*/; +static const NSUInteger TLSMaxDataSize = 16834; +static const NSUInteger TLSMaxRPCPayloadDataToEncryptSize = 16384 /*TLS Max Record Size*/ - 5 /*TLS Record Header Size*/ - 32 /*TLS MES Auth CDE Size*/ - 256 /*TLS Max Record Padding Size*/; #pragma mark - SDLProtocol Private Interface @@ -370,7 +371,7 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr // See if the message is small enough to send in one transmission. If not, break it up into smaller messages and send. NSUInteger rpcMTUSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC]; - NSUInteger mtuSize = (encryption ? MIN(TLSMaxDataToEncryptSize, rpcMTUSize) : rpcMTUSize); + NSUInteger mtuSize = (encryption ? MIN(TLSMaxRPCPayloadDataToEncryptSize, rpcMTUSize) : rpcMTUSize); NSArray *protocolMessages = nil; if (protocolMessage.size < mtuSize) { protocolMessages = @[protocolMessage]; @@ -437,8 +438,16 @@ - (void)sendRawData:(NSData *)data withServiceType:(SDLServiceType)serviceType { } - (void)sendEncryptedRawData:(NSData *)data onService:(SDLServiceType)serviceType { - // TODO: Ensure data is at or below TLS max - [self sdl_sendRawData:data onService:serviceType encryption:YES]; + // We need to chunk encrypted data beneath the max TLS size + NSUInteger offset = 0; + do { + NSUInteger remainingDataLength = data.length - offset; + NSUInteger chunkSize = (remainingDataLength > TLSMaxDataSize) ? TLSMaxDataSize : remainingDataLength; + NSData *chunk = [NSData dataWithBytesNoCopy:(BytePtr)data.bytes + offset length:chunkSize freeWhenDone:NO]; + + [self sdl_sendRawData:chunk onService:serviceType encryption:YES]; + offset += chunkSize; + } while (offset < data.length); } - (void)sdl_sendRawData:(NSData *)data onService:(SDLServiceType)service encryption:(BOOL)encryption { From f6e5f7e3847f1ce571f24b4d5f7e01ebfc52ae30 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 26 Apr 2021 11:53:28 -0400 Subject: [PATCH 16/19] Style update --- .../private/SDLProtocolMessageAssembler.m | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocolMessageAssembler.m b/SmartDeviceLink/private/SDLProtocolMessageAssembler.m index e02ead324..f03cc39cf 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageAssembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageAssembler.m @@ -32,13 +32,12 @@ - (void)handleMessage:(SDLProtocolMessage *)message withCompletionHandler:(SDLMe } if (self.parts == nil) { - self.parts = [NSMutableDictionary new]; + self.parts = [NSMutableDictionary dictionary]; } // Determine which frame it is and save it. - // Note: frames are numbered 1,2,3, ..., 0 - // Always 0 for last frame. + // Note: frames are numbered 1,2,3, ..., 0. Always 0 for last frame. if (message.header.frameType == SDLFrameTypeFirst) { // If it's the first-frame, extract the meta-data self.expectedBytes = NSSwapBigIntToHost(((UInt32 *)message.payload.bytes)[0]); @@ -51,25 +50,21 @@ - (void)handleMessage:(SDLProtocolMessage *)message withCompletionHandler:(SDLMe self.parts[frameNumberObj] = message.payload; } - - // // If we have all the parts, assemble it and execute the completion handler. - // SDLProtocolMessage *assembledMessage = nil; if (self.parts.count == self.frameCount + 1) { // +1 since we also require the first-frame - // Create the header SDLProtocolHeader *header = message.header.copy; header.frameType = SDLFrameTypeSingle; header.frameData = SDLFrameInfoSingleFrame; - // Create the payload NSMutableData *payload = [[NSMutableData alloc] init]; for (unsigned int i = 1; i < self.frameCount; i++) { NSData *dataToAppend = [self.parts objectForKey:[NSNumber numberWithUnsignedInt:i]]; [payload appendData:dataToAppend]; } + // Append the last frame, it has a frame # of 0. NSData *dataToAppend = [self.parts objectForKey:[NSNumber numberWithUnsignedInt:0]]; [payload appendData:dataToAppend]; @@ -82,7 +77,6 @@ - (void)handleMessage:(SDLProtocolMessage *)message withCompletionHandler:(SDLMe // Create the message. assembledMessage = [SDLProtocolMessage messageWithHeader:header andPayload:payload]; - // Execute completion handler. if (completionHandler != nil) { completionHandler(YES, assembledMessage); @@ -90,7 +84,6 @@ - (void)handleMessage:(SDLProtocolMessage *)message withCompletionHandler:(SDLMe // Done with this data, release it. self.parts = nil; - } else { // Not done, let caller know if (completionHandler != nil) { From d0c3a77b3b8872b965b43c6f42b4eab48bba55c2 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 26 Apr 2021 11:54:42 -0400 Subject: [PATCH 17/19] Style updates --- SmartDeviceLink/private/SDLProtocolMessageAssembler.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocolMessageAssembler.m b/SmartDeviceLink/private/SDLProtocolMessageAssembler.m index f03cc39cf..e4a07541c 100644 --- a/SmartDeviceLink/private/SDLProtocolMessageAssembler.m +++ b/SmartDeviceLink/private/SDLProtocolMessageAssembler.m @@ -59,14 +59,14 @@ - (void)handleMessage:(SDLProtocolMessage *)message withCompletionHandler:(SDLMe header.frameData = SDLFrameInfoSingleFrame; // Create the payload - NSMutableData *payload = [[NSMutableData alloc] init]; + NSMutableData *payload = [NSMutableData data]; for (unsigned int i = 1; i < self.frameCount; i++) { - NSData *dataToAppend = [self.parts objectForKey:[NSNumber numberWithUnsignedInt:i]]; + NSData *dataToAppend = self.parts[@(i)]; [payload appendData:dataToAppend]; } // Append the last frame, it has a frame # of 0. - NSData *dataToAppend = [self.parts objectForKey:[NSNumber numberWithUnsignedInt:0]]; + NSData *dataToAppend = self.parts[@0]; [payload appendData:dataToAppend]; // Validation From 8a34f697ab6bbf9d423d0129e33836333b3e6060 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 26 Apr 2021 14:57:21 -0400 Subject: [PATCH 18/19] Add tests for encryption --- .../MessageSpecs/SDLProtocolSpec.m | 113 ++++++++++++------ 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m index 42f05e904..aa6537b14 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m @@ -14,6 +14,7 @@ #import "SDLControlFramePayloadRPCStartServiceAck.h" #import "SDLControlFramePayloadVideoStartServiceAck.h" #import "SDLDeleteCommand.h" +#import "SDLEncryptionLifecycleManager.h" #import "SDLGlobals.h" #import "SDLProtocolHeader.h" #import "SDLProtocol.h" @@ -292,66 +293,102 @@ NSError *error = nil; BOOL sent = [testProtocol sendRPC:mockRequest error:&error]; - expect(verified).toEventually(beTrue()); + expect(numTimesCalled).toEventually(equal(1)); expect(sent).to(beTrue()); expect(error).to(beNil()); }); it(@"should correctly send a request larger than the MTU size", ^{ - char dummyBytes[2000]; + char dummyBytes[1100]; SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; deleteRequest.correlationID = @12345; - deleteRequest.bulkData = [NSData dataWithBytes:dummyBytes length:2000]; + deleteRequest.bulkData = [NSData dataWithBytes:dummyBytes length:1100]; __block NSUInteger numTimesCalled = 0; id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); - [[[transportMock stub] andDo:^(NSInvocation* invocation) { - verified = YES; - - //Without the __unsafe_unretained, a double release will occur. More information: https://github.com/erikdoe/ocmock/issues/123 - __unsafe_unretained NSData* data; - [invocation getArgument:&data atIndex:2]; - NSData* dataSent = [data copy]; - - NSData* jsonTestData = [NSJSONSerialization dataWithJSONObject:dictionaryV2 options:0 error:0]; - NSUInteger dataLength = jsonTestData.length; - - const char testPayloadHeader[12] = {0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x87, 0x65, (dataLength >> 24) & 0xFF, (dataLength >> 16) & 0xFF, (dataLength >> 8) & 0xFF, dataLength & 0xFF}; - - NSMutableData* payloadData = [NSMutableData dataWithBytes:testPayloadHeader length:12]; - [payloadData appendData:jsonTestData]; - [payloadData appendBytes:"COMMAND" length:strlen("COMMAND")]; - - const char testHeader[12] = {0x20 | SDLFrameTypeSingle, SDLServiceTypeBulkData, SDLFrameInfoSingleFrame, 0x01, (payloadData.length >> 24) & 0xFF, (payloadData.length >> 16) & 0xFF,(payloadData.length >> 8) & 0xFF, payloadData.length & 0xFF, 0x00, 0x00, 0x00, 0x01}; - - NSMutableData* testData = [NSMutableData dataWithBytes:testHeader length:12]; - [testData appendData:payloadData]; - - expect(dataSent).to(equal([testData copy])); [[[transportMock stub] andDo:^(NSInvocation *invocation) { numTimesCalled++; }] sendData:[OCMArg any]]; SDLProtocol *testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:nil]; - // Send a start service ack to ensure that it's set up for sending the RPC - SDLV2ProtocolHeader *testHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:2]; - testHeader.serviceType = SDLServiceTypeRPC; - testHeader.sessionID = 0x01; - [testProtocol protocol:testProtocol didReceiveStartServiceACK:[SDLProtocolMessage messageWithHeader:testHeader andPayload:nil]]; + NSError *error = nil; + [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithMajor:2 minor:0 patch:0]; + BOOL sent = [testProtocol sendRPC:deleteRequest error:&error]; + + expect(numTimesCalled).toEventually(equal(3)); + expect(sent).to(beTrue()); + expect(error).to(beNil()); + }); - id disassember = OCMClassMock([SDLProtocolMessageDisassembler class]); - OCMStub([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); - OCMReject([disassember disassemble:[OCMArg any] withMTULimit:0]).ignoringNonObjectArgs(); + describe(@"when encrypting the requests", ^{ + context(@"when the encryption manager is not ready", ^{ + it(@"should not send the request and return an error", ^{ + char dummyBytes[20000]; - [testProtocol sendRPC:mockRequest]; + SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; + deleteRequest.correlationID = @12345; + deleteRequest.bulkData = [NSData dataWithBytes:dummyBytes length:20000]; + deleteRequest.payloadProtected = YES; - expect(numTimesCalled).toEventually(equal(2)); - }); + __block NSUInteger numTimesCalled = 0; + id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); + [[[transportMock stub] andDo:^(NSInvocation *invocation) { + numTimesCalled++; + }] sendData:[OCMArg any]]; - it(@"should correctly adjust the MTU size when the packet is encrypted and the service MTU size is larger than the TLS max size", ^{ + SDLProtocol *testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:nil]; + NSError *error = nil; + [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0]; + BOOL sent = [testProtocol sendRPC:deleteRequest error:&error]; + + expect(numTimesCalled).toEventually(equal(0)); + expect(sent).to(beFalse()); + expect(error).toNot(beNil()); + }); + }); + + context(@"when the encryption manager is ready", ^{ + __block NSUInteger numTimesCalled = 0; + __block SDLProtocol *testProtocol = nil; + NSUInteger dataSize = 20000; + beforeEach(^{ + id transportMock = OCMProtocolMock(@protocol(SDLTransportType)); + [[[transportMock stub] andDo:^(NSInvocation *invocation) { + numTimesCalled++; + }] sendData:[OCMArg any]]; + + SDLEncryptionLifecycleManager *encryptionMock = OCMClassMock([SDLEncryptionLifecycleManager class]); + OCMStub(encryptionMock.isEncryptionReady).andReturn(YES); + + id securityManager = OCMProtocolMock(@protocol(SDLSecurityType)); + char dummyBytes[dataSize]; + NSData *returnData = [NSData dataWithBytes:dummyBytes length:dataSize]; + OCMStub([securityManager encryptData:[OCMArg any] withError:[OCMArg setTo:nil]]).andReturn(returnData); + + testProtocol = [[SDLProtocol alloc] initWithTransport:transportMock encryptionManager:encryptionMock]; + testProtocol.securityManager = securityManager; + }); + + fit(@"should correctly adjust the MTU size when the packet is encrypted and the service MTU size is larger than the TLS max size", ^{ + char dummyBytes[dataSize]; + + SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; + deleteRequest.correlationID = @12345; + deleteRequest.bulkData = [NSData dataWithBytes:dummyBytes length:dataSize]; + deleteRequest.payloadProtected = YES; + + NSError *error = nil; + [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0]; + BOOL sent = [testProtocol sendRPC:deleteRequest error:&error]; + + expect(numTimesCalled).toEventually(equal(3)); + expect(sent).to(beTrue()); + expect(error).to(beNil()); + }); + }); }); }); }); From 6f5565403f723fd8705e3863a7a1812630ab616d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 27 Apr 2021 15:18:37 -0400 Subject: [PATCH 19/19] Fix review comments --- SmartDeviceLink/private/SDLProtocol.m | 6 +++++- .../ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m | 2 +- .../ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m | 2 +- generator/rpc_spec | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLProtocol.m b/SmartDeviceLink/private/SDLProtocol.m index 5f1b8302a..8f87bbdfb 100644 --- a/SmartDeviceLink/private/SDLProtocol.m +++ b/SmartDeviceLink/private/SDLProtocol.m @@ -397,6 +397,10 @@ - (BOOL)sendRPC:(SDLRPCMessage *)message encrypted:(BOOL)encryption error:(NSErr return YES; } +/// Receives an array of `SDLProtocolMessage` and attempts to encrypt their payloads in place through the active security manager. If anything fails, it will return NO and pass back the error. +/// @param protocolMessages The array of protocol messages to encrypt. +/// @param error A passback error object if attempting to encrypt the protocol message payloads fails. +/// @returns YES if the encryption was successful, NO if it was not - (BOOL)sdl_encryptProtocolMessages:(NSArray *)protocolMessages error:(NSError *__autoreleasing *)error { for (SDLProtocolMessage *message in protocolMessages) { if (message.header.frameType == SDLFrameTypeFirst) { continue; } @@ -438,7 +442,7 @@ - (void)sendRawData:(NSData *)data withServiceType:(SDLServiceType)serviceType { } - (void)sendEncryptedRawData:(NSData *)data onService:(SDLServiceType)serviceType { - // We need to chunk encrypted data beneath the max TLS size + // Break up data larger than the max TLS size so the data can be encrypted by the security manager without failing due to the data size being too big NSUInteger offset = 0; do { NSUInteger remainingDataLength = data.length - offset; diff --git a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m index aa6537b14..0dee191ef 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/MessageSpecs/SDLProtocolSpec.m @@ -372,7 +372,7 @@ testProtocol.securityManager = securityManager; }); - fit(@"should correctly adjust the MTU size when the packet is encrypted and the service MTU size is larger than the TLS max size", ^{ + it(@"should correctly adjust the MTU size when the packet is encrypted and the service MTU size is larger than the TLS max size", ^{ char dummyBytes[dataSize]; SDLDeleteCommand *deleteRequest = [[SDLDeleteCommand alloc] initWithId:55]; diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m index e2df2b5c8..a65652060 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolMessageDisassemblerSpec.m @@ -49,7 +49,7 @@ context(@"when the MTU size is smaller than the payload size", ^{ it(@"Should disassemble the message properly", ^ { - //Allocate 2000 bytes, and use it as sample data + //Allocate 4000 bytes and use it as sample data const NSUInteger dataLength = 4000; char dummyBytes[dataLength]; diff --git a/generator/rpc_spec b/generator/rpc_spec index 72632f946..6b9835535 160000 --- a/generator/rpc_spec +++ b/generator/rpc_spec @@ -1 +1 @@ -Subproject commit 72632f946941d63a57ee5e99896e3eae3627f7dd +Subproject commit 6b98355357b5b1893bbb59cb668d28545023457c