25
25
*/
26
26
class Protection
27
27
{
28
- static $ algorithmMapping = [
29
- 1 => 'md2 ' ,
30
- 2 => 'md4 ' ,
31
- 3 => 'md5 ' ,
32
- 4 => 'sha1 ' ,
33
- 5 => '' , // 'mac' -> not possible with hash()
34
- 6 => 'ripemd ' ,
35
- 7 => 'ripemd160 ' ,
36
- 8 => '' ,
37
- 9 => '' , //'hmac' -> not possible with hash()
38
- 10 => '' ,
39
- 11 => '' ,
40
- 12 => 'sha256 ' ,
41
- 13 => 'sha384 ' ,
42
- 14 => 'sha512 ' ,
43
- ];
44
- static $ initialCodeArray = [
45
- 0xE1F0 ,
46
- 0x1D0F ,
47
- 0xCC9C ,
48
- 0x84C0 ,
49
- 0x110C ,
50
- 0x0E10 ,
51
- 0xF1CE ,
52
- 0x313E ,
53
- 0x1872 ,
54
- 0xE139 ,
55
- 0xD40F ,
56
- 0x84F9 ,
57
- 0x280C ,
58
- 0xA96A ,
59
- 0x4EC3
60
- ];
61
- static $ encryptionMatrix =
62
- [
63
- [0xAEFC , 0x4DD9 , 0x9BB2 , 0x2745 , 0x4E8A , 0x9D14 , 0x2A09 ],
64
- [0x7B61 , 0xF6C2 , 0xFDA5 , 0xEB6B , 0xC6F7 , 0x9DCF , 0x2BBF ],
65
- [0x4563 , 0x8AC6 , 0x05AD , 0x0B5A , 0x16B4 , 0x2D68 , 0x5AD0 ],
66
- [0x0375 , 0x06EA , 0x0DD4 , 0x1BA8 , 0x3750 , 0x6EA0 , 0xDD40 ],
67
- [0xD849 , 0xA0B3 , 0x5147 , 0xA28E , 0x553D , 0xAA7A , 0x44D5 ],
68
- [0x6F45 , 0xDE8A , 0xAD35 , 0x4A4B , 0x9496 , 0x390D , 0x721A ],
69
- [0xEB23 , 0xC667 , 0x9CEF , 0x29FF , 0x53FE , 0xA7FC , 0x5FD9 ],
70
- [0x47D3 , 0x8FA6 , 0x0F6D , 0x1EDA , 0x3DB4 , 0x7B68 , 0xF6D0 ],
71
- [0xB861 , 0x60E3 , 0xC1C6 , 0x93AD , 0x377B , 0x6EF6 , 0xDDEC ],
72
- [0x45A0 , 0x8B40 , 0x06A1 , 0x0D42 , 0x1A84 , 0x3508 , 0x6A10 ],
73
- [0xAA51 , 0x4483 , 0x8906 , 0x022D , 0x045A , 0x08B4 , 0x1168 ],
74
- [0x76B4 , 0xED68 , 0xCAF1 , 0x85C3 , 0x1BA7 , 0x374E , 0x6E9C ],
75
- [0x3730 , 0x6E60 , 0xDCC0 , 0xA9A1 , 0x4363 , 0x86C6 , 0x1DAD ],
76
- [0x3331 , 0x6662 , 0xCCC4 , 0x89A9 , 0x0373 , 0x06E6 , 0x0DCC ],
77
- [0x1021 , 0x2042 , 0x4084 , 0x8108 , 0x1231 , 0x2462 , 0x48C4 ]
78
- ];
79
- static $ passwordMaxLength = 15 ;
80
-
81
28
/**
82
29
* Editing restriction none|readOnly|comments|trackedChanges|forms
83
30
*
@@ -91,28 +38,28 @@ class Protection
91
38
*
92
39
* @var string
93
40
*/
94
- private $ password ;
41
+ private $ password = '' ;
95
42
96
43
/**
97
44
* Number of hashing iterations
98
45
*
99
46
* @var int
100
47
*/
101
- private $ spinCount = 100000 ;
48
+ private $ spinCount = 0 ;
102
49
103
50
/**
104
- * Algorithm-SID according to self ::$algorithmMapping
51
+ * Algorithm-SID (see to \PhpOffice\PhpWord\Writer\Word2007\Part\Settings ::$algorithmMapping)
105
52
*
106
53
* @var int
107
54
*/
108
- private $ algorithmSid = 4 ;
55
+ private $ algorithmSid = 0 ;
109
56
110
57
/**
111
58
* Hashed salt
112
59
*
113
60
* @var string
114
61
*/
115
- private $ salt ;
62
+ private $ salt = '' ;
116
63
117
64
/**
118
65
* Create a new instance
@@ -165,7 +112,7 @@ public function getPassword()
165
112
*/
166
113
public function setPassword ($ password )
167
114
{
168
- $ this ->password = $ this -> getPasswordHash ( $ password) ;
115
+ $ this ->password = $ password ;
169
116
170
117
return $ this ;
171
118
}
@@ -204,7 +151,7 @@ public function getAlgorithmSid()
204
151
}
205
152
206
153
/**
207
- * Set algorithm-sid (see self ::$algorithmMapping)
154
+ * Set algorithm-sid (see \PhpOffice\PhpWord\Writer\Word2007\Part\Settings ::$algorithmMapping)
208
155
*
209
156
* @param $algorithmSid
210
157
* @return self
@@ -238,128 +185,4 @@ public function setSalt($salt)
238
185
239
186
return $ this ;
240
187
}
241
-
242
- /**
243
- * Get algorithm from self::$algorithmMapping
244
- *
245
- * @return string
246
- */
247
- private function getAlgorithm ()
248
- {
249
- $ algorithm = self ::$ algorithmMapping [$ this ->algorithmSid ];
250
- if ($ algorithm == '' ) {
251
- $ algorithm = 'sha1 ' ;
252
- }
253
-
254
- return $ algorithm ;
255
- }
256
-
257
- /**
258
- * Create a hashed password that MS Word will be able to work with
259
- *
260
- * @param string $password
261
- * @return string
262
- */
263
- private function getPasswordHash ($ password )
264
- {
265
- $ orig_encoding = mb_internal_encoding ();
266
- mb_internal_encoding ("UTF-8 " );
267
-
268
- if (empty ($ password )) {
269
- return '' ;
270
- }
271
-
272
- $ password = mb_substr ($ password , 0 , min (self ::$ passwordMaxLength , mb_strlen ($ password )));
273
-
274
- // Construct a new NULL-terminated string consisting of single-byte characters:
275
- // Get the single-byte values by iterating through the Unicode characters of the truncated password.
276
- // For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
277
- $ pass_utf8 = mb_convert_encoding ($ password , 'UCS-2LE ' , 'UTF-8 ' );
278
- $ byteChars = [];
279
- for ($ i = 0 ; $ i < mb_strlen ($ password ); $ i ++) {
280
- $ byteChars [$ i ] = ord (substr ($ pass_utf8 , $ i * 2 , 1 ));
281
- if ($ byteChars [$ i ] == 0 ) {
282
- $ byteChars [$ i ] = ord (substr ($ pass_utf8 , $ i * 2 + 1 , 1 ));
283
- }
284
- }
285
-
286
- // build low-order word and hig-order word and combine them
287
- $ combinedKey = $ this ->buildCombinedKey ($ byteChars );
288
- // build reversed hexadecimal string
289
- $ hex = strtoupper (dechex ($ combinedKey & 0xFFFFFFFF ));
290
- $ reversedHex = $ hex [6 ].$ hex [7 ].$ hex [4 ].$ hex [5 ].$ hex [2 ].$ hex [3 ].$ hex [0 ].$ hex [1 ];
291
-
292
- $ generatedKey = mb_convert_encoding ($ reversedHex , 'UCS-2LE ' , 'UTF-8 ' );
293
-
294
- // Implementation Notes List:
295
- // Word requires that the initial hash of the password with the salt not be considered in the count.
296
- // The initial hash of salt + key is not included in the iteration count.
297
- $ generatedKey = hash ($ this ->getAlgorithm (), base64_decode ($ this ->getSalt ()) . $ generatedKey , true );
298
- for ($ i = 0 ; $ i < $ this ->getSpinCount (); $ i ++) {
299
- $ generatedKey = hash ($ this ->getAlgorithm (), $ generatedKey . pack ("CCCC " , $ i , $ i >>8 , $ i >>16 , $ i >>24 ), true );
300
- }
301
- $ generatedKey = base64_encode ($ generatedKey );
302
-
303
- mb_internal_encoding ($ orig_encoding );
304
-
305
- return $ generatedKey ;
306
- }
307
-
308
- /**
309
- * Build combined key from low-order word and high-order word
310
- *
311
- * @param array $byteChars -> byte array representation of password
312
- * @return int
313
- */
314
- private function buildCombinedKey ($ byteChars )
315
- {
316
- // Compute the high-order word
317
- // Initialize from the initial code array (see above), depending on the passwords length.
318
- $ highOrderWord = self ::$ initialCodeArray [sizeof ($ byteChars ) - 1 ];
319
-
320
- // For each character in the password:
321
- // For every bit in the character, starting with the least significant and progressing to (but excluding)
322
- // the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
323
- // the Encryption Matrix
324
- for ($ i = 0 ; $ i < sizeof ($ byteChars ); $ i ++) {
325
- $ tmp = self ::$ passwordMaxLength - sizeof ($ byteChars ) + $ i ;
326
- $ matrixRow = self ::$ encryptionMatrix [$ tmp ];
327
- for ($ intBit = 0 ; $ intBit < 7 ; $ intBit ++) {
328
- if (($ byteChars [$ i ] & (0x0001 << $ intBit )) != 0 ) {
329
- $ highOrderWord = ($ highOrderWord ^ $ matrixRow [$ intBit ]);
330
- }
331
- }
332
- }
333
-
334
- // Compute low-order word
335
- // Initialize with 0
336
- $ lowOrderWord = 0 ;
337
- // For each character in the password, going backwards
338
- for ($ i = sizeof ($ byteChars ) - 1 ; $ i >= 0 ; $ i --) {
339
- // low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
340
- $ lowOrderWord = (((($ lowOrderWord >> 14 ) & 0x0001 ) | (($ lowOrderWord << 1 ) & 0x7FFF )) ^ $ byteChars [$ i ]);
341
- }
342
- // Lastly, low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B.
343
- $ lowOrderWord = (((($ lowOrderWord >> 14 ) & 0x0001 ) | (($ lowOrderWord << 1 ) & 0x7FFF )) ^ sizeof ($ byteChars ) ^ 0xCE4B );
344
-
345
- // Combine the Low and High Order Word
346
- return $ this ->int32 (($ highOrderWord << 16 ) + $ lowOrderWord );
347
- }
348
-
349
- /**
350
- * simulate behaviour of int32
351
- *
352
- * @param int $value
353
- * @return int
354
- */
355
- private function int32 ($ value )
356
- {
357
- $ value = ($ value & 0xFFFFFFFF );
358
-
359
- if ($ value & 0x80000000 ) {
360
- $ value = -((~$ value & 0xFFFFFFFF ) + 1 );
361
- }
362
-
363
- return $ value ;
364
- }
365
188
}
0 commit comments