Skip to content

Commit 0221414

Browse files
committed
randomly genereate salt for word password protection
1 parent 703e341 commit 0221414

File tree

2 files changed

+24
-25
lines changed

2 files changed

+24
-25
lines changed

src/PhpWord/Metadata/Protection.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ class Protection
4545
*
4646
* @var int
4747
*/
48-
private $spinCount = 0;
48+
private $spinCount = 100000;
4949

5050
/**
5151
* Algorithm-SID (see to \PhpOffice\PhpWord\Writer\Word2007\Part\Settings::$algorithmMapping)
5252
*
5353
* @var int
5454
*/
55-
private $algorithmSid = 0;
55+
private $mswordAlgorithmSid = 4;
5656

5757
/**
5858
* Hashed salt
@@ -145,20 +145,20 @@ public function setSpinCount($spinCount)
145145
*
146146
* @return int
147147
*/
148-
public function getAlgorithmSid()
148+
public function getMswordAlgorithmSid()
149149
{
150-
return $this->algorithmSid;
150+
return $this->mswordAlgorithmSid;
151151
}
152152

153153
/**
154154
* Set algorithm-sid (see \PhpOffice\PhpWord\Writer\Word2007\Part\Settings::$algorithmMapping)
155155
*
156-
* @param $algorithmSid
156+
* @param $mswordAlgorithmSid
157157
* @return self
158158
*/
159-
public function setAlgorithmSid($algorithmSid)
159+
public function setMswordAlgorithmSid($mswordAlgorithmSid)
160160
{
161-
$this->algorithmSid = $algorithmSid;
161+
$this->mswordAlgorithmSid = $mswordAlgorithmSid;
162162

163163
return $this;
164164
}

src/PhpWord/Writer/Word2007/Part/Settings.php

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,17 @@ private function getProtection()
213213
)
214214
);
215215
} else {
216+
if ($protection->getSalt() == null) {
217+
$protection->setSalt(openssl_random_pseudo_bytes(16));
218+
}
216219
$this->settings['w:documentProtection'] = array(
217220
'@attributes' => array(
218221
'w:enforcement' => 1,
219222
'w:edit' => $protection->getEditing(),
220223
'w:cryptProviderType' => 'rsaFull',
221224
'w:cryptAlgorithmClass' => 'hash',
222225
'w:cryptAlgorithmType' => 'typeAny',
223-
'w:cryptAlgorithmSid' => $protection->getAlgorithmSid(),
226+
'w:cryptAlgorithmSid' => $protection->getMswordAlgorithmSid(),
224227
'w:cryptSpinCount' => $protection->getSpinCount(),
225228
'w:hash' => $this->getPasswordHash($protection),
226229
'w:salt' => $this->getSaltHash($protection->getSalt()),
@@ -239,11 +242,13 @@ private function getCompatibility()
239242
{
240243
$compatibility = $this->getParentWriter()->getPhpWord()->getCompatibility();
241244
if ($compatibility->getOoxmlVersion() !== null) {
242-
$this->settings['w:compat']['w:compatSetting'] = array('@attributes' => array(
243-
'w:name' => 'compatibilityMode',
244-
'w:uri' => 'http://schemas.microsoft.com/office/word',
245-
'w:val' => $compatibility->getOoxmlVersion(),
246-
));
245+
$this->settings['w:compat']['w:compatSetting'] = array(
246+
'@attributes' => array(
247+
'w:name' => 'compatibilityMode',
248+
'w:uri' => 'http://schemas.microsoft.com/office/word',
249+
'w:val' => $compatibility->getOoxmlVersion(),
250+
)
251+
);
247252
}
248253
}
249254

@@ -277,21 +282,19 @@ private function getPasswordHash($protection)
277282
// build low-order word and hig-order word and combine them
278283
$combinedKey = $this->buildCombinedKey($byteChars);
279284
// build reversed hexadecimal string
280-
$hex = strtoupper(dechex($combinedKey & 0xFFFFFFFF));
281-
$reversedHex = $hex[6].$hex[7].$hex[4].$hex[5].$hex[2].$hex[3].$hex[0].$hex[1];
285+
$hex = strtoupper(dechex($combinedKey & 0xFFFFFFFF));
286+
$reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1];
282287

283288
$generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8');
284289

285290
// Implementation Notes List:
286291
// Word requires that the initial hash of the password with the salt not be considered in the count.
287292
// The initial hash of salt + key is not included in the iteration count.
288-
$algorithm = $this->getAlgorithm($protection->getAlgorithmSid());
293+
$algorithm = $this->getAlgorithm($protection->getMswordAlgorithmSid());
289294
$generatedKey = hash($algorithm, base64_decode($this->getSaltHash($protection->getSalt())) . $generatedKey, true);
290295

291-
$spinCount = (!empty($protection->getSpinCount())) ? $protection->getSpinCount() : 100000;
292-
293-
for ($i = 0; $i < $spinCount; $i++) {
294-
$generatedKey = hash($algorithm, $generatedKey . pack("CCCC", $i, $i>>8, $i>>16, $i>>24), true);
296+
for ($i = 0; $i < $protection->getSpinCount(); $i++) {
297+
$generatedKey = hash($algorithm, $generatedKey . pack("CCCC", $i, $i >> 8, $i >> 16, $i >> 24), true);
295298
}
296299
$generatedKey = base64_encode($generatedKey);
297300

@@ -308,10 +311,6 @@ private function getPasswordHash($protection)
308311
*/
309312
private function getAlgorithm($sid)
310313
{
311-
if (empty($sid)) {
312-
$sid = 4;
313-
}
314-
315314
$algorithm = self::$algorithmMapping[$sid];
316315
if ($algorithm == '') {
317316
$algorithm = 'sha1';
@@ -328,7 +327,7 @@ private function getAlgorithm($sid)
328327
*/
329328
private function getSaltHash($salt)
330329
{
331-
return $salt;
330+
return base64_encode(str_pad(substr($salt, 0, 16), 16, '1'));
332331
}
333332

334333
/**

0 commit comments

Comments
 (0)