From bbb1e6e2f467ee4bd7b9a8c800e4f07343d7d99b Mon Sep 17 00:00:00 2001
From: Johan Pascal <johan.pascal@belledonne-communications.com>
Date: Thu, 31 Mar 2016 17:42:30 +0200
Subject: [PATCH] Add ZRTP Commit packet hvi check on DHPart2 packet reception

---
 include/packetParser.h |  1 +
 src/packetParser.c     | 28 ++++++++++++++++++++++++++-
 test/bzrtpParserTest.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--
 test/bzrtpParserTest.h |  1 +
 test/bzrtpTest.c       |  1 +
 5 files changed, 79 insertions(+), 3 deletions(-)

--- /dev/null
+++ bzrtp-1.0.2/bbb1e6e2f467ee4bd7b9a8c800e4f07343d7d99b.patch
@@ -0,0 +1,183 @@
+From bbb1e6e2f467ee4bd7b9a8c800e4f07343d7d99b Mon Sep 17 00:00:00 2001
+From: Johan Pascal <johan.pascal@belledonne-communications.com>
+Date: Thu, 31 Mar 2016 17:42:30 +0200
+Subject: [PATCH] Add ZRTP Commit packet hvi check on DHPart2 packet reception
+
+---
+ include/packetParser.h |  1 +
+ src/packetParser.c     | 28 ++++++++++++++++++++++++++-
+ test/bzrtpParserTest.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--
+ test/bzrtpParserTest.h |  1 +
+ test/bzrtpTest.c       |  1 +
+ 5 files changed, 79 insertions(+), 3 deletions(-)
+
+diff --git a/include/packetParser.h b/include/packetParser.h
+index 622213f..95ec6d5 100644
+--- a/include/packetParser.h
++++ b/include/packetParser.h
+@@ -42,6 +42,7 @@
+ #define		BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN	0xa080
+ #define		BZRTP_PARSER_ERROR_UNMATCHINGMAC		0xa100
+ #define		BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE	0xa200
++#define		BZRTP_PARSER_ERROR_UNMATCHINGHVI		0xa400
+ 
+ #define		BZRTP_BUILDER_ERROR_INVALIDPACKET		0x5001
+ #define		BZRTP_BUILDER_ERROR_INVALIDMESSAGE		0x5002
+diff --git a/src/packetParser.c b/src/packetParser.c
+index 767c5d4..2ca5cdb 100644
+--- a/src/packetParser.c
++++ b/src/packetParser.c
+@@ -438,6 +438,32 @@ int bzrtp_packetParser(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpC
+ 						free (messageData);
+ 						return BZRTP_PARSER_ERROR_UNMATCHINGMAC;
+ 					}
++
++					/* Check the hvi received in the commit message  - RFC section 4.4.1.1*/
++					/* First compute the expected hvi */
++					/* hvi = hash(initiator's DHPart2 message(current zrtpPacket)) || responder's Hello message) using the agreed hash function truncated to 256 bits */
++					/* create a string with the messages concatenated */
++					{
++						uint8_t computedHvi[32];
++						uint16_t HelloMessageLength = zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength;
++						uint16_t DHPartHelloMessageStringLength = zrtpPacket->messageLength + HelloMessageLength;
++
++						uint8_t *DHPartHelloMessageString = (uint8_t *)malloc(DHPartHelloMessageStringLength*sizeof(uint8_t));
++
++						memcpy(DHPartHelloMessageString, input+ZRTP_PACKET_HEADER_LENGTH, zrtpPacket->messageLength);
++						memcpy(DHPartHelloMessageString+zrtpPacket->messageLength, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, HelloMessageLength);
++
++						zrtpChannelContext->hashFunction(DHPartHelloMessageString, DHPartHelloMessageStringLength, 32, computedHvi);
++
++						free(DHPartHelloMessageString);
++
++						/* Compare computed and received hvi */
++						if (memcmp(computedHvi, peerCommitMessageData->hvi, 32)!=0) {
++							free (messageData);
++							return BZRTP_PARSER_ERROR_UNMATCHINGHVI;
++						}
++					}
++
+ 				} else { /* if we are initiator(we didn't received any commit message and then no H2), we must check that H3=SHA256(SHA256(H1)) and the Hello message MAC */
+ 					uint8_t checkH2[32];
+ 					uint8_t checkH3[32];
+@@ -482,7 +508,7 @@ int bzrtp_packetParser(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpC
+ 				/* attach the message structure to the packet one */
+ 				zrtpPacket->messageData = (void *)messageData;
+ 
+-				/* the parsed commit packet must be saved as it is used to generate the total_hash */
++				/* the parsed packet must be saved as it is used to generate the total_hash */
+ 				zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t));
+ 				memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */
+ 			}
+diff --git a/test/bzrtpParserTest.c b/test/bzrtpParserTest.c
+index cf67bb5..faa1b8c 100644
+--- a/test/bzrtpParserTest.c
++++ b/test/bzrtpParserTest.c
+@@ -101,7 +101,8 @@ uint8_t mackeyr[32] = {0x3a, 0xa5, 0x22, 0x43, 0x26, 0x13, 0x8f, 0xd6, 0x54, 0x5
+ uint8_t zrtpkeyi[16] = {0x22, 0xf6, 0xea, 0xaa, 0xa4, 0xad, 0x53, 0x30, 0x71, 0x97, 0xcc, 0x68, 0x6b, 0xb0, 0xcb, 0x55};
+ uint8_t zrtpkeyr[16] = {0x09, 0x50, 0xcd, 0x9e, 0xc2, 0x78, 0x54, 0x31, 0x93, 0x2e, 0x99, 0x31, 0x15, 0x58, 0xd0, 0x2a};
+ 
+-void test_parser(void) {
++
++void test_parser_param(uint8_t hvi_trick) {
+ 	int i, retval;
+ 	bzrtpPacket_t *zrtpPacket;
+ 
+@@ -159,13 +160,34 @@ void test_parser(void) {
+ 		/* parse a packet string from patterns */
+ 		zrtpPacket = bzrtp_packetCheck(patternZRTPPackets[i], patternZRTPMetaData[i][0], (patternZRTPMetaData[i][1])-1, &retval);
+ 		retval +=  bzrtp_packetParser((patternZRTPMetaData[i][2]==0x87654321)?context12345678:context87654321, (patternZRTPMetaData[i][2]==0x87654321)?context12345678->channelContext[0]:context87654321->channelContext[0], patternZRTPPackets[i], patternZRTPMetaData[i][0], zrtpPacket);
+-		/*printf("parsing Ret val is %x index is %d\n", retval, i);*/
++		if (hvi_trick==0) {
++			CU_ASSERT_EQUAL_FATAL(retval,0);
++		} else { /* when hvi trick is enable, the DH2 parsing shall fail and return BZRTP_PARSER_ERROR_UNMATCHINGHVI */
++			if (zrtpPacket->messageType==MSGTYPE_DHPART2) {
++				CU_ASSERT_EQUAL_FATAL(retval, BZRTP_PARSER_ERROR_UNMATCHINGHVI);
++				/* We shall then anyway skip the end of the test */
++				/* reset pointers to selfHello packet in order to avoid double free */
++				context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
++				context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
++
++				bzrtp_destroyBzrtpContext(context87654321, 0x87654321);
++				bzrtp_destroyBzrtpContext(context12345678, 0x12345678);
++
++				return;
++
++			} else {
++				CU_ASSERT_EQUAL_FATAL(retval,0);
++			}
++		}
++			bzrtp_message("parsing Ret val is %x index is %d\n", retval, i);
+ 		/* We must store some packets in the context if we want to be able to parse further packets */
+ 		if (zrtpPacket->messageType==MSGTYPE_HELLO) {
+ 			if (patternZRTPMetaData[i][2]==0x87654321) {
+ 				context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
++				context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
+ 			} else {
+ 				context87654321->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
++				context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
+ 			}
+ 			freePacketFlag = 0;
+ 		}
+@@ -205,13 +227,38 @@ void test_parser(void) {
+ 		if (freePacketFlag == 1) {
+ 			bzrtp_freeZrtpPacket(zrtpPacket);
+ 		}
++
++		/* modify the hvi stored in the peerPackets, this shall result in parsing failure on DH2 packet */
++		if (hvi_trick == 1) {
++			if (zrtpPacket->messageType==MSGTYPE_COMMIT) {
++				if (patternZRTPMetaData[i][2]==0x87654321) {
++					bzrtpCommitMessage_t *peerCommitMessageData;
++					peerCommitMessageData = (bzrtpCommitMessage_t *)zrtpPacket->messageData;
++					peerCommitMessageData->hvi[0]=0xFF;
++				}
++			}
++		}
++
+ 	}
+ 
++	/* reset pointers to selfHello packet in order to avoid double free */
++	context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
++	context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
++
++
+ 	bzrtp_destroyBzrtpContext(context87654321, 0x87654321);
+ 	bzrtp_destroyBzrtpContext(context12345678, 0x12345678);
+ 
+ }
+ 
++void test_parser(void) {
++	test_parser_param(0);
++}
++
++void test_parser_hvi(void) {
++	test_parser_param(1);
++}
++
+ /* context structure mainly used by statemachine test, but also needed by parserComplete to get the zid Filename */
+ typedef struct my_Context_struct {
+ 	unsigned char nom[30]; /* nom du contexte */
+diff --git a/test/bzrtpParserTest.h b/test/bzrtpParserTest.h
+index 8a70878..74548f2 100644
+--- a/test/bzrtpParserTest.h
++++ b/test/bzrtpParserTest.h
+@@ -20,5 +20,6 @@
+  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  */
+ void test_parser(void);
++void test_parser_hvi(void);
+ void test_parserComplete(void);
+ void test_stateMachine(void);
+diff --git a/test/bzrtpTest.c b/test/bzrtpTest.c
+index dba439d..a9fa90f 100644
+--- a/test/bzrtpTest.c
++++ b/test/bzrtpTest.c
+@@ -75,6 +75,7 @@ int main(int argc, char *argv[] ) {
+ 	/* Add the parser suite to the registry */
+ 	parserTestSuite = CU_add_suite("Bzrtp ZRTP Packet Parser", NULL, NULL);
+ 	CU_add_test(parserTestSuite, "Parse", test_parser);
++	CU_add_test(parserTestSuite, "Parse hvi check fail", test_parser_hvi);
+ 	CU_add_test(parserTestSuite, "Parse Exchange", test_parserComplete);
+ 	CU_add_test(parserTestSuite, "State machine", test_stateMachine);
+ 
--- bzrtp-1.0.2.orig/include/packetParser.h
+++ bzrtp-1.0.2/include/packetParser.h
@@ -42,6 +42,7 @@
 #define		BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN	0xa080
 #define		BZRTP_PARSER_ERROR_UNMATCHINGMAC		0xa100
 #define		BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE	0xa200
+#define		BZRTP_PARSER_ERROR_UNMATCHINGHVI		0xa400
 
 #define		BZRTP_BUILDER_ERROR_INVALIDPACKET		0x5001
 #define		BZRTP_BUILDER_ERROR_INVALIDMESSAGE		0x5002
--- bzrtp-1.0.2.orig/src/packetParser.c
+++ bzrtp-1.0.2/src/packetParser.c
@@ -438,6 +438,32 @@ int bzrtp_packetParser(bzrtpContext_t *z
 						free (messageData);
 						return BZRTP_PARSER_ERROR_UNMATCHINGMAC;
 					}
+
+					/* Check the hvi received in the commit message  - RFC section 4.4.1.1*/
+					/* First compute the expected hvi */
+					/* hvi = hash(initiator's DHPart2 message(current zrtpPacket)) || responder's Hello message) using the agreed hash function truncated to 256 bits */
+					/* create a string with the messages concatenated */
+					{
+						uint8_t computedHvi[32];
+						uint16_t HelloMessageLength = zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength;
+						uint16_t DHPartHelloMessageStringLength = zrtpPacket->messageLength + HelloMessageLength;
+
+						uint8_t *DHPartHelloMessageString = (uint8_t *)malloc(DHPartHelloMessageStringLength*sizeof(uint8_t));
+
+						memcpy(DHPartHelloMessageString, input+ZRTP_PACKET_HEADER_LENGTH, zrtpPacket->messageLength);
+						memcpy(DHPartHelloMessageString+zrtpPacket->messageLength, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, HelloMessageLength);
+
+						zrtpChannelContext->hashFunction(DHPartHelloMessageString, DHPartHelloMessageStringLength, 32, computedHvi);
+
+						free(DHPartHelloMessageString);
+
+						/* Compare computed and received hvi */
+						if (memcmp(computedHvi, peerCommitMessageData->hvi, 32)!=0) {
+							free (messageData);
+							return BZRTP_PARSER_ERROR_UNMATCHINGHVI;
+						}
+					}
+
 				} else { /* if we are initiator(we didn't received any commit message and then no H2), we must check that H3=SHA256(SHA256(H1)) and the Hello message MAC */
 					uint8_t checkH2[32];
 					uint8_t checkH3[32];
@@ -482,7 +508,7 @@ int bzrtp_packetParser(bzrtpContext_t *z
 				/* attach the message structure to the packet one */
 				zrtpPacket->messageData = (void *)messageData;
 
-				/* the parsed commit packet must be saved as it is used to generate the total_hash */
+				/* the parsed packet must be saved as it is used to generate the total_hash */
 				zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t));
 				memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */
 			}
--- bzrtp-1.0.2.orig/test/bzrtpParserTest.c
+++ bzrtp-1.0.2/test/bzrtpParserTest.c
@@ -101,7 +101,8 @@ uint8_t mackeyr[32] = {0x3a, 0xa5, 0x22,
 uint8_t zrtpkeyi[16] = {0x22, 0xf6, 0xea, 0xaa, 0xa4, 0xad, 0x53, 0x30, 0x71, 0x97, 0xcc, 0x68, 0x6b, 0xb0, 0xcb, 0x55};
 uint8_t zrtpkeyr[16] = {0x09, 0x50, 0xcd, 0x9e, 0xc2, 0x78, 0x54, 0x31, 0x93, 0x2e, 0x99, 0x31, 0x15, 0x58, 0xd0, 0x2a};
 
-void test_parser(void) {
+
+void test_parser_param(uint8_t hvi_trick) {
 	int i, retval;
 	bzrtpPacket_t *zrtpPacket;
 	
@@ -159,13 +160,34 @@ void test_parser(void) {
 		/* parse a packet string from patterns */
 		zrtpPacket = bzrtp_packetCheck(patternZRTPPackets[i], patternZRTPMetaData[i][0], (patternZRTPMetaData[i][1])-1, &retval);
 		retval +=  bzrtp_packetParser((patternZRTPMetaData[i][2]==0x87654321)?context12345678:context87654321, (patternZRTPMetaData[i][2]==0x87654321)?context12345678->channelContext[0]:context87654321->channelContext[0], patternZRTPPackets[i], patternZRTPMetaData[i][0], zrtpPacket);
-		/*printf("parsing Ret val is %x index is %d\n", retval, i);*/
+		if (hvi_trick==0) {
+			CU_ASSERT_EQUAL_FATAL(retval,0);
+		} else { /* when hvi trick is enable, the DH2 parsing shall fail and return BZRTP_PARSER_ERROR_UNMATCHINGHVI */
+			if (zrtpPacket->messageType==MSGTYPE_DHPART2) {
+				CU_ASSERT_EQUAL_FATAL(retval, BZRTP_PARSER_ERROR_UNMATCHINGHVI);
+				/* We shall then anyway skip the end of the test */
+				/* reset pointers to selfHello packet in order to avoid double free */
+				context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
+				context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
+
+				bzrtp_destroyBzrtpContext(context87654321, 0x87654321);
+				bzrtp_destroyBzrtpContext(context12345678, 0x12345678);
+
+				return;
+
+			} else {
+				CU_ASSERT_EQUAL_FATAL(retval,0);
+			}
+		}
+			bzrtp_message("parsing Ret val is %x index is %d\n", retval, i);
 		/* We must store some packets in the context if we want to be able to parse further packets */
 		if (zrtpPacket->messageType==MSGTYPE_HELLO) {
 			if (patternZRTPMetaData[i][2]==0x87654321) {
 				context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
+				context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
 			} else {
 				context87654321->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
+				context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket;
 			}
 			freePacketFlag = 0;
 		}
@@ -205,13 +227,38 @@ void test_parser(void) {
 		if (freePacketFlag == 1) {
 			bzrtp_freeZrtpPacket(zrtpPacket);
 		}
+
+		/* modify the hvi stored in the peerPackets, this shall result in parsing failure on DH2 packet */
+		if (hvi_trick == 1) {
+			if (zrtpPacket->messageType==MSGTYPE_COMMIT) {
+				if (patternZRTPMetaData[i][2]==0x87654321) {
+					bzrtpCommitMessage_t *peerCommitMessageData;
+					peerCommitMessageData = (bzrtpCommitMessage_t *)zrtpPacket->messageData;
+					peerCommitMessageData->hvi[0]=0xFF;
+				}
+			}
+		}
+
 	}
 
+	/* reset pointers to selfHello packet in order to avoid double free */
+	context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
+	context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL;
+
+
 	bzrtp_destroyBzrtpContext(context87654321, 0x87654321);
 	bzrtp_destroyBzrtpContext(context12345678, 0x12345678);
 
 }
 
+void test_parser(void) {
+	test_parser_param(0);
+}
+
+void test_parser_hvi(void) {
+	test_parser_param(1);
+}
+
 /* context structure mainly used by statemachine test, but also needed by parserComplete to get the zid Filename */
 typedef struct my_Context_struct {
 	unsigned char nom[30]; /* nom du contexte */
--- bzrtp-1.0.2.orig/test/bzrtpParserTest.h
+++ bzrtp-1.0.2/test/bzrtpParserTest.h
@@ -20,5 +20,6 @@
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 void test_parser(void);
+void test_parser_hvi(void);
 void test_parserComplete(void);
 void test_stateMachine(void);
--- bzrtp-1.0.2.orig/test/bzrtpTest.c
+++ bzrtp-1.0.2/test/bzrtpTest.c
@@ -86,6 +86,7 @@ int main(int argc, char *argv[] ) {
 	/* Add the parser suite to the registry */
 	parserTestSuite = CU_add_suite("Bzrtp ZRTP Packet Parser", NULL, NULL);
 	CU_add_test(parserTestSuite, "Parse", test_parser);
+	CU_add_test(parserTestSuite, "Parse hvi check fail", test_parser_hvi);
 	CU_add_test(parserTestSuite, "Parse Exchange", test_parserComplete);
 	CU_add_test(parserTestSuite, "State machine", test_stateMachine);
 
