none
MS-OFFCRYPTO worked examples? RRS feed

  • Question

  • I'm trying to work through the recently release documentation (MS-OFFCRYPTO) covering the encryption of Office document types which covers the binary and ECMA-376 formats.

    The documentation of the information contained the various streams is eacy to follow.  However I've not been able to generate values which match those created by office using the description of the algorithms to be used to generate keys.  Perhaps it because I'm an idiot but they do seem pretty vague so maybe there's scope for mis-interpretation. 

    Here's an example from section 2.3.4.7 on page 38:

    The initial password hash is generated as:
    H  = H(salt, password)

    Because the section refers to the generation of a hash involved in the encryption of Office 2007 content the hash algorithm we're talking about here is (MUST be) SHA1 so the parameter to the hash function is presumably a byte array.  But the line:

    H  = H(salt, password)

    is not very precise.  Should the byte array be a concatenation of the two parts?

    In the same section there's the line:

    Hn  = H(iterator, Hn-1 )

    Same hash function.  "iterator" is defined to be an unsigned integer but, again, exactly how is the array that is to be passed to the hash function to be formed?

    Bear in mind that just flipping a bit here will generate wildly different results, so mis-interpreting this operation is going to lead to the wrong answer.

    Are there any worked examples that show exactly how these hints should be used encrypt/decrypt office documents?

    Thanks

    Barry Smith


     

    Thursday, August 7, 2008 6:49 PM

Answers

