none
Doubts in [MS-OXORMMS]: Unprotecting an RMS enabled message. RRS feed

  • Question

  • Hi,
    I am developing an application that would decrypt an RMS enabled message. From the protocol document MS-OXORMMS i found that the message is compressed using a zlib algorithm. 
    Based on the above document and some help from Microsoft i got hold of the following documentation page:

    What this page says is that if i need to compress a message/license, i need to first split the file into sets of 4096 bytes(the last set might have <= 4096 bytes). The page then gives details on prefix/headers/length values that are added.
    When i apply a similar (but opposite) logic for decompression, i find only the first block decompressing properly. Even at that point zlib uncompression logic returns a Z_DATA_ERROR.
    The rest of the blocks do not decompress at all. As a result i am stuck in decompressing the RMS protected message.

    Further i find some trivial mistakes in this protocol document
    1. In the Appendix A, point 3 it claims to use a zlib version greater than 1.2.3, which is not present on zlib's home site and something i am still searching for. :)

    2. In section 3.1.4.2.1(pg 20) the first bullet talks of a section 3.1.4.1.4 which talks of the decompression logic(which i very badly need). However this section itself doesn't exist.

    Can someone help me on this?
    Viswa.
    Tuesday, August 4, 2009 9:56 AM

Answers

  • Hi Viswa,

    in MS-OXORMMS, section 3.1.4.1.2, there is a description of the message.rpmsg attachment.  There is a note <4> that contains the header, block id and sizes of the decompressed and compressed data.

    ---------------<excerpt>---------------------
    3.1.4.1.2  Format of the Storage Container
    The "message.rpmsg" attachment is a ZLIB compressed file that contains the standard ZLIB
    header <4> followed by the compressed storage container. Figure 1 shows the format of the
    storage container.

    ...

    <4> Section 3.1.4.1.2: The specific format of the message.rpmsg header is described as
    follows:
     
    CHAR STRING Header - Value is "\x76\xE8\x04\x60\xC4\x11\xE3\x86" in little-endian
    format.
    DWORD ULCheck - Value is 0x00000FA0
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.
    DWORD SizeAfterInflation - Size of byte steam after compressing.
     
    The bytes following the header contain the compressed bits of the storage container. The size
    of the compressed bits is determined by SizeBeforeInflation. 

    ---------------<\excerpt>---------------------

    Sorry that I forgot to include the declaration of the ZLIBDRMHEADER struct:

    typedef

     

    struct

    {

         ULONG ulCheck;  // 0x00000FA0

         ULONG cbUncompressed;  // size after inflation

         ULONG cbCompressed;  // size before inflation

    } ZLIBDRMHEADER;

    This accomodates the last three elements in <4>. 

    What error do you see and does any of the compressed data inflate properly?  Can you inspect the output buffer to see if any of it looks correct?

    Tom


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 12:46 PM
    Moderator
  • Viswa,

    I just noticed another problem in MS-OXORMMS.  Although it looks like your code is reading these values in the correct order, I just wanted to call your attention to it:

    <4> Section 3.1.4.1.2: The specific format of the message.rpmsg header is described as
    follows:
     
    CHAR STRING Header - Value is "\x76\xE8\x04\x60\xC4\x11\xE3\x86" in little-endian format.
    DWORD ULCheck - Value is 0x00000FA0
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.
    DWORD SizeAfterInflation - Size of byte steam after compressing.

    The size DWORD's should be swapped like this:

    DWORD SizeAfterInflation - Size of byte steam after compressing.
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 8:20 PM
    Moderator
  • Viswa,

    Thanks for the followup question.  I've spent a little time modifying my sample code in order to inflate the DRMLicense property in stream __substg1.0_80011102-00000000 and I do see the bytes at the beginning of the uncompressed data.   I believe these represent the total length of the following uncompressed license data.  In my case, for example, the DRMLicense data began with:

    0000h: 3E 44 00 00 3C 00 58 00 72 00 4D 00 4C 00 20 00  >D..<.X.r.M.L. .

    The end of the data is:

    8870h: 3E 00 3C 00 2F 00 58 00 72 00 4D 00 4C 00 3E 00  >.<./.X.r.M.L.>.

    The final byte is at offset 8880h.  Subtract these "length" bytes and you get (8880h - 3) = 887Dh, which is in single bytes.  Divide that by 2 for wide characters and you get 443Eh. 

    I will make sure the specification has mention of this and if not, update it accordingly.  Again, thanks for bringing it up.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Wednesday, September 30, 2009 12:23 AM
    Moderator
  • Viswa,

    I received confirmation that the bytes in question are indeed the length of the XrML data (or Use License data), uncompressed that follows.

    We will be updating MS-OXORMMS to reflect that fact with explanation of these preceding bytes.

    Thanks for your patience on this issue.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Wednesday, October 14, 2009 4:13 AM
    Moderator
  • Hi Viswa,

    I actually wrote a blog that you might find helpful.   I explain how the stream names are formed in that blog.  DRM License property is described in MS-OXPROPS:

    2.544   PidNameRightsManagementLicense
    Canonical name: PidNameRightsManagementLicense
    Property set: PS_PUBLIC_STRINGS {00020329-0000-0000-C000-000000000046}
    Property name: DRMLicense
    Data type: PtypMultipleBinary, 0x1102
    Area: Secure Messaging Properties
    References: [MS-OXORMMS]

    Hence, the 0x1102. 

    MS-OXMSG Section 2.2.3 Named Property Mapping Storage, descibes how I mapped the DRMLicense property id PidNameRightsManagementLicense to 0x8001 property id, which is index 1 in the entry stream. 

    I hope this helps.

    Tom Jebo
    Microsoft Open Specifications Documentation Support
    • Proposed as answer by Chris Mullaney Wednesday, February 10, 2010 10:24 PM
    • Marked as answer by Chris Mullaney Friday, February 19, 2010 5:49 PM
    Wednesday, February 3, 2010 4:39 PM

