private static bool IsUserPassword(byte[] passwordBytes, EncryptionDictionary encryptionDictionary, int length, byte[] documentIdBytes) { if (encryptionDictionary.Revision == 5 || encryptionDictionary.Revision == 6) { return(IsUserPasswordRevision5And6(passwordBytes, encryptionDictionary)); } // 1. Create an encryption key based on the user password string. var calculatedEncryptionKey = CalculateKeyRevisions2To4(passwordBytes, encryptionDictionary, length, documentIdBytes); byte[] output; if (encryptionDictionary.Revision >= 3) { using (var md5 = MD5.Create()) { // 2. Initialize the MD5 hash function and pass the 32-byte padding string. UpdateMd5(md5, PaddingBytes); // 3. Pass the first element of the file identifier array to the hash function and finish the hash. UpdateMd5(md5, documentIdBytes); md5.TransformFinalBlock(EmptyArray <byte> .Instance, 0, 0); var result = md5.Hash; // 4. Encrypt the 16-byte result of the hash, using an RC4 encryption function with the encryption key from step 1. var temp = RC4.Encrypt(calculatedEncryptionKey, result); // 5. Do the following 19 times: for (byte i = 1; i <= 19; i++) { // Take the output from the previous invocation of the RC4 function // and pass it as input to a new invocation of the function // Use an encryption key generated by taking each byte of the original encryption key (from step 1) // and performing an XOR operation between that byte and the single-byte value of the iteration counter. var key = calculatedEncryptionKey.Select(x => (byte)(x ^ i)).ToArray(); temp = RC4.Encrypt(key, temp); } output = temp; } } else { // 2. Encrypt the 32-byte padding string using an RC4 encryption function with the encryption key from the preceding step. output = RC4.Encrypt(calculatedEncryptionKey, PaddingBytes); } if (encryptionDictionary.Revision >= 3) { return(encryptionDictionary.UserBytes.Take(16).SequenceEqual(output.Take(16))); } return(encryptionDictionary.UserBytes.SequenceEqual(output)); }
private byte[] DecryptData(byte[] data, IndirectReference reference) { if (useAes && encryptionKey.Length == 32) { return(AesEncryptionHelper.Decrypt(data, encryptionKey)); } var finalKey = GetObjectKey(reference); if (useAes) { return(AesEncryptionHelper.Decrypt(data, finalKey)); } return(RC4.Encrypt(finalKey, data)); }
private static bool IsOwnerPassword(byte[] passwordBytes, EncryptionDictionary encryptionDictionary, int length, byte[] documentIdBytes, out byte[] userPassword) { userPassword = null; if (encryptionDictionary.Revision == 5 || encryptionDictionary.Revision == 6) { return(IsOwnerPasswordRevision5And6(passwordBytes, encryptionDictionary)); } // 1. Pad or truncate the owner password string, if there is no owner password use the user password instead. var paddedPassword = GetPaddedPassword(passwordBytes); using (var md5 = MD5.Create()) { // 2. Initialize the MD5 hash function and pass the result of step 1 as input. var hash = md5.ComputeHash(paddedPassword); // 3. (Revision 3 or greater) Do the following 50 times: if (encryptionDictionary.Revision >= 3) { // Take the output from the previous MD5 hash and pass it as input into a new MD5 hash. for (var i = 0; i < 50; i++) { hash = md5.ComputeHash(md5.Hash); } } // 4. Create an RC4 encryption key using the first n bytes of the output from the final MD5 hash, // where n is always 5 for revision 2 but for revision 3 or greater depends on the value of the encryption dictionary's Length entry. var key = hash.Take(length).ToArray(); if (encryptionDictionary.Revision == 2) { // 5. (Revision 2 only) Decrypt the value of the encryption dictionary's owner entry, // using an RC4 encryption function with the encryption key computed in step 1 - 4. userPassword = RC4.Encrypt(key, encryptionDictionary.OwnerBytes); } else { // 5. (Revision 3 or greater) Do the following 20 times: byte[] output = null; for (var i = 0; i < 20; i++) { // Generate a per iteration key by taking the original key and performing an XOR operation between each // byte of the key and the single-byte value of the iteration counter (from 19 to 0). var keyIter = key.Select(x => (byte)(x ^ (19 - i))).ToArray(); if (i == 0) { output = encryptionDictionary.OwnerBytes; } // Decrypt the value of the encryption dictionary's owner entry (first iteration) // or the output from the previous iteration using an RC4 encryption function. output = RC4.Encrypt(keyIter, output); } userPassword = output; } // 6. The result of step 5 purports to be the user password. // Authenticate this user password, if it is correct, the password supplied is the owner password. var result = IsUserPassword(userPassword, encryptionDictionary, length, documentIdBytes); return(result); } }