diff options
author | Robert Henigan <robert.henigan@livio.io> | 2021-03-25 14:51:50 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-25 14:51:50 -0400 |
commit | 33d2fbb99bf56bd363dc7177ab1b4eb5842207c9 (patch) | |
tree | 891f02407e4d003083d354c8fdff8b72618a8eaf | |
parent | 824de0e11c08b95c5bca679c299677bb3b6a6c40 (diff) | |
parent | 385f31462beeb2514d606203f5e5dd756750dd87 (diff) | |
download | sdl_android-33d2fbb99bf56bd363dc7177ab1b4eb5842207c9.tar.gz |
Merge pull request #1644 from smartdevicelink/bugfix/issue_1642
Implement multiframe encryption correctly
-rw-r--r-- | base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java | 119 | ||||
-rw-r--r-- | base/src/main/java/com/smartdevicelink/transport/SdlPsm.java | 13 |
2 files changed, 84 insertions, 48 deletions
diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index d9e437548..64578e7ee 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -71,6 +71,10 @@ public class SdlProtocolBase { private final static String FailurePropagating_Msg = "Failure propagating "; private static final int TLS_MAX_RECORD_SIZE = 16384; + private final static int TLS_RECORD_HEADER_SIZE = 5; + private final static int TLS_RECORD_MES_AUTH_CDE_SIZE = 32; + private final static int TLS_MAX_RECORD_PADDING_SIZE = 256; + private final static int TLS_MAX_DATA_TO_ENCRYPT_SIZE = TLS_MAX_RECORD_SIZE - TLS_RECORD_HEADER_SIZE - TLS_RECORD_MES_AUTH_CDE_SIZE - TLS_MAX_RECORD_PADDING_SIZE; private static final int PRIMARY_TRANSPORT_ID = 1; private static final int SECONDARY_TRANSPORT_ID = 2; @@ -561,7 +565,8 @@ public class SdlProtocolBase { public void sendMessage(ProtocolMessage protocolMsg) { SessionType sessionType = protocolMsg.getSessionType(); byte sessionID = protocolMsg.getSessionID(); - + boolean requiresEncryption = protocolMsg.getPayloadProtected(); + SdlSecurityBase sdlSec = null; byte[] data; if (protocolVersion.getMajor() > 1 && sessionType != SessionType.NAV && sessionType != SessionType.PCM) { if (sessionType.eq(SessionType.CONTROL)) { @@ -590,21 +595,15 @@ public class SdlProtocolBase { data = protocolMsg.getData(); } - if (iSdlProtocol != null && protocolMsg.getPayloadProtected()) { - - if (data != null && data.length > 0) { - byte[] dataToRead = new byte[TLS_MAX_RECORD_SIZE]; - SdlSecurityBase sdlSec = iSdlProtocol.getSdlSecurity(); - if (sdlSec == null) - return; - - Integer iNumBytes = sdlSec.encryptData(data, dataToRead); - if ((iNumBytes == null) || (iNumBytes <= 0)) - return; - - byte[] encryptedData = new byte[iNumBytes]; - System.arraycopy(dataToRead, 0, encryptedData, 0, iNumBytes); - data = encryptedData; + if (requiresEncryption) { + if (iSdlProtocol == null) { + DebugTool.logError(TAG, "Unable to encrypt packet, protocol callback was null"); + return; + } + sdlSec = iSdlProtocol.getSdlSecurity(); + if (sdlSec == null) { + DebugTool.logError(TAG, "Unable to encrypt packet, security library not found"); + return; } } @@ -616,24 +615,25 @@ public class SdlProtocolBase { return; } - synchronized (messageLock) { - if (data != null && data.length > getMtu(sessionType)) { + //Set the MTU according to session MTU provided by the IVI or the max data size for encryption if required + final Long mtu = requiresEncryption ? TLS_MAX_DATA_TO_ENCRYPT_SIZE : getMtu(sessionType); + synchronized (messageLock) { + if (data != null && data.length > mtu) { + //Since the packet is larger than the mtu size, it will be sent as a multi-frame packet messageID++; // Assemble first frame. - Long mtu = getMtu(sessionType); - int frameCount = Long.valueOf(data.length / mtu).intValue(); - if (data.length % mtu > 0) { - frameCount++; - } + int frameCount = (int) Math.ceil(data.length / (double) mtu); + byte[] firstFrameData = new byte[8]; // First four bytes are data size. System.arraycopy(BitConverter.intToByteArray(data.length), 0, firstFrameData, 0, 4); // Second four bytes are frame count. System.arraycopy(BitConverter.intToByteArray(frameCount), 0, firstFrameData, 4, 4); - SdlPacket firstHeader = SdlPacketFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, (byte) protocolVersion.getMajor(), firstFrameData, protocolMsg.getPayloadProtected()); + //NOTE: First frames cannot be encrypted because their payloads need to be exactly 8 bytes + SdlPacket firstHeader = SdlPacketFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, (byte) protocolVersion.getMajor(), firstFrameData, false); firstHeader.setPriorityCoefficient(1 + protocolMsg.priorityCoefficient); firstHeader.setTransportRecord(activeTransports.get(sessionType)); //Send the first frame @@ -642,33 +642,65 @@ public class SdlProtocolBase { int currentOffset = 0; byte frameSequenceNumber = 0; + byte[] dataBuffer, encryptedData; for (int i = 0; i < frameCount; i++) { - if (i < (frameCount - 1)) { + + frameSequenceNumber++; + + if (frameSequenceNumber == SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME) { + //If sequence numbers roll over to 0, increment again to avoid + //using the reserved sequence value for the final frame ++frameSequenceNumber; - if (frameSequenceNumber == - SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME) { - // we can't use 0x00 as frameSequenceNumber, because - // it's reserved for the last frame - ++frameSequenceNumber; - } - } else { + } + + if (i == frameCount - 1) { frameSequenceNumber = SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME; - } // end-if + } int bytesToWrite = data.length - currentOffset; if (bytesToWrite > mtu) { bytesToWrite = mtu.intValue(); } - SdlPacket consecHeader = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), data, currentOffset, bytesToWrite, protocolMsg.getPayloadProtected()); - consecHeader.setTransportRecord(activeTransports.get(sessionType)); - consecHeader.setPriorityCoefficient(i + 2 + protocolMsg.priorityCoefficient); - handlePacketToSend(consecHeader); + + SdlPacket consecutiveFrame; + if (requiresEncryption) { + //Retrieve a chunk of the data into a temporary buffer to be encrypted + dataBuffer = new byte[bytesToWrite]; + System.arraycopy(data, currentOffset, dataBuffer, 0, bytesToWrite); + + encryptedData = new byte[TLS_MAX_RECORD_SIZE]; + Integer numberOfBytesEncrypted = sdlSec.encryptData(dataBuffer, encryptedData); + if (numberOfBytesEncrypted == null || (numberOfBytesEncrypted <= 0)) { + DebugTool.logError(TAG, "Unable to encrypt data"); + return; + } + + consecutiveFrame = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, numberOfBytesEncrypted, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), encryptedData, 0, numberOfBytesEncrypted, true); + } else { + consecutiveFrame = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), data, currentOffset, bytesToWrite, false); + } + + consecutiveFrame.setTransportRecord(activeTransports.get(sessionType)); + consecutiveFrame.setPriorityCoefficient(i + 2 + protocolMsg.priorityCoefficient); + handlePacketToSend(consecutiveFrame); currentOffset += bytesToWrite; } } else { messageID++; + if (requiresEncryption && data != null && data.length > 0) { + //Encrypt the data before sending + byte[] encryptedData = new byte[TLS_MAX_RECORD_SIZE]; + Integer numberOfBytesEncrypted = sdlSec.encryptData(data, encryptedData); + if (numberOfBytesEncrypted == null || (numberOfBytesEncrypted <= 0)) { + DebugTool.logError(TAG, "Unable to encrypt data"); + return; + } + //Put the encrypted bytes back into the data array + data = new byte[numberOfBytesEncrypted]; + System.arraycopy(encryptedData, 0, data, 0, numberOfBytesEncrypted); + } int dataLength = data != null ? data.length : 0; - SdlPacket header = SdlPacketFactory.createSingleSendData(sessionType, sessionID, dataLength, messageID, (byte) protocolVersion.getMajor(), data, protocolMsg.getPayloadProtected()); + SdlPacket header = SdlPacketFactory.createSingleSendData(sessionType, sessionID, dataLength, messageID, (byte) protocolVersion.getMajor(), data, requiresEncryption); header.setPriorityCoefficient(protocolMsg.priorityCoefficient); header.setTransportRecord(activeTransports.get(sessionType)); handlePacketToSend(header); @@ -1374,16 +1406,17 @@ public class SdlProtocolBase { if (packet.getPayload() != null && packet.getDataSize() > 0 && packet.isEncrypted()) { SdlSecurityBase sdlSec = iSdlProtocol.getSdlSecurity(); - byte[] dataToRead = new byte[4096]; + byte[] dataToRead = new byte[TLS_MAX_RECORD_SIZE]; - Integer iNumBytes = sdlSec.decryptData(packet.getPayload(), dataToRead); - if ((iNumBytes == null) || (iNumBytes <= 0)) { + Integer numberOfDecryptedBytes = sdlSec.decryptData(packet.getPayload(), dataToRead); + if ((numberOfDecryptedBytes == null) || (numberOfDecryptedBytes <= 0)) { return; } - byte[] decryptedData = new byte[iNumBytes]; - System.arraycopy(dataToRead, 0, decryptedData, 0, iNumBytes); + byte[] decryptedData = new byte[numberOfDecryptedBytes]; + System.arraycopy(dataToRead, 0, decryptedData, 0, numberOfDecryptedBytes); packet.payload = decryptedData; + packet.dataSize = numberOfDecryptedBytes; } if (packet.getFrameType().equals(FrameType.Control)) { diff --git a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java index a142cebad..99510b061 100644 --- a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java +++ b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java @@ -61,14 +61,14 @@ public class SdlPsm { private static final byte FIRST_FRAME_DATA_SIZE = 0x08; private static final int VERSION_MASK = 0xF0; //4 highest bits - private static final int COMPRESSION_MASK = 0x08; //4th lowest bit + private static final int ENCRYPTION_MASK = 0x08; //4th lowest bit private static final int FRAME_TYPE_MASK = 0x07; //3 lowest bits int state; int version; - boolean compression; + boolean encrypted; int frameType; int serviceType; int controlFrameInfo; @@ -97,7 +97,7 @@ public class SdlPsm { if (version == 0) { //It should never be 0 return ERROR_STATE; } - compression = (1 == ((rawByte & (byte) COMPRESSION_MASK) >> 3)); + encrypted = (1 == ((rawByte & (byte) ENCRYPTION_MASK) >> 3)); frameType = rawByte & (byte) FRAME_TYPE_MASK; @@ -194,7 +194,10 @@ public class SdlPsm { break; case SdlPacket.FRAME_TYPE_FIRST: - if (dataLength == FIRST_FRAME_DATA_SIZE) { + if (dataLength == FIRST_FRAME_DATA_SIZE || this.encrypted) { + //In a few production releases of core the first frame could be + //encrypted. Therefore it is not an error state if the first frame data + //length is greater than 8 in that case alone. break; } default: @@ -263,7 +266,7 @@ public class SdlPsm { public SdlPacket getFormedPacket() { if (state == FINISHED_STATE) { //Log.trace(TAG, "Finished packet."); - return new SdlPacket(version, compression, frameType, + return new SdlPacket(version, encrypted, frameType, serviceType, controlFrameInfo, sessionId, dataLength, messageId, payload); } else { |