Using LibreOffice or OpenOffice, for a Microsoft '.doc' or .'xls' file, from Java, C++, C#, Python, BeanShell, JavaScript, or Basic
Topics
About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: the Java programming language
About: C++
About: Microsoft .NET Framework
About: the Python programming language
About: LibreOffice Basic
About: Apache OpenOffice Basic
About: BeanShell
About: JavaScript
The table of contents of this article
- Starting Context
- Target Context
- Orientation
- Main Body
- 1: Creating a Hash of the Editing Password
- 2: Setting a Document-Storing Property
- 3: Executing Some Sample Programs
- 4: The Conclusion and Beyond
Starting Context
- The reader has a basic knowledge on one of the UNO-supported programming languages (Java, C++, the Microsoft .NET Framework programming languages (C# and Visual Basic.NET, in most cases), Python, LibreOffice or Apache OpenOffice Basic, BeanShell, and JavaScript).
- The reader has a knowledge on what UNO is and how it is related to LibreOffice or Apache OpenOffice.
- The reader has a knowledge on basic elements of UNO and the terminology used for them in this series.
- The reader has a knowledge on how to get a UNO objects context to any LibreOffice or Apache OpenOffice instance (there are some articles on how to do it for any external console Java, C++, C#, and Python programs; there is an article on how to do it for any external GUI Java program; some articles on how to do it for any external GUI C++, C#, and Python programs will come later).
- The reader has a knowledge on how to access the intended document and how to store the document into a file, using UNO (the article on how to use LibreOffice or Apache OpenOffice as a files converter (the concept, a Java implementation, a C++ implementation, a C# implementation, a Python implementation, and a LibreOffice or Apache OpenOffice Basic implementation) contains a major part of the information).
Target Context
- The reader will know how to set any editing password into a Microsoft binary Word or Excel ('.doc' or '.xls') file from his or her program.
Here are Hypothesizer 7, Objector 37A, and Objector 37B in front of a computer.
Orientation
Hypothesizer 7
In the 1st part of this article, we have discussed how to set any editing password into an OpenDocument format file from our program.
In this part of this article, we will know how to set any editing password into a Microsoft Office binary Word or Excel format ('.doc' or '.xls') file from our program.
Objector 37A
Actually, the LibreOffice GUI does not properly set the editing password into any '.doc' file. I mean, Microsoft Word program refuses to let me edit the file with the password I have specified.
Hypothesizer 7
Sir, that is a bug of LibreOffice.
Objector 37A
A bug?
Hypothesizer 7
Yes. The logic for creating the hash of the password is flawed.
Objector 37A
Aren't many people complaining about the bug? That bug seems to have been left unrectified for a long time . . .
Hypothesizer 7
Probably, not many want the feature anyway.
Objector 37A
Why?
Hypothesizer 7
Probably because the editing password is not much efective in preventing the file from being maliciously modified, as we have discussed in 'Orientation' of the 1st part of this article, although I think that there is certain usefullness as a measure for preventing the file from being unintentionally modified. Or maybe just the older format is not being used much any more.
Objector 37B
In fact, I want to use the feature in order to prevent myself from mistakenly editing some files that are supposed to be fixed, in my private environment where nobody else is ever imagined to touch the files.
Hypothesizer 7
Madam, I think that that is a valid usage.
Objector 37A
Anyway, can't I set the password properly either by the way you will introduce in this article?
Hypothesizer 7
In fact, you can, because your program will correctly create the hash.
Objector 37A
Oh, so, my program will create the hash . . .
Hypothesizer 7
Yes. The hashing logics are the main theme of this article.
Main Body
1: Creating a Hash of the Editing Password
Hypothesizer 7
The tedious part of setting the editing password into an Office document file is to create the hash of the editing password.
In fact, the hashing algorithm is different for each Microsoft binary format.
This is my Java function that creates the hash for any '.doc' file.
@Java Source Code
package theBiasPlanet.unoUtilities.cryptography;
public class MicrosoftPasswordsHasher {
private static final int c_numberOfInitializationCodes = 15;
private static final short [] c_initializationCodes = new short [] {(short) 0xE1F0, (short) 0x1D0F, (short) 0xCC9C, (short) 0x84C0, (short) 0x110C, (short) 0x0E10, (short) 0xF1CE, (short) 0x313E, (short) 0x1872, (short) 0xE139, (short) 0xD40F, (short) 0x84F9, (short) 0x280C, (short) 0xA96A, (short) 0x4EC3};
private static final int c_numberOfEncryptionMatrixColumns = 7;
private static final short [] [] c_encryptionMatrix = new short [] [] {
{ (short) 0xAEFC, (short) 0x4DD9, (short) 0x9BB2, (short) 0x2745, (short) 0x4E8A, (short) 0x9D14, (short) 0x2A09},
{ (short) 0x7B61, (short) 0xF6C2, (short) 0xFDA5, (short) 0xEB6B, (short) 0xC6F7, (short) 0x9DCF, (short) 0x2BBF},
{ (short) 0x4563, (short) 0x8AC6, (short) 0x05AD, (short) 0x0B5A, (short) 0x16B4, (short) 0x2D68, (short) 0x5AD0},
{ (short) 0x0375, (short) 0x06EA, (short) 0x0DD4, (short) 0x1BA8, (short) 0x3750, (short) 0x6EA0, (short) 0xDD40},
{ (short) 0xD849, (short) 0xA0B3, (short) 0x5147, (short) 0xA28E, (short) 0x553D, (short) 0xAA7A, (short) 0x44D5},
{ (short) 0x6F45, (short) 0xDE8A, (short) 0xAD35, (short) 0x4A4B, (short) 0x9496, (short) 0x390D, (short) 0x721A},
{ (short) 0xEB23, (short) 0xC667, (short) 0x9CEF, (short) 0x29FF, (short) 0x53FE, (short) 0xA7FC, (short) 0x5FD9},
{ (short) 0x47D3, (short) 0x8FA6, (short) 0x8FA6, (short) 0x1EDA, (short) 0x3DB4, (short) 0x7B68, (short) 0xF6D0},
{ (short) 0xB861, (short) 0x60E3, (short) 0xC1C6, (short) 0x93AD, (short) 0x377B, (short) 0x6EF6, (short) 0xDDEC},
{ (short) 0x45A0, (short) 0x8B40, (short) 0x06A1, (short) 0x0D42, (short) 0x1A84, (short) 0x3508, (short) 0x6A10},
{ (short) 0xAA51, (short) 0x4483, (short) 0x8906, (short) 0x022D, (short) 0x045A, (short) 0x08B4, (short) 0x1168},
{ (short) 0x76B4, (short) 0xED68, (short) 0xCAF1, (short) 0x85C3, (short) 0x1BA7, (short) 0x374E, (short) 0x6E9C},
{ (short) 0x3730, (short) 0x6E60, (short) 0xDCC0, (short) 0xA9A1, (short) 0x4363, (short) 0x86C6, (short) 0x1DAD},
{ (short) 0x3331, (short) 0x6662, (short) 0xCCC4, (short) 0x89A9, (short) 0x0373, (short) 0x06E6, (short) 0x0DCC},
{ (short) 0x1021, (short) 0x2042, (short) 0x4084, (short) 0x8108, (short) 0x1231, (short) 0x2462, (short) 0x48C4}
};
public static int hashIn32bits (String a_originalDatum) {
int l_hash = 0;
int l_originalDatumLength = a_originalDatum.length ();
if (l_originalDatumLength > 0) {
if (l_originalDatumLength > c_numberOfInitializationCodes) {
l_originalDatumLength = c_numberOfInitializationCodes;
}
int l_highHash = c_initializationCodes [l_originalDatumLength - 1];
int l_lowHash = 0;
char l_character = 0x0000;
byte l_byte = 0x00;
byte l_highByte = 0x00;
byte l_lowByte = 0x00;
for (int l_characterIndex = 0; l_characterIndex < l_originalDatumLength; l_characterIndex ++) {
l_character = a_originalDatum.charAt (l_characterIndex);
l_highByte = (byte) (l_character >>> 8);
l_lowByte = (byte) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
for (int l_matrixcolumnIndex = 0; l_matrixcolumnIndex < c_numberOfEncryptionMatrixColumns; l_matrixcolumnIndex ++) {
if ((l_byte & (1 << l_matrixcolumnIndex)) != 0) {
l_highHash = (short) (l_highHash ^ c_encryptionMatrix [c_numberOfInitializationCodes - l_originalDatumLength + l_characterIndex] [l_matrixcolumnIndex]);
}
}
l_character = a_originalDatum.charAt (l_originalDatumLength -1 - l_characterIndex);
l_highByte = (byte) (l_character >>> 8);
l_lowByte = (byte) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
l_lowHash = ( ( (l_lowHash >>> 14) & 0x0001 ) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_byte;
}
l_lowHash = ( ( (l_lowHash >>> 14) & 0x0001) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_originalDatumLength ^ 0xCE4B;
l_hash = (l_highHash << 16) | l_lowHash;
}
return l_hash;
}
~
}
Objector 37B
. . . Is the hash an integer?
Hypothesizer 7
It is a 32-bits array that will be ultimately turned into a UNO 'long' datum.
Objector 37B
Hmm.
Hypothesizer 7
Note that 'l_highHash' and 'l_lowhHash' are really 16-bits arrays, but as Java bits left shift operation automatically (gratuitously, in my opinion) changes any 'short' datum into an 'int' datum, I use 'int' variables, not 'short' variables, for them.
Objector 37B
Huh?
Hypothesizer 7
For example, we want
'0b1111111111111111' ->
'0b1111111111111110' ->
'0b0111111111111111'
by '(0b1111111111111111 << 1) >>> 1', but Java does
'0b1111111111111111' ->
'0b11111111111111111111111111111111' ->
'0b11111111111111111111111111111110' ->
'0b01111111111111111111111111111111', which is a problem.
Objector 37B
. . . You mean, it's OK if the last 16 bits are correct, but they are not?
Hypothesizer 7
Exactly. So, we have it in 'int' from the beginning, making it
'0b00000000000000001111111111111111' ->
'0b00000000000000011111111111111110' ->
'0b00000000000000001111111111111111'.
Objector 37B
Ah, the last 16 bits are as we hope them to be.
Hypothesizer 7
These are my C++, C#, and Python functions that create the hash for any '.doc' file.
@C++ Source Code
#ifndef __theBiasPlanet_unoUtilities_cryptography_MicrosoftPasswordsHasher_hpp__
#define __theBiasPlanet_unoUtilities_cryptography_MicrosoftPasswordsHasher_hpp__
#include <string>
using namespace ::std;
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
class __theBiasPlanet_unoUtilities_symbolExportingOrImportingForVisualCplusplus__ MicrosoftPasswordsHasher {
private:
static int const c_numberOfInitializationCodes;
static unsigned short const c_initializationCodes [];
static int const c_numberOfEncryptionMatrixColumns = 7;
static unsigned short const c_encryptionMatrix [] [c_numberOfEncryptionMatrixColumns];
public:
static long hashIn32bits (string const & a_originalDatum);
~
};
}
}
}
#endif
#include "theBiasPlanet/unoUtilities/cryptography/MicrosoftPasswordsHasher.hpp"
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
int const MicrosoftPasswordsHasher::c_numberOfInitializationCodes = 15;
unsigned short const MicrosoftPasswordsHasher::c_initializationCodes [] = {(unsigned short) 0xE1F0, (unsigned short) 0x1D0F, (unsigned short) 0xCC9C, (unsigned short) 0x84C0, (unsigned short) 0x110C, (unsigned short) 0x0E10, (unsigned short) 0xF1CE, (unsigned short) 0x313E, (unsigned short) 0x1872, (unsigned short) 0xE139, (unsigned short) 0xD40F, (unsigned short) 0x84F9, (unsigned short) 0x280C, (unsigned short) 0xA96A, (unsigned short) 0x4EC3};
int const MicrosoftPasswordsHasher::c_numberOfEncryptionMatrixColumns;
unsigned short const MicrosoftPasswordsHasher::c_encryptionMatrix [] [MicrosoftPasswordsHasher::c_numberOfEncryptionMatrixColumns] = {
{ (unsigned short) 0xAEFC, (unsigned short) 0x4DD9, (unsigned short) 0x9BB2, (unsigned short) 0x2745, (unsigned short) 0x4E8A, (unsigned short) 0x9D14, (unsigned short) 0x2A09},
{ (unsigned short) 0x7B61, (unsigned short) 0xF6C2, (unsigned short) 0xFDA5, (unsigned short) 0xEB6B, (unsigned short) 0xC6F7, (unsigned short) 0x9DCF, (unsigned short) 0x2BBF},
{ (unsigned short) 0x4563, (unsigned short) 0x8AC6, (unsigned short) 0x05AD, (unsigned short) 0x0B5A, (unsigned short) 0x16B4, (unsigned short) 0x2D68, (unsigned short) 0x5AD0},
{ (unsigned short) 0x0375, (unsigned short) 0x06EA, (unsigned short) 0x0DD4, (unsigned short) 0x1BA8, (unsigned short) 0x3750, (unsigned short) 0x6EA0, (unsigned short) 0xDD40},
{ (unsigned short) 0xD849, (unsigned short) 0xA0B3, (unsigned short) 0x5147, (unsigned short) 0xA28E, (unsigned short) 0x553D, (unsigned short) 0xAA7A, (unsigned short) 0x44D5},
{ (unsigned short) 0x6F45, (unsigned short) 0xDE8A, (unsigned short) 0xAD35, (unsigned short) 0x4A4B, (unsigned short) 0x9496, (unsigned short) 0x390D, (unsigned short) 0x721A},
{ (unsigned short) 0xEB23, (unsigned short) 0xC667, (unsigned short) 0x9CEF, (unsigned short) 0x29FF, (unsigned short) 0x53FE, (unsigned short) 0xA7FC, (unsigned short) 0x5FD9},
{ (unsigned short) 0x47D3, (unsigned short) 0x8FA6, (unsigned short) 0x8FA6, (unsigned short) 0x1EDA, (unsigned short) 0x3DB4, (unsigned short) 0x7B68, (unsigned short) 0xF6D0},
{ (unsigned short) 0xB861, (unsigned short) 0x60E3, (unsigned short) 0xC1C6, (unsigned short) 0x93AD, (unsigned short) 0x377B, (unsigned short) 0x6EF6, (unsigned short) 0xDDEC},
{ (unsigned short) 0x45A0, (unsigned short) 0x8B40, (unsigned short) 0x06A1, (unsigned short) 0x0D42, (unsigned short) 0x1A84, (unsigned short) 0x3508, (unsigned short) 0x6A10},
{ (unsigned short) 0xAA51, (unsigned short) 0x4483, (unsigned short) 0x8906, (unsigned short) 0x022D, (unsigned short) 0x045A, (unsigned short) 0x08B4, (unsigned short) 0x1168},
{ (unsigned short) 0x76B4, (unsigned short) 0xED68, (unsigned short) 0xCAF1, (unsigned short) 0x85C3, (unsigned short) 0x1BA7, (unsigned short) 0x374E, (unsigned short) 0x6E9C},
{ (unsigned short) 0x3730, (unsigned short) 0x6E60, (unsigned short) 0xDCC0, (unsigned short) 0xA9A1, (unsigned short) 0x4363, (unsigned short) 0x86C6, (unsigned short) 0x1DAD},
{ (unsigned short) 0x3331, (unsigned short) 0x6662, (unsigned short) 0xCCC4, (unsigned short) 0x89A9, (unsigned short) 0x0373, (unsigned short) 0x06E6, (unsigned short) 0x0DCC},
{ (unsigned short) 0x1021, (unsigned short) 0x2042, (unsigned short) 0x4084, (unsigned short) 0x8108, (unsigned short) 0x1231, (unsigned short) 0x2462, (unsigned short) 0x48C4}
};
}
}
}
#include "theBiasPlanet/unoUtilities/cryptography/MicrosoftPasswordsHasher.hpp"
#include <limits>
#include "theBiasPlanet/coreUtilities/stringsHandling/StringHandler.hpp"
using namespace ::theBiasPlanet::coreUtilities::stringsHandling;
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
long MicrosoftPasswordsHasher::hashIn32bits (string const & a_originalDatum) {
unsigned long l_hash = 0;
u16string l_originalDatumInUtf16 = StringHandler::getUtf16String (a_originalDatum);
int l_originalDatumLength = l_originalDatumInUtf16.length ();
if (l_originalDatumLength > 0) {
if (l_originalDatumLength > c_numberOfInitializationCodes) {
l_originalDatumLength = c_numberOfInitializationCodes;
}
unsigned short l_highHash = c_initializationCodes [l_originalDatumLength - 1];
unsigned short l_lowHash = 0;
unsigned short l_character = 0x0000;
unsigned char l_byte = 0x00;
unsigned char l_highByte = 0x00;
unsigned char l_lowByte = 0x00;
for (int l_characterIndex = 0; l_characterIndex < l_originalDatumLength; l_characterIndex ++) {
l_character = l_originalDatumInUtf16 [l_characterIndex];
l_highByte = (unsigned char) (l_character >> 8);
l_lowByte = (unsigned char) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
for (int l_matrixcolumnIndex = 0; l_matrixcolumnIndex < c_numberOfEncryptionMatrixColumns; l_matrixcolumnIndex ++) {
if ((l_byte & (1 << l_matrixcolumnIndex)) != 0) {
l_highHash = (unsigned short) (l_highHash ^ c_encryptionMatrix [c_numberOfInitializationCodes - l_originalDatumLength + l_characterIndex] [l_matrixcolumnIndex]);
}
}
l_character = l_originalDatumInUtf16 [l_originalDatumLength -1 - l_characterIndex];
l_highByte = (unsigned char) (l_character >> 8);
l_lowByte = (unsigned char) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
l_lowHash = ( ( (l_lowHash >> 14) & 0x0001 ) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_byte;
}
l_lowHash = ( ( (l_lowHash >> 14) & 0x0001) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_originalDatumLength ^ 0xCE4B;
l_hash = (l_highHash << 16) | l_lowHash;
}
return (long) l_hash;
}
~
}
}
}
@C# Source Code
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
using System;
using System.Text;
public class MicrosoftPasswordsHasher {
private const int c_numberOfInitializationCodes = 15;
private static readonly ushort [] c_initializationCodes = new ushort [] {(ushort) 0xE1F0, (ushort) 0x1D0F, (ushort) 0xCC9C, (ushort) 0x84C0, (ushort) 0x110C, (ushort) 0x0E10, (ushort) 0xF1CE, (ushort) 0x313E, (ushort) 0x1872, (ushort) 0xE139, (ushort) 0xD40F, (ushort) 0x84F9, (ushort) 0x280C, (ushort) 0xA96A, (ushort) 0x4EC3};
private const int c_numberOfEncryptionMatrixColumns = 7;
private static readonly ushort [, ] c_encryptionMatrix = new ushort [, ] {
{ (ushort) 0xAEFC, (ushort) 0x4DD9, (ushort) 0x9BB2, (ushort) 0x2745, (ushort) 0x4E8A, (ushort) 0x9D14, (ushort) 0x2A09},
{ (ushort) 0x7B61, (ushort) 0xF6C2, (ushort) 0xFDA5, (ushort) 0xEB6B, (ushort) 0xC6F7, (ushort) 0x9DCF, (ushort) 0x2BBF},
{ (ushort) 0x4563, (ushort) 0x8AC6, (ushort) 0x05AD, (ushort) 0x0B5A, (ushort) 0x16B4, (ushort) 0x2D68, (ushort) 0x5AD0},
{ (ushort) 0x0375, (ushort) 0x06EA, (ushort) 0x0DD4, (ushort) 0x1BA8, (ushort) 0x3750, (ushort) 0x6EA0, (ushort) 0xDD40},
{ (ushort) 0xD849, (ushort) 0xA0B3, (ushort) 0x5147, (ushort) 0xA28E, (ushort) 0x553D, (ushort) 0xAA7A, (ushort) 0x44D5},
{ (ushort) 0x6F45, (ushort) 0xDE8A, (ushort) 0xAD35, (ushort) 0x4A4B, (ushort) 0x9496, (ushort) 0x390D, (ushort) 0x721A},
{ (ushort) 0xEB23, (ushort) 0xC667, (ushort) 0x9CEF, (ushort) 0x29FF, (ushort) 0x53FE, (ushort) 0xA7FC, (ushort) 0x5FD9},
{ (ushort) 0x47D3, (ushort) 0x8FA6, (ushort) 0x8FA6, (ushort) 0x1EDA, (ushort) 0x3DB4, (ushort) 0x7B68, (ushort) 0xF6D0},
{ (ushort) 0xB861, (ushort) 0x60E3, (ushort) 0xC1C6, (ushort) 0x93AD, (ushort) 0x377B, (ushort) 0x6EF6, (ushort) 0xDDEC},
{ (ushort) 0x45A0, (ushort) 0x8B40, (ushort) 0x06A1, (ushort) 0x0D42, (ushort) 0x1A84, (ushort) 0x3508, (ushort) 0x6A10},
{ (ushort) 0xAA51, (ushort) 0x4483, (ushort) 0x8906, (ushort) 0x022D, (ushort) 0x045A, (ushort) 0x08B4, (ushort) 0x1168},
{ (ushort) 0x76B4, (ushort) 0xED68, (ushort) 0xCAF1, (ushort) 0x85C3, (ushort) 0x1BA7, (ushort) 0x374E, (ushort) 0x6E9C},
{ (ushort) 0x3730, (ushort) 0x6E60, (ushort) 0xDCC0, (ushort) 0xA9A1, (ushort) 0x4363, (ushort) 0x86C6, (ushort) 0x1DAD},
{ (ushort) 0x3331, (ushort) 0x6662, (ushort) 0xCCC4, (ushort) 0x89A9, (ushort) 0x0373, (ushort) 0x06E6, (ushort) 0x0DCC},
{ (ushort) 0x1021, (ushort) 0x2042, (ushort) 0x4084, (ushort) 0x8108, (ushort) 0x1231, (ushort) 0x2462, (ushort) 0x48C4}
};
public static int hashIn32bits (String a_originalDatum) {
uint l_hash = 0;
int l_originalDatumLength = a_originalDatum.Length;
if (l_originalDatumLength > 0) {
if (l_originalDatumLength > c_numberOfInitializationCodes) {
l_originalDatumLength = c_numberOfInitializationCodes;
}
ushort l_highHash = c_initializationCodes [l_originalDatumLength - 1];
ushort l_lowHash = 0;
char l_character = (char) 0x0000;
byte l_byte = 0x00;
byte l_highByte = 0x00;
byte l_lowByte = 0x00;
for (int l_characterIndex = 0; l_characterIndex < l_originalDatumLength; l_characterIndex ++) {
l_character = a_originalDatum [l_characterIndex];
l_highByte = (byte) (l_character >> 8);
l_lowByte = (byte) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
for (int l_matrixcolumnIndex = 0; l_matrixcolumnIndex < c_numberOfEncryptionMatrixColumns; l_matrixcolumnIndex ++) {
if ((l_byte & (1 << l_matrixcolumnIndex)) != 0) {
l_highHash = (ushort) (l_highHash ^ c_encryptionMatrix [c_numberOfInitializationCodes - l_originalDatumLength + l_characterIndex, l_matrixcolumnIndex]);
}
}
l_character = a_originalDatum [l_originalDatumLength -1 - l_characterIndex];
l_highByte = (byte) (l_character >> 8);
l_lowByte = (byte) (l_character & 0xFF);
l_byte = l_lowByte != 0x00 ? l_lowByte : l_highByte;
l_lowHash = (ushort) (( ( (l_lowHash >> 14) & 0x0001 ) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_byte);
}
l_lowHash = (ushort) (( ( (l_lowHash >> 14) & 0x0001) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_originalDatumLength ^ 0xCE4B);
l_hash = (uint) ((l_highHash << 16) | l_lowHash);
}
return (int) l_hash;
}
~
}
}
}
}
@Python Source Code
from typing import List
class MicrosoftPasswordsHasher:
c_numberOfInitializationCodes: int = 15
c_initializationCodes: List [int] = [0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3]
c_numberOfEncryptionMatrixColumns: int = 7
c_encryptionMatrix: List [List [int]] = [
[ 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09],
[ 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF],
[ 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0],
[ 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40],
[ 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5],
[ 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A],
[ 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9],
[ 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0],
[ 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC],
[ 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10],
[ 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168],
[ 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C],
[ 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD],
[ 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC],
[ 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4]
]
@staticmethod
def hashIn32bits (a_originalDatum: str) -> int:
l_hash: int = 0
l_originalDatumLength: int = len (a_originalDatum)
if l_originalDatumLength > 0:
if l_originalDatumLength > MicrosoftPasswordsHasher.c_numberOfInitializationCodes:
l_originalDatumLength = MicrosoftPasswordsHasher.c_numberOfInitializationCodes
l_highHash: int = MicrosoftPasswordsHasher.c_initializationCodes [l_originalDatumLength - 1]
l_lowHash: int = 0
l_character: int = 0x0000
l_byte: int = 0x00
l_highByte: int = 0x00
l_lowByte: int = 0x00
l_characterIndex: int = 0
for l_characterIndex in range (0, l_originalDatumLength, 1):
l_character = ord (a_originalDatum [l_characterIndex])
l_highByte = (l_character >> 8)
l_lowByte = (l_character & 0xFF)
l_byte = l_lowByte if l_lowByte != 0x00 else l_highByte
l_matrixcolumnIndex: int = 0
for l_matrixcolumnIndex in range (0, MicrosoftPasswordsHasher.c_numberOfEncryptionMatrixColumns, 1):
if (l_byte & (1 << l_matrixcolumnIndex)) != 0:
l_highHash = (l_highHash ^ MicrosoftPasswordsHasher.c_encryptionMatrix [MicrosoftPasswordsHasher.c_numberOfInitializationCodes - l_originalDatumLength + l_characterIndex] [l_matrixcolumnIndex])
l_character = ord (a_originalDatum [l_originalDatumLength -1 - l_characterIndex])
l_highByte = (l_character >> 8)
l_lowByte = (l_character & 0xFF)
l_byte = l_lowByte if l_lowByte != 0x00 else l_highByte
l_lowHash = ( ( (l_lowHash >> 14) & 0x0001 ) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_byte
l_lowHash = ( ( (l_lowHash >> 14) & 0x0001) | ( (l_lowHash << 1) & 0x7FFF)) ^ l_originalDatumLength ^ 0xCE4B
l_hash = (l_highHash << 16) | l_lowHash
return l_hash
~
Objector 37B
Hmm.
Hypothesizer 7
On the other hand, this is my Java function that creates the hash for any '.xls' file.
@Java Source Code
package theBiasPlanet.unoUtilities.cryptography;
public class MicrosoftPasswordsHasher {
~
public static short hashIn16bits (String a_originalDatum) {
int l_hash = 0;
byte [] l_originalDatumUtf8BytesArray = null;
try {
l_originalDatumUtf8BytesArray = a_originalDatum.getBytes ("UTF-8");
}
catch (Exception l_exception) {
// Is supposed to never happen.
}
int l_originalDatumUtf8BytesArrayLength = l_originalDatumUtf8BytesArray.length;
if (l_originalDatumUtf8BytesArrayLength <= Short.MAX_VALUE) {
for (int l_byteIndex = l_originalDatumUtf8BytesArrayLength - 1; l_byteIndex >= 0; l_byteIndex --) {
l_hash = ( (l_hash >>> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF);
l_hash ^= l_originalDatumUtf8BytesArray [l_byteIndex];
}
l_hash = ( (l_hash >>> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF);
l_hash ^= (0x8000 | ('N' << 8) | 'K');
l_hash ^= l_originalDatumUtf8BytesArrayLength;
}
return (short) l_hash;
}
}
Objector 37B
. . . This time, the hash is a 16-bits array?
Hypothesizer 7
Yes, but also it will be ultimately turned into a UNO 'long' datum.
Objector 37B
So, the upper 16-bits of the 'long' datum should be all '0'?
Hypothesizer 7
Yes.
Again, 'l_hash' is really a 16-bits arrays, but I use an 'int' variable because of the same reason cited above.
Objector 37B
Of course.
Hypothesizer 7
These are my C++, C#, and Python functions that create the hash for any '.xls' file.
@C++ Source Code
#ifndef __theBiasPlanet_unoUtilities_cryptography_MicrosoftPasswordsHasher_hpp__
#define __theBiasPlanet_unoUtilities_cryptography_MicrosoftPasswordsHasher_hpp__
#include <string>
#include "theBiasPlanet/unoUtilities/visualCplusplusSpecificHeaders/VisualCplusplusSpecificDefinitions.hpp"
using namespace ::std;
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
class __theBiasPlanet_unoUtilities_symbolExportingOrImportingForVisualCplusplus__ MicrosoftPasswordsHasher {
private:
~
public:
~
static short hashIn16bits (string const & a_originalDatum);
};
}
}
}
#endif
#include "theBiasPlanet/unoUtilities/cryptography/MicrosoftPasswordsHasher.hpp"
#include <limits>
#include "theBiasPlanet/coreUtilities/stringsHandling/StringHandler.hpp"
using namespace ::theBiasPlanet::coreUtilities::stringsHandling;
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
~
short MicrosoftPasswordsHasher::hashIn16bits (string const & a_originalDatum) {
unsigned short l_hash = 0;
int l_originalDatumLength = a_originalDatum.length ();
if (l_originalDatumLength <= numeric_limits <short>::max ()) {
for (int l_byteIndex = l_originalDatumLength - 1; l_byteIndex >= 0; l_byteIndex --) {
l_hash = ( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF);
l_hash ^= a_originalDatum [l_byteIndex];
}
l_hash = ( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF);
l_hash ^= (0x8000 | ('N' << 8) | 'K');
l_hash ^= l_originalDatumLength;
}
return (short) l_hash;
}
}
}
}
@C# Source Code
namespace theBiasPlanet {
namespace unoUtilities {
namespace cryptography {
using System;
using System.Text;
public class MicrosoftPasswordsHasher {
~
public static short hashIn16bits (String a_originalDatum) {
ushort l_hash = 0;
byte [] l_originalDatumUtf8BytesArray = null;
try {
l_originalDatumUtf8BytesArray = Encoding.UTF8.GetBytes (a_originalDatum);
}
catch (Exception l_exception) {
// Is supposed to never happen.
}
int l_originalDatumUtf8BytesArrayLength = l_originalDatumUtf8BytesArray.Length;
if (l_originalDatumUtf8BytesArrayLength <= Int16.MaxValue) {
for (int l_byteIndex = l_originalDatumUtf8BytesArrayLength - 1; l_byteIndex >= 0; l_byteIndex --) {
l_hash = (ushort) (( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF));
l_hash ^= l_originalDatumUtf8BytesArray [l_byteIndex];
}
l_hash = (ushort) (( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF));
l_hash ^= (0x8000 | ('N' << 8) | 'K');
l_hash ^= (ushort) l_originalDatumUtf8BytesArrayLength;
}
return (short)l_hash;
}
}
}
}
}
@Python Source Code
from typing import List
class MicrosoftPasswordsHasher:
~
@staticmethod
def hashIn16bits (a_originalDatum: str) -> int:
l_hash: int = 0
l_originalDatumUtf8BytesArray: bytes = b''
try:
l_originalDatumUtf8BytesArray = a_originalDatum.encode (UTF-8)
except (Exception) as l_exception:
# Is supposed to never happen.
None
l_originalDatumUtf8BytesArrayLength: int = len (l_originalDatumUtf8BytesArray)
if l_originalDatumUtf8BytesArrayLength <= 2**15 - 1:
l_byteIndex: int = 0
for l_byteIndex in range (l_originalDatumUtf8BytesArrayLength - 1, 0 - 1, -1):
l_hash = ( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF)
l_hash ^= l_originalDatumUtf8BytesArray [l_byteIndex]
l_hash = ( (l_hash >> 14) & 0x01) | ( (l_hash << 1) & 0x7FFF)
l_hash ^= (0x8000 | (ord ('N') << 8) | ord ('K'))
l_hash ^= l_originalDatumUtf8BytesArrayLength
return l_hash
Objector 37B
What have happened to the implementations for the other languages cited in your 'Topics'?
Hypothesizer 7
I will leave them to anyone who want them.
Objector 37B
. . .
2: Setting a Document-Storing Property
Hypothesizer 7
The document-storing property to be set is 'ModifyPasswordInfo' as is for the OpenDocument formats, but this time, the value is a UNO 'long' datum.
In Java, we can just put the hash gotten in the previous section into the property.
In C++ and C#, we wrap the hash gotten in the previous section in UNO 'Any', and put the UNO 'Any' datum into the property.
In Python, we get the 'signed long' (for the '.doc' format) or 'signed short' (for the '.xls' format) datum of the hash gotten in the previous section, wrap the signed datum in UNO 'Any', and put the UNO 'Any' datum into the property.
Objector 37A
Huh? Why the fuss only for Python?
Hypothesizer 7
That is because we have set the desired bits array into the hash 'int' datum in the previous section, but UNO will not see the bits array itself, but the number value of the 'int' variable.
Objector 37A
Huh?
Hypothesizer 7
For example, we have set '0xFFFFFFFF' into the hash 'int' datum, but it is not '-1', but '4294967295', which is out of the 'long' range, causing an error; so we have to turn '4294967295' into '-1', which will become '0xFFFFFFFF' in UNO.
Objector 37A
Hmm . . ., whatever.
3: Executing Some Sample Programs
Hypothesizer 7
In fact, the editing-password-setting feature has been implementated into the file conversion sample programs introduced in a previous artcile (the concept, a Java implementation, a C++ implementation, a C# implementation, and a Python implementation).
How to get and build the project and how to execute the programs are described in the previous article, and the specifications of the file conversions order CSV file for setting editing passwords are described in the 1st part of this article.
4: The Conclusion and Beyond
Hypothesizer 7
Now, we know how to set any editing password into a Microsoft binary Word or Excel ('.doc' or '.xls') file from our program.
Although we should be aware of how effective editing password is for what purpose, there will be some practically valid use cases.
We will see how to set any editing password into an Office Open XML format file from our program, in the next part of this article.