All replies

  • Greetings and thanks for your post.

    The initial password hash is a salt byte array prepended to the byte array of the password. I will see if I can find a sample and post if back here.

    Steve Smegner
    Application Development Consulting Group

    Thursday, August 7, 2008 11:52 PM
  • If there is no code example, could you please at least tell us what is wrong with this interpretation (as it does not seem to give correct key, SHA1 and AES-128 used):

    Lets have salt all zeros, and simple password 'test'.

    So in hex-a:
    salt: 00000000000000000000000000000000
    password: 7400650073007400

    H0 = SHA1(000000000000000000000000000000007400650073007400) = a0dec49c1f5125f1681d73f932f3b929a312e067

    then 50000 iteration (showing first 2 and last 2)

    H1 (iterator = 0) = SHA1(00000000A0DEC49C1F5125F1681D73F932F3B929A312E067) = 6ee77ef4fb1cc4047976c74c427ecb04abdbf171

    H2 (iterator = 1) = SHA1(010000006EE77EF4FB1CC4047976C74C427ECB04ABDBF171) = f293446ae62c106b1f8ba2993241b43894bdee88
    ....
    H49999 (iterator = 49998) = SHA1(4EC30000AF701AA7B9614AD21EFDE3294C1567FE82521E6C) = a4a4b05ad4f6a9bcac4391d7b543fab7048df9cc
    H50000 (iterator = 49999) = SHA1(4FC30000A4A4B05AD4F6A9BCAC4391D7B543FAB7048DF9CC) =
    cbd12c8d02c5f6461e8d6eafc04035002955126d

    Hfinal (block = 0) = SHA1(CBD12C8D02C5F6461E8D6EAFC04035002955126D00000000) = 480b05354060e8965616227d0f576368b84634f0

    That would give us KEY for AES-128 = 480B05354060E8965616227D0F576368

    But this does not work (ie Verifier validation using this key fails). Please note that using different byte order for iterator (ie 00000000, 00000001, ... , 0000C34F) does not work either.

    So what is wrong with this interpretation?

    Thanks.
    • Edited by _00_ Thursday, August 14, 2008 1:14 PM adding simple text formatting
    Thursday, August 14, 2008 12:18 PM
  • the get key step in you steps is wrong:

    Hfinal = 480b05354060e8965616227d0f576368
    b84634f0

    to derive the key should using step 4, not step 3.
    as following:
    XORBuff = 363636363636363636....(repeat "0x36" 64 times)

    Hfinal' = Hfinal ^ XORBuff

    temp = SHA1(Hfinal')

    key = first 16 bytes of temp


    following is my code(need .net framework 3.5):
        private static void TryECMA376()
        {
          byte[] salt = new byte[] { 0x30, 0x3A, 0x39, 0x9E, 0x02, 0xB6, 0x66, 0x71, 0xA9, 0xEA, 0xDB, 0x17, 0x0C, 0xA6, 0xDF, 0x24 };
          byte[] pwd = Encoding.Unicode.GetBytes("123");
          byte[] tmp = H(salt, pwd);
          for (int i = 0; i < 50000; i++)
          {
            byte[] tmpI = BitConverter.GetBytes(i);
            tmp = H(tmpI, tmp);
          }
          tmp = H(tmp, BitConverter.GetBytes(0));

          SHA1Managed sha = new SHA1Managed();
          byte[] XorBuff = new byte[64];
          for (int i = 0; i < XorBuff.Length; i++)
          {
            XorBuff[i] = 0x36;
          }

          for (int i = 0; i < tmp.Length; i++)
          {
            XorBuff[i] ^= tmp[i];
          }

          tmp = sha.ComputeHash(XorBuff);
          
          byte[] key = new byte[16];
          Array.Copy(tmp, key, 16);

          byte[] pwdVerifier = new byte[] { 0x0A, 0x3A, 0xD5, 0x44, 0x17, 0xAB, 0x9B, 0x26, 0xA7, 0xFC, 0x65, 0xFE, 0x2F, 0x77, 0xDD, 0xA0 };
          byte[] pwdVerifierHash = new byte[] { 0x29, 0x0E, 0x48, 0xC6, 0x26, 0xA8, 0x72, 0x3B, 0xA2, 0xCF, 0x3F, 0x6A, 0x9B, 0x3A, 0x94, 0xB8, 0x59, 0x7A, 0x8E, 0xCE, 0xB5, 0x55, 0xE9, 0x15, 0x06, 0x89, 0xCD, 0x9C, 0xDA, 0x6C, 0x39, 0x31 };

          AesManaged aes = new AesManaged();
          aes.Mode = CipherMode.ECB;
          aes.KeySize = 128;
          aes.Key = key;
          aes.Padding = PaddingMode.None;
          ICryptoTransform ict = aes.CreateDecryptor();
          ICryptoTransform ict2 = aes.CreateDecryptor();
          byte[] pwdVerifierDec;
          byte[] pwdVerifierHashDec;
          pwdVerifierDec = ict.TransformFinalBlock(pwdVerifier, 0, pwdVerifier.Length);
          pwdVerifierHashDec = ict.TransformFinalBlock(pwdVerifierHash, 0, pwdVerifierHash.Length);
          //pwdVerifierDec = new byte[16];
          //pwdVerifierHashDec = new byte[32];
          //ict.TransformBlock(pwdVerifier, 0, pwdVerifier.Length, pwdVerifierDec, 0);
          //ict2.TransformBlock(pwdVerifierHash, 0, 32, pwdVerifierHashDec, 0);
          //ict2.TransformBlock(pwdVerifierHash, 16, 16, pwdVerifierHashDec, 16);

          pwdVerifierDec = sha.ComputeHash(pwdVerifierDec);

          for (int i = 0; i < pwdVerifierDec.Length; i++)
          {
            if (pwdVerifierDec[i] != pwdVerifierHashDec[i])
            {
              Console.WriteLine("Oh, NO!!!!!!!!!!!!!!!");
              return;
            }
          }
          Console.WriteLine("Yeah~~~~~~~~~~");

          FileStream fs = File.OpenRead("D:\\EncryptedPackage");
          byte[] encPak = new byte[fs.Length];
          fs.Read(encPak, 0, encPak.Length);
          byte[] decPak = new byte[fs.Length];
          fs.Close();
          decPak = ict2.TransformFinalBlock(encPak, 8, encPak.Length - 8);
          FileStream fso = File.Create("D:\\yeah.zip");
          fso.Write(decPak, 0, decPak.Length);
          fso.Close();
        }

        private static byte[] H(byte[] b1,byte[] b2)
        {
          SHA1Managed sha = new SHA1Managed();
          byte[] t = new byte[b1.Length + b2.Length];
          b1.CopyTo(t, 0);
          b2.CopyTo(t, b1.Length);
          return sha.ComputeHash(t); 
        }



    http://gcdn.GrapeCity.com/cs/
    • Edited by J2.NETe Tuesday, September 9, 2008 6:42 AM add codes
    Tuesday, September 9, 2008 5:35 AM
  • Weird ... in documentation it says, that Step 4 is required IF* cbRequiredKeyLength is greater than cbHash (ie for AES 192 and AES 256)

    Hope you are correct and documentation is just misleading...

    Anyway - THANKS!

    * I can confirm that Step 4 is required even for AES 128
    • Edited by _00_ Tuesday, September 9, 2008 11:59 AM confirmation added
    Tuesday, September 9, 2008 11:01 AM
  • Yes, I think document is misleading...
    http://gcdn.GrapeCity.com/cs/
    Wednesday, September 10, 2008 4:42 AM
  • Greetings,
    An updated version of MS-OFFCRYPTO has been posted here: http://msdn.microsoft.com/en-us/library/cc313071.aspx. This is version 1.01. This document contains the corrected password hash algorithm.

     

    Steve Smegner

    Application Development Consulting Group

    • Proposed as answer by Steve Smegner Thursday, October 16, 2008 5:53 AM
    • Marked as answer by Chris Mullaney Tuesday, October 21, 2008 11:00 PM
    Thursday, October 16, 2008 5:53 AM
  • Hi Steve,

    What is the simplest way to check if the ppt and pptx files has password protection?

    Thanks, Steve.

    -- Tommy.
    Monday, April 6, 2009 4:55 PM
  • Hi Tommy,

     

    There are some relevant MS-OFFCRYPTO code samples in C# available on CodePlex which we believe will answer your question: http://www.codeplex.com/offcrypto.  You may also find the following blog a useful resource on this subject: http://blogs.msdn.com/david_leblanc/default.aspx.

     

    Best regards,

     

    Edgar

    Tuesday, April 7, 2009 12:29 AM
    Moderator
  • Hi Edgar,

    Thank you so much for your response. 

    -- Tommy.
    Tuesday, April 7, 2009 3:41 AM
  • Hi.............

    i m working on ms word 2010 password.

    Is steps of decryption in word 2007 opening password and 2010 are same..........

    Thursday, December 22, 2011 8:31 AM
  • Hi prateek1108, thank you for your question. A member of the protocol documentation team will respond to you soon.
    Josh Curry (jcurry) | Escalation Engineer | Open Specifications Support Team
    Thursday, December 22, 2011 4:18 PM
    Moderator
  • Hi JCurry...thanks for responding on my question...

    Hope i will get response very soon

    Friday, December 23, 2011 3:25 AM
  • Hi Prateek, future communications regarding this issue will take place in the other thread since it is not a continuation of a much older thread.

    Thank you.

    http://social.msdn.microsoft.com/Forums/en-US/os_openXML-ecma/thread/bcac35b3-a305-4e43-8d0b-fad69e2f9fb6

     

     


    Josh Curry (jcurry) | Escalation Engineer | Open Specifications Support Team
    Friday, December 23, 2011 4:38 PM
    Moderator