2020年4月5日日曜日

37: 編集パスワードをバイナリーWord/ExcelファイルにUNOを用いてセットする

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

LibreOfficeまたはOpenOfficeを用いて、Microsoft '.doc'、'.xls'ファイルへ、Java、C++、C#、Python、BeanShell、JavaScript、Basicから

話題


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Javaプログラミング言語
About: C++
About: Microsoft .NET Framework
About: Pythonプログラミング言語
About: LibreOffice Basic
About: Apache OpenOffice Basic
About: BeanShell
About: JavaScript

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、MicrosoftバイナリーWordまたはExcel('.doc'、'.xls')ファイルに編集パスワードを自分のプログラムからセットする方法を知る。
ト書き
Hypothesizer 7、Objector 37A、Objector 37Bがコンピューターの前にいる。


オリエンテーション


Hypothesizer 7
本記事の第1パートでは、OpenDocumentフォーマットファイルに編集パスワードを私たちのプログラムからセットする方法を議論しました。

本記事の本パートでは、Microsoft OfficeバイナリーWordまたはExcelフォーマット('.doc'、'.xls')ファイルに編集パスワードを私たちのプログラムからセットする方法を知ります。

Objector 37A
実のところ、LibreOffice GUIは、編集パスワードを'.doc'ファイルに適切にセットしないんだよ。つまり、Microsoft Wordプログラムは、私が指定したパスワードではファイルを編集させてくれない。

Hypothesizer 7
サー、それはLibreOfficeのバグです。

Objector 37A
バグ?

Hypothesizer 7
はい。パスワードのハッシュを生成するロジックが間違っています。

Objector 37A
そのバグについて大勢が不平を言ったりしてないのか?そのバグは長いこと修正されず放置されてるようだが...

Hypothesizer 7
多分、その機能を欲している人が元々多くないのでしょう。

Objector 37A
なんで?

Hypothesizer 7
多分、編集パスワードは、ファイルが悪意をもって編集されるのを防ぐのにあまり効果的ではないからでしょう(本記事の第1パートの'オリエンテーション'で論じたように)、まあ、ファイルが意図せずに編集されるのを防ぐための措置としてある程度の有用性があると私は思いますが。もしくは、その旧式フォーマットはもうあまり使われていないというだけかもしれません。

Objector 37B
実のところ、私は、その機能を、もう固定されたものとしている一部のファイルを自分自身が誤って編集してしまうのを防ぐために使いたいわけ、それらのファイルを他人が触るとは全く想像してない自分のプライベートな環境でね。

Hypothesizer 7
マダム、それは妥当な使用法だと思います。

Objector 37A
とにかく、君が本記事で紹介する方法でもパスワードを適切にセットできないのかね?

Hypothesizer 7
実のところ、できます、あなたのプログラムがハッシュを正しく生成しますので。

Objector 37A
おお、それじゃあ、私のプログラムがハッシュを生成するのか...

Hypothesizer 7
はい、ハッシュ化ロジックが本記事のメインテーマです。


本体


1: 編集パスワードのハッシュを生成する


Hypothesizer 7
Officeドキュメントファイルに編集パスワードを設定することの面倒な部分は、編集パスワードのハッシュを生成することです。 

実のところ、ハッシュ化アルゴリズムは、Microsoftバイナリーフォーマット毎に異なります。

以下は、'.doc'ファイル用のハッシュを生成する私のJavaファンクションです。

@Java ソースコード
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
...ハッシュは整数なの?

Hypothesizer 7
それは、32ビット群配列で、最終的にはUNO 'long'データになります。

Objector 37B
ふーん。

Hypothesizer 7
ご注意いただきたいのですが、'l_highHash'および'l_lowhHash'は本当は16ビット群配列ですが、Javaのビット群シフト操作は自動的に(余計なことに、私の意見では)任意の'short'データを'int'データに変換するので、それらに私は'int'変数群を使用します、'short'変数群ではなく。

Objector 37B
はあ?

Hypothesizer 7
例えば、私たちが'(0b1111111111111111 << 1) >>> 1'で欲しいのは、
'0b1111111111111111' ->
'0b1111111111111110' ->
'0b0111111111111111'
ですが、Javaが行なうのは、
'0b1111111111111111' ->
'0b11111111111111111111111111111111' ->
'0b11111111111111111111111111111110' ->
'0b01111111111111111111111111111111'であり、それは問題です。

