Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Shared;
4
 
5
use PhpOffice\PhpSpreadsheet\Exception as SpException;
6
use PhpOffice\PhpSpreadsheet\Worksheet\Protection;
7
 
8
class PasswordHasher
9
{
10
    const MAX_PASSWORD_LENGTH = 255;
11
 
12
    /**
13
     * Get algorithm name for PHP.
14
     */
15
    private static function getAlgorithm(string $algorithmName): string
16
    {
17
        if (!$algorithmName) {
18
            return '';
19
        }
20
 
21
        // Mapping between algorithm name in Excel and algorithm name in PHP
22
        $mapping = [
23
            Protection::ALGORITHM_MD2 => 'md2',
24
            Protection::ALGORITHM_MD4 => 'md4',
25
            Protection::ALGORITHM_MD5 => 'md5',
26
            Protection::ALGORITHM_SHA_1 => 'sha1',
27
            Protection::ALGORITHM_SHA_256 => 'sha256',
28
            Protection::ALGORITHM_SHA_384 => 'sha384',
29
            Protection::ALGORITHM_SHA_512 => 'sha512',
30
            Protection::ALGORITHM_RIPEMD_128 => 'ripemd128',
31
            Protection::ALGORITHM_RIPEMD_160 => 'ripemd160',
32
            Protection::ALGORITHM_WHIRLPOOL => 'whirlpool',
33
        ];
34
 
35
        if (array_key_exists($algorithmName, $mapping)) {
36
            return $mapping[$algorithmName];
37
        }
38
 
39
        throw new SpException('Unsupported password algorithm: ' . $algorithmName);
40
    }
41
 
42
    /**
43
     * Create a password hash from a given string.
44
     *
45
     * This method is based on the spec at:
46
     * https://interoperability.blob.core.windows.net/files/MS-OFFCRYPTO/[MS-OFFCRYPTO].pdf
47
     * 2.3.7.1 Binary Document Password Verifier Derivation Method 1
48
     *
49
     * It replaces a method based on the algorithm provided by
50
     * Daniel Rentz of OpenOffice and the PEAR package
51
     * Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>.
52
     *
53
     * Scrutinizer will squawk at the use of bitwise operations here,
54
     * but it should ultimately pass.
55
     *
56
     * @param string $password Password to hash
57
     */
58
    private static function defaultHashPassword(string $password): string
59
    {
60
        $verifier = 0;
61
        $pwlen = strlen($password);
62
        $passwordArray = pack('c', $pwlen) . $password;
63
        for ($i = $pwlen; $i >= 0; --$i) {
64
            $intermediate1 = (($verifier & 0x4000) === 0) ? 0 : 1;
65
            $intermediate2 = 2 * $verifier;
66
            $intermediate2 = $intermediate2 & 0x7fff;
67
            $intermediate3 = $intermediate1 | $intermediate2;
68
            $verifier = $intermediate3 ^ ord($passwordArray[$i]);
69
        }
70
        $verifier ^= 0xCE4B;
71
 
72
        return strtoupper(dechex($verifier));
73
    }
74
 
75
    /**
76
     * Create a password hash from a given string by a specific algorithm.
77
     *
78
     * 2.4.2.4 ISO Write Protection Method
79
     *
80
     * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
81
     *
82
     * @param string $password Password to hash
83
     * @param string $algorithm Hash algorithm used to compute the password hash value
84
     * @param string $salt Pseudorandom string
85
     * @param int $spinCount Number of times to iterate on a hash of a password
86
     *
87
     * @return string Hashed password
88
     */
89
    public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string
90
    {
91
        if (strlen($password) > self::MAX_PASSWORD_LENGTH) {
92
            throw new SpException('Password exceeds ' . self::MAX_PASSWORD_LENGTH . ' characters');
93
        }
94
        $phpAlgorithm = self::getAlgorithm($algorithm);
95
        if (!$phpAlgorithm) {
96
            return self::defaultHashPassword($password);
97
        }
98
 
99
        $saltValue = base64_decode($salt);
100
        $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
101
 
102
        $hashValue = hash($phpAlgorithm, $saltValue . /** @scrutinizer ignore-type */ $encodedPassword, true);
103
        for ($i = 0; $i < $spinCount; ++$i) {
104
            $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
105
        }
106
 
107
        return base64_encode($hashValue);
108
    }
109
}