All replies

  • Hi Viswanathan, thanks for your post regarding the [MS-OXORMMS] protocol specification. One of my colleagues will contact you shortly.

    Regards,
    Bill Wesse


    Escalation Engineer
    Tuesday, August 4, 2009 1:20 PM
  • Greetings Viswa.,

    I am the engineer who has taken ownership of your issue. I am investigating this and will update you as things progress.

    Regarding Section 3.1.4.1.2, I notice this Section on Page 10 and 11 of the specified document. Page 10 contains a short sentence, while Page 11 contains a figure depicting "Message.rpmsg Storage".

    Dominic Salemno
    Senior Support Escalation Engineer

    Wednesday, August 5, 2009 1:33 PM
  • Hi Dominic,
    I guess the protocol document got updated recently (to version 3.0) and this seems to have clarified the aspersions i had. That's good.

    Further when can i expect some progress on this issue? I hope you have the necessary details on the issue i am seeing?
    Thanks,
    Viswa.
    Wednesday, August 5, 2009 7:03 PM
  • Viswa.,

    I am still investigating this issue. I will update you as things progress.

    Dominic Salemno
    Senior Support Escalation Engineer
    Tuesday, August 11, 2009 2:13 PM
  • Viswa.,

    Can you send me a sample file to dochelp@microsoft.com regarding decompression?

    Dominic Salemno
    Senior Support Escalation Engineer
    Tuesday, August 11, 2009 6:00 PM
  • Viswa.,

    I have received your files and am continuing to investigate this issue. I will update you as things progress.

    Dominic Salemno
    Senior Support Escalation Engineer
    Thursday, August 13, 2009 10:37 PM
  • Viswa,

    regarding your first question:
    >> 1. In the Appendix A, point 3 it claims to use a zlib version greater than 1.2.3, which is not present on zlib's home site and something i am still searching for. :)

    MS-OXORMMS will be updated to reflect that Zlib 1.2.3 is the currently required version (as opposed to "greater" than 1.2.3). 


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, August 14, 2009 1:25 PM
    Moderator
  • Viswa,

    Just another update to clarify what versions of ZLIB library are used by Outlook: 

    Outlook          ZLIB library version
    ------------------------------------
    2003              1.1.4 
    2007              1.2.3

     


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, August 14, 2009 5:28 PM
    Moderator
  • Tom,
    Thanks for the clarification.

    Dominic,
    Can i have some update on the files i had sent.
    Thanks,
    VIswa.
    Tuesday, August 18, 2009 6:14 AM
  • Viswa.,

    I am still investigating this issue. I should have some more information for you soon.

    Dominic Salemno
    Senior Support Escalation Engineer
    Friday, August 21, 2009 6:55 PM
  • Dominic,
    Any update on this? This seems to be dormant for some time.
    Thanks,
    Viswa.
    Monday, August 24, 2009 5:39 AM
  • Viswa.,

    I am still investigating this issue. I will update you as things progress.

    Dominic Salemno
    Senior Support Escalation Engineer

    Tuesday, August 25, 2009 1:47 PM
  • Dominic,
    Any progress? Its been quite some time.
    Viswa
    Wednesday, September 9, 2009 6:15 PM
  • Viswa,

    I am handling your case now.  I have been in communication with our product team and am currently waiting for a clarification from them on why the decompression isn't working.  I appreciate your patience and will try to get an answer to you as soon as possible.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Wednesday, September 9, 2009 6:22 PM
    Moderator
  • Hi Viswa,

    Sorry for the delay in getting back to you.  MS-OXORMMS is going to be updated to clarify a few points around decompression per your observations.  At the same time, I've been finishing up some small sample code that should help you understand how to use inflate() properly.  The main point being that you use the same z_stream struct in each iteration of the loop calling inflate().  There are some finer points I can show you in the code.  

    I should have this code ready to share with you by end of the day today.  If not, it will be ready in the morning.  Thanks for your patience.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Thursday, September 24, 2009 6:05 PM
    Moderator
  • Viswa,

    The following code fragment will show how to use inflate on a file containing a rights managed message.  The assumption is that you have a file i.e. message.rpmsg containing the compressed message as input and a file i.e. uncompressed.data that this loop will write each uncompressed block to.  These are assumed to be passed to the command line.  Also, I've omitted irrelevant code (like processing the command line) and replaced it with "..." so, you will have to fill in some details.  Let me know if you have any questions after reviewing this code:

    -------------------------------<snip>-----------------------------

    #define
    ZLIB_BUFFER_SIZE (4 * 1024)

    #define

     

    ZLIB_DRM_HEADER_MAGIC (0x0FA0)

    int

     

    main(int argc, char *argv[])

    {

     

    FILE* streamIn = NULL;

    FILE* streamOut = NULL;

     

    ...

    static
    const char c_szCompressedDrmMessageHeader[] = "\x76\xE8\x04\x60\xC4\x11\xE3\x86";

    CHAR szHeader[

    sizeof(c_szCompressedDrmMessageHeader)] = {0};

    byte rgbOriginal [ZLIB_BUFFER_SIZE];

    byte rgbCompressed[ZLIB_BUFFER_SIZE*2];

    streamIn=fopen(argv[paramFileIn],

    "rb");

     

    if (streamIn==NULL)

     

    return 0;

     

    if (fread(szHeader, 1, sizeof(szHeader)-1, streamIn) == 0)

     

    return 0;

     

    if (strcmp(szHeader, c_szCompressedDrmMessageHeader) != 0)

    {

    printf(

    "Not a DRM message header");

     

    return -1;

    }

    streamOut=fopen(argv[paramFileOut],

    "wb");

     

    if (streamOut==NULL)

     

    return 0;

    ...

    {

    z_stream zcpr;

    ZLIBDRMHEADER theHeader = {0};

     

    int ret=Z_OK;

     

    long lOrigDone = 0;

     

    int step=0;

    memset(&zcpr,0,

    sizeof(z_stream));

    inflateInit(&zcpr);

     ...

     

    while (true)

    {

    ZeroMemory(rgbOriginal,

    sizeof(rgbOriginal));

     

    if (fread(&theHeader, sizeof(theHeader), 1, streamIn) == 0)

    {

    printf(

    "read failed");

     

    break;

    }

     

    if (theHeader.ulCheck != ZLIB_DRM_HEADER_MAGIC)

    {

    printf(

    "Header Check failed!");

     

    return -3;

    }

     

    if (fread(rgbCompressed, theHeader.cbCompressed, 1, streamIn) == 0)

    {

    printf(

    "read failed");

     

    return -4;

    }

    zcpr.next_in = rgbCompressed;

    zcpr.next_out = rgbOriginal;

    zcpr.avail_in = theHeader.cbCompressed;

    zcpr.avail_out =

    sizeof(rgbOriginal);

     

    do

    {

    ret=inflate(&zcpr,Z_SYNC_FLUSH);

    step++;

    }

    while (ret==Z_OK);

    fwrite(rgbOriginal, 1, theHeader.cbUncompressed, streamOut);

    }

    lSizeUncpr=zcpr.total_out;

    inflateEnd(&zcpr);

    }

    }

    ...

    fclose(streamIn);

    fclose(streamOut);

     

    return 0;

    }

    -------------------------------<\snip>-----------------------------

    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Thursday, September 24, 2009 6:56 PM
    Moderator
  • Tom,

    Thanks for the code - looks like it might help on a similar issue I'm having with offline address books, so I'd like to investigate this further.

    I had a look at it, and it appears in the redaction that something got added or deleted - there appears to be mismatched braces. Here is what I get:
    #define ZLIB_BUFFER_SIZE (4 * 1024) 
    #define ZLIB_DRM_HEADER_MAGIC (0x0FA0)
    int main(int argc, char *argv[]) 
    {
    	FILE* streamIn = NULL; 
    	FILE* streamOut = NULL;
    	...
    
    	static const char c_szCompressedDrmMessageHeader[] = "\x76\xE8\x04\x60\xC4\x11\xE3\x86"; 
    	CHAR szHeader[ sizeof(c_szCompressedDrmMessageHeader)] = {0}; 
    	byte rgbOriginal [ZLIB_BUFFER_SIZE];
    	byte rgbCompressed[ZLIB_BUFFER_SIZE*2];
    	streamIn=fopen(argv[paramFileIn],"rb"); 
    	if (streamIn==NULL) 
    		return 0; 
    	if (fread(szHeader, 1, sizeof(szHeader)-1, streamIn) == 0) 
    		return 0; 
    	if (strcmp(szHeader, c_szCompressedDrmMessageHeader) != 0)  {
    		printf("Not a DRM message header"); 
    		return -1; 
    	}
    	streamOut=fopen(argv[paramFileOut], "wb"); 
    	if (streamOut==NULL) 
    		return 0; 
    	...
    
    	{
    		z_stream zcpr;
    		ZLIBDRMHEADER theHeader = {0};
    		int ret=Z_OK; 
    		long lOrigDone = 0; 
    		int step=0; 
    		memset(&zcpr,0, sizeof(z_stream)); 
    		inflateInit(&zcpr);
    		...
    		while (true) 
    		{
    			ZeroMemory(rgbOriginal, sizeof(rgbOriginal)); 
    			if (fread(&theHeader, sizeof(theHeader), 1, streamIn) == 0)  {
    				printf("read failed"); 
    				break; 
    			}
    			if (theHeader.ulCheck != ZLIB_DRM_HEADER_MAGIC) {
    				printf("Header Check failed!"); 
    				return -3; 
    			}
    			if (fread(rgbCompressed, theHeader.cbCompressed, 1, streamIn) == 0)  {
    				printf("read failed"); 
    				return -4; 
    			}
    			zcpr.next_in = rgbCompressed;
    			zcpr.next_out = rgbOriginal;
    			zcpr.avail_in = theHeader.cbCompressed;
    			zcpr.avail_out = sizeof(rgbOriginal); 
    			do {
    				ret=inflate(&zcpr,Z_SYNC_FLUSH);
    				step++;
    			} while (ret==Z_OK); 
    			fwrite(rgbOriginal, 1, theHeader.cbUncompressed, streamOut);
    		}
    		lSizeUncpr=zcpr.total_out;
    		inflateEnd(&zcpr);
    	}
    }
    ...
    fclose(streamIn);
    fclose(streamOut);
    return 0; 
    }
    Can you try as a "code block" formatted area?
    Friday, September 25, 2009 3:32 AM
  • Tom,
    Where can i get the definition of ZLIBDRMHEADER ?
    I checked around the SDK files and couldn't get hold of this.

    Further i tried this following code piece using some logic you presented and the decompression failed. If you could answer either questions it would be helpful.
    int decompress_one_file()
    {
       z_stream stream;
       int err;
       char *infileName = "E:\\temp\\Cvzlibdecrypt\\Debug\\message.rpmsg";//sample input file
       char *outfileName = "E:\\temp\\Cvzlibdecrypt\\Debug\\message.out";//sample output file
       gzFile infile = gzopen(infileName, "rb");
       FILE *opfile = fopen(outfileName,"wb");
       if (!infile || !opfile) return -1;
    
       //check if the byte follows the pattern
       Byte val;
       Byte marker[4];
       Byte ulongRead[4];
       int num_read = 0,cnt = 0;
    
       for(cnt=0;cnt<8;cnt++)
       {
    	   if(!((num_read = gzread(infile,&val,1)) > 0 && val == rpMsgPattern[cnt]))//similar to the RMS header pattern
    	   {
    		   printf("File is not a valid rpmsg\n");
    			gzclose(infile);
    			return -1;
    	   }
       }
       printf("File has a valid prefix\n");
       //get the marker
    
       bool isDataAvailable = true;
       int step = 0;
       memset(&stream,0, sizeof(z_stream)); 
       err = inflateInit(&stream);
       if (err != Z_OK) return err;
       do
       {
    	   ULONG ulUncSize = 0;
    	   ULONG ulComSize = 0;
    	   if( (num_read = gzread(infile,marker,sizeof(marker))) == 4 )
    	   {
    		   if( (marker[0] == 0xA0 && marker[1] == 0x0F) || (marker[0] == 0x0F && marker[1] == 0xA0))
    		   {
    			   printf("block has a valid marker\n");
    		   }
    		   else
    		   {
    				isDataAvailable = false;
    				continue;
    		   }
    	   }
    	   //read the uncompressed byte size
    	   if((num_read = gzread(infile,ulongRead,sizeof(ulongRead))) > 0)
    	   {
    		   printf("Successfully read the size after decryption:");
    		   for(cnt = 0;cnt<4;cnt++)
    		   {
    			   ulUncSize += (ULONG)ulongRead[cnt] * (ULONG)convToUlong[cnt];//uncompressed size
    		   }
    	   }
    	   else
    	   {
    		   isDataAvailable = false;
    			continue;
    	   }
    
    	   printf("%ld\n",ulUncSize);
    
    	   //read the compressed byte size
    	   if((num_read = gzread(infile,ulongRead,sizeof(ulongRead))) > 0)
    	   {
    		   printf("Successfully read the size before decryption:");
    		   for(cnt = 0;cnt<4;cnt++)
    		   {
    			   ulComSize += (ULONG)ulongRead[cnt] * (ULONG)convToUlong[cnt];//Compressed size
    		   }
    	   }
    	   else
    		   isDataAvailable = false;
    
    	   printf("%ld\n",ulComSize);
    	   Byte FAR *inpByte = new Byte FAR [ulComSize];
    	   Byte *opByte = new Byte FAR [ulUncSize];
    	   if( !inpByte || !opByte )
    	   {
    		printf("No memory available");
    		gzclose(infile);
    		fclose(opfile);
    		isDataAvailable = false;
    		continue;
    	   }
    	   if((num_read = gzread(infile,inpByte,ulComSize)) <= 0)
    	   {
    		printf("Unable to read %d bytes, read only %d",ulComSize,num_read);
    		isDataAvailable = false;
    		delete []inpByte;
    		delete []opByte;
    		continue;
    	   }
     
    	   for(int i=0;i<ulUncSize;i++)
    		   opByte[i] = 0x00;
    	   stream.next_in = inpByte;
    	   stream.avail_in = ulComSize;
    	    /* Check for source > 64K on 16-bit machine: */
    		if ((uLong)stream.avail_in != ulComSize) return Z_BUF_ERROR;
    
    		stream.avail_out = ulUncSize;
    	   	stream.next_out = opByte;
    	    	/* Check for source > 64K on 16-bit machine: */
    		if ((uLong)stream.avail_out != ulUncSize) return Z_BUF_ERROR;
    
                     //Where i do the inflate ...based on what has been shared in the previous code snippets
    		do
    		{
    			err=inflate(&stream,Z_SYNC_FLUSH);
    			step++;
    		} while (err==Z_OK); 
    		fwrite(opByte, 1, ulUncSize, opfile);
    	   //	   gzclearerr(infile);
    	   delete []inpByte;
    	   delete []opByte;
    	   inpByte = NULL;
    	   opByte = NULL;
       }while(isDataAvailable);
    	err = inflateEnd(&stream);
    	return err;
       
       if (num_read == -1)
            printf("Error in decompression\n");
    
       gzclose(infile);
       fclose(opfile);
    
    }

    Friday, September 25, 2009 10:16 AM
  • Hi Viswa,

    in MS-OXORMMS, section 3.1.4.1.2, there is a description of the message.rpmsg attachment.  There is a note <4> that contains the header, block id and sizes of the decompressed and compressed data.

    ---------------<excerpt>---------------------
    3.1.4.1.2  Format of the Storage Container
    The "message.rpmsg" attachment is a ZLIB compressed file that contains the standard ZLIB
    header <4> followed by the compressed storage container. Figure 1 shows the format of the
    storage container.

    ...

    <4> Section 3.1.4.1.2: The specific format of the message.rpmsg header is described as
    follows:
     
    CHAR STRING Header - Value is "\x76\xE8\x04\x60\xC4\x11\xE3\x86" in little-endian
    format.
    DWORD ULCheck - Value is 0x00000FA0
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.
    DWORD SizeAfterInflation - Size of byte steam after compressing.
     
    The bytes following the header contain the compressed bits of the storage container. The size
    of the compressed bits is determined by SizeBeforeInflation. 

    ---------------<\excerpt>---------------------

    Sorry that I forgot to include the declaration of the ZLIBDRMHEADER struct:

    typedef

     

    struct

    {

         ULONG ulCheck;  // 0x00000FA0

         ULONG cbUncompressed;  // size after inflation

         ULONG cbCompressed;  // size before inflation

    } ZLIBDRMHEADER;

    This accomodates the last three elements in <4>. 

    What error do you see and does any of the compressed data inflate properly?  Can you inspect the output buffer to see if any of it looks correct?

    Tom


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 12:46 PM
    Moderator
  • Thanks Brad,

    Sorry about that.  Here's effectively the whole program.  Let me know if this is better. 
    #define ZLIB_BUFFER_SIZE  (4 * 1024)
    #define ZLIB_DRM_HEADER_MAGIC (0x0FA0)
    
    typedef struct
    {
    	ULONG ulCheck;
    	ULONG cbUncompressed;
    	ULONG cbCompressed;
    } ZLIBDRMHEADER;
    
    int main(int argc, char *argv[])
    {
    
        int BlockSizeUncompress=0x1000;
        int cprLevel=Z_BEST_COMPRESSION;
        long lCompressedSize=0;
        long lSizeUncpr;
        DWORD dwGetTick,dwMsecQP;
        LARGE_INTEGER li_qp,li_rdtsc,dwResRdtsc;
        bool	fCompress = false;
        FILE* streamIn = NULL;
        FILE* streamOut = NULL;
    
        enum
        {
              paramExe,
              paramOperation,
              paramFileIn,
              paramFileOut,
              paramBlockSizeUncompress,
              paramCompressLevel
        };
    
        if (argc<paramFileOut)
        {
            printf("run TestZlib compress <FileIn> <FileOut> [BlockSizeUncompress] [compres. level]\n"
    			"    TestZlib uncompress <FileIn> <FileOut>\n"
    			"For example: TestZlib uncompress message.rpmsg uncompressed.data");
            return 0;
        }
    
    	if (argv[paramOperation][0] == 'u')
    		fCompress = false;
    	else if (argv[paramOperation][0] == 'c')
    		fCompress = true;
    	else
    	{
    		return -1;
    	}
    
        if (argc>paramBlockSizeUncompress)
            BlockSizeUncompress=atol(argv[paramBlockSizeUncompress]);
    
        if (argc>paramCompressLevel)
            cprLevel=(int)atol(argv[paramCompressLevel]);
    
    	if (fCompress)
    	{
    
                            // Do compression logic here.
    
    	}
    	else
    	{
    		static const char c_szCompressedDrmMessageHeader[] = "\x76\xE8\x04\x60\xC4\x11\xE3\x86";
    		CHAR szHeader[sizeof(c_szCompressedDrmMessageHeader)] = {0};
    		byte          rgbOriginal  [ZLIB_BUFFER_SIZE];
    		byte          rgbCompressed[ZLIB_BUFFER_SIZE*2];
    		streamIn=fopen(argv[paramFileIn], "rb");
    
    		if (streamIn==NULL)
    			return 0;
    
    		if (fread(szHeader, 1, sizeof(szHeader)-1, streamIn) == 0)
    			return 0;
    
    		if (strcmp(szHeader, c_szCompressedDrmMessageHeader) != 0)
    		{
    			printf("Not a DRM message header");
    			return -1;
    		}
    
    		streamOut=fopen(argv[paramFileOut], "wb");
    		if (streamOut==NULL)
    			return 0;
    
    		{
    			z_stream zcpr;
    			ZLIBDRMHEADER theHeader = {0};
    			int ret=Z_OK;
    			long lOrigDone = 0;
    			int step=0;
    			memset(&zcpr,0,sizeof(z_stream));
    			inflateInit(&zcpr);
    
    
    			while (true)
    			{
    				ZeroMemory(rgbOriginal, sizeof(rgbOriginal));
    				if (fread(&theHeader, sizeof(theHeader), 1, streamIn) == 0)
    				{
    					printf("read failed");
    					break;
    				}
    
    				if (theHeader.ulCheck != ZLIB_DRM_HEADER_MAGIC)
    				{
    					printf("Header Check failed!");
    					return -3;
    				}
    
    				if (fread(rgbCompressed, theHeader.cbCompressed, 1, streamIn) == 0)
    				{
    					printf("read failed");
    					return -4;
    				}
    
    				zcpr.next_in = rgbCompressed;
    				zcpr.next_out = rgbOriginal;
    				zcpr.avail_in = theHeader.cbCompressed;
    				zcpr.avail_out = sizeof(rgbOriginal);
    
    				do
    				{
    					ret=inflate(&zcpr,Z_SYNC_FLUSH);
    					step++;
    				} while (ret==Z_OK);
    
    				fwrite(rgbOriginal, 1, theHeader.cbUncompressed, streamOut);
    			}
    
    			lSizeUncpr=zcpr.total_out;
    			inflateEnd(&zcpr);
    		}
    	}
    
    	fclose(streamIn);
    	fclose(streamOut);
    
        return 0;
    }

    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 1:09 PM
    Moderator
  • Viswa,

    You also may want to check out my reply to Brad's post.  I reposted the code in a code block and it should be more complete now.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 1:10 PM
    Moderator
  • Viswa,

    I just noticed another problem in MS-OXORMMS.  Although it looks like your code is reading these values in the correct order, I just wanted to call your attention to it:

    <4> Section 3.1.4.1.2: The specific format of the message.rpmsg header is described as
    follows:
     
    CHAR STRING Header - Value is "\x76\xE8\x04\x60\xC4\x11\xE3\x86" in little-endian format.
    DWORD ULCheck - Value is 0x00000FA0
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.
    DWORD SizeAfterInflation - Size of byte steam after compressing.

    The size DWORD's should be swapped like this:

    DWORD SizeAfterInflation - Size of byte steam after compressing.
    DWORD SizeBeforeInflation - Size of byte stream after decompressing.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Friday, September 25, 2009 8:20 PM
    Moderator
  • Tom.

    Thanks - much appreciated.

    BRad
    Friday, September 25, 2009 10:10 PM
  • Tom,
    Finally the decompression works...Thanks for that.
    I verified this against a rpmsg.message and after some tweaking used the same code piece for uncompressing the zlib compressed License too.

    I however have one more doubt when it decompresses the zlib compressed license.(DRMLicense property in the message) I see the output has the following characters followed by the Wide char equivalent of the license:
    6A 44 00 (jD.)
    Is there some significance behind these values? Further for decryption of the RMS message should we pass the whole DRMLicense buffer that gets decompressed or should the above characters be removed? Am not clear on this.
    Viswa.
    Tuesday, September 29, 2009 7:28 AM
  • Viswa,

    Thanks for the followup question.  I've spent a little time modifying my sample code in order to inflate the DRMLicense property in stream __substg1.0_80011102-00000000 and I do see the bytes at the beginning of the uncompressed data.   I believe these represent the total length of the following uncompressed license data.  In my case, for example, the DRMLicense data began with:

    0000h: 3E 44 00 00 3C 00 58 00 72 00 4D 00 4C 00 20 00  >D..<.X.r.M.L. .

    The end of the data is:

    8870h: 3E 00 3C 00 2F 00 58 00 72 00 4D 00 4C 00 3E 00  >.<./.X.r.M.L.>.

    The final byte is at offset 8880h.  Subtract these "length" bytes and you get (8880h - 3) = 887Dh, which is in single bytes.  Divide that by 2 for wide characters and you get 443Eh. 

    I will make sure the specification has mention of this and if not, update it accordingly.  Again, thanks for bringing it up.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Wednesday, September 30, 2009 12:23 AM
    Moderator
  • Viswa,

    I received confirmation that the bytes in question are indeed the length of the XrML data (or Use License data), uncompressed that follows.

    We will be updating MS-OXORMMS to reflect that fact with explanation of these preceding bytes.

    Thanks for your patience on this issue.


    Regards, Tom Jebo Senior Support Escalation Engineer Microsoft DS Protocol Team
    Wednesday, October 14, 2009 4:13 AM
    Moderator
  • Tom,
    I just revisited this thread today and was interested in the last but one reply you had given - stream __substg1.0_80011102-00000000.

    I am curious to find out how you were able to map the DRMLicense property to that stream name? I am trying in vain to figure this out through the MS-OXOMSG document.
    Thanks,
    Viswa.
    Monday, February 1, 2010 9:26 AM
  • Hi Viswa,

    I actually wrote a blog that you might find helpful.   I explain how the stream names are formed in that blog.  DRM License property is described in MS-OXPROPS:

    2.544   PidNameRightsManagementLicense
    Canonical name: PidNameRightsManagementLicense
    Property set: PS_PUBLIC_STRINGS {00020329-0000-0000-C000-000000000046}
    Property name: DRMLicense
    Data type: PtypMultipleBinary, 0x1102
    Area: Secure Messaging Properties
    References: [MS-OXORMMS]

    Hence, the 0x1102. 

    MS-OXMSG Section 2.2.3 Named Property Mapping Storage, descibes how I mapped the DRMLicense property id PidNameRightsManagementLicense to 0x8001 property id, which is index 1 in the entry stream. 

    I hope this helps.

    Tom Jebo
    Microsoft Open Specifications Documentation Support
    • Proposed as answer by Chris Mullaney Wednesday, February 10, 2010 10:24 PM
    • Marked as answer by Chris Mullaney Friday, February 19, 2010 5:49 PM
    Wednesday, February 3, 2010 4:39 PM