Objector 37B
...最後尾16ビット群が正しければオーケーだけど、そうなってないってこと?

Hypothesizer 7
そのとおりです。そこで、私たちはそれを始めから'int'に入れて、以下のようにします
'0b00000000000000001111111111111111' ->
'0b00000000000000011111111111111110' ->
'0b00000000000000001111111111111111'。

Objector 37B
ああ、最後尾16ビット群が望みどおりになってるわね。

Hypothesizer 7
以下が、'.doc'ファイル用のハッシュを生成する私のC++、C#、Pythonファンクションです。

@C++ ソースコード
#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# ソースコード
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 ソースコード
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
ふーん。

Hypothesizer 7
他方では、以下が、'.xls'ファイル用のハッシュを生成する私のJavaファンクションです。

@Java ソースコード
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
...今回は、ハッシュは16ビット群配列ってこと?

Hypothesizer 7
はい、しかし、それも最終的にはUNO 'long'データになります。

Objector 37B
それじゃあ、その'long'データの前部16ビット群は全'0'にするってこと?

Hypothesizer 7
はい。

この場合も、'l_hash'は本当は16ビット群配列ですが、私は'int'変数を使用します、上に挙げられたのと同じ理由のために。

Objector 37B
もちろん。

Hypothesizer 7
以下が、'.xls'ファイル用のハッシュを生成する私のC++、C#、Pythonファンクションです。

@C++ ソースコード
#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# ソースコード
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 ソースコード
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
あなたの'話題'に挙げられてる他の言語の実装はどうなったの?

Hypothesizer 7
それらは、それらを欲しい方にお任せします。

Objector 37B
...


2: 1つのドキュメント格納プロパティをセットする


Hypothesizer 7
セットすべきドキュメント群プロパティは'ModifyPasswordInfo'です(OpenDocumentフォーマット群の場合と同じく)、しかし今回は、値はUNO 'long'データです。

Javaにおいては、そのプロパティに、前セクションで得られたハッシュをただ入れればいいだけです。

C++およびC#においては、前セクションで得られたハッシュをUNO 'Any'にラップして、そのUNO 'Any'データをプロパティに入れます。

Pythonにおいては、前セクションで得られたハッシュの'signed long'('.doc'フォーマットの場合)または'signed short'('.xls'フォーマットの場合)データを取得し、その符号付きデータをUNO 'Any'にラップし、そのUNO 'Any'データをプロパティに入れます。

Objector 37A
はあ?なんでPythonの場合だけそんな大騒ぎをするのか?

Hypothesizer 7
それは、前セクションにて私たちは望みのビット群配列をハッシュ'int'データの中へセットしましたが、UNOはそのビット群配列そのものではなく、'int'変数の数字値を見るからです。

Objector 37A
はあ?

Hypothesizer 7
例えば、ハッシュ'int'データの中に'0xFFFFFFFF'をセットしたとすると、それは、'-1'ではなく、'4294967295'であり、それは、'long'の範囲になく、エラーを起こします。したがって、私たちは'4294967295'を'-1'に変換しなければならず、それがUNOにおいて'0xFFFFFFFF'になります。

Objector 37A
ふーむ...まあいいけど。


3: サンプルプログラムを実行する


Hypothesizer 7
実は、編集パスワードをセットする機能は、ある以前の記事(コンセプトあるJava実装あるC++実装あるC#実装あるPython実装)で紹介されたファイルコンバージョンサンプルプログラムに実装されています。

当該プロジェクトを入手・ビルドする方法およびプログラム群を実行する方法は、上記以前の記事に記述されており、編集パスワードをセットするためのファイルコンバージョン命令CSVファイル仕様は、本記事の第1パートに記述されています


4: 結びとその先


Hypothesizer 7
これで、MicrosoftバイナリーWordまたはExcel('.doc'または'.xls')ファイルに編集パスワードを私たちのプログラムからセットする方法を知りました。

編集パスワードがいかなる目的のためにどれだけ効果的であるかを意識する必要がありますが、実際的に妥当なユースケースはいくらかあるでしょう。

Office Open XMLフォーマットファイルに編集パスワードを私たちのプログラムからセットする方法を、本記事の次のパートで見ましょう。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>