44
55class PasswordHasher
66{
7+ const ALGORITHM_MD2 = 'MD2 ' ;
8+ const ALGORITHM_MD4 = 'MD4 ' ;
9+ const ALGORITHM_MD5 = 'MD5 ' ;
10+ const ALGORITHM_SHA_1 = 'SHA-1 ' ;
11+ const ALGORITHM_SHA_256 = 'SHA-256 ' ;
12+ const ALGORITHM_SHA_384 = 'SHA-384 ' ;
13+ const ALGORITHM_SHA_512 = 'SHA-512 ' ;
14+ const ALGORITHM_RIPEMD_128 = 'RIPEMD-128 ' ;
15+ const ALGORITHM_RIPEMD_160 = 'RIPEMD-160 ' ;
16+ const ALGORITHM_WHIRLPOOL = 'WHIRLPOOL ' ;
17+
18+ /**
19+ * Mapping between algorithm name in Excel and algorithm name in PHP.
20+ *
21+ * @var array
22+ */
23+ private static $ algorithmArray = [
24+ self ::ALGORITHM_MD2 => 'md2 ' ,
25+ self ::ALGORITHM_MD4 => 'md4 ' ,
26+ self ::ALGORITHM_MD5 => 'md5 ' ,
27+ self ::ALGORITHM_SHA_1 => 'sha1 ' ,
28+ self ::ALGORITHM_SHA_256 => 'sha256 ' ,
29+ self ::ALGORITHM_SHA_384 => 'sha384 ' ,
30+ self ::ALGORITHM_SHA_512 => 'sha512 ' ,
31+ self ::ALGORITHM_RIPEMD_128 => 'ripemd128 ' ,
32+ self ::ALGORITHM_RIPEMD_160 => 'ripemd160 ' ,
33+ self ::ALGORITHM_WHIRLPOOL => 'whirlpool ' ,
34+ ];
35+
36+ /**
37+ * Get algorithm from self::$algorithmArray.
38+ *
39+ * @param string $pAlgorithmName
40+ *
41+ * @return string
42+ */
43+ private static function getAlgorithm ($ pAlgorithmName )
44+ {
45+ if (array_key_exists ($ pAlgorithmName , self ::$ algorithmArray )) {
46+ return self ::$ algorithmArray [$ pAlgorithmName ];
47+ }
48+
49+ return '' ;
50+ }
51+
752 /**
853 * Create a password hash from a given string.
954 *
@@ -15,7 +60,7 @@ class PasswordHasher
1560 *
1661 * @return string Hashed password
1762 */
18- public static function hashPassword ($ pPassword )
63+ public static function defaultHashPassword ($ pPassword )
1964 {
2065 $ password = 0x0000 ;
2166 $ charPos = 1 ; // char position
@@ -34,4 +79,48 @@ public static function hashPassword($pPassword)
3479
3580 return strtoupper (dechex ($ password ));
3681 }
82+
83+ /**
84+ * Create a password hash from a given string by a specific algorithm.
85+ *
86+ * 2.4.2.4 ISO Write Protection Method
87+ *
88+ * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
89+ *
90+ * @param string $pPassword Password to hash
91+ * @param string $pAlgorithmName Hash algorithm used to compute the password hash value
92+ * @param string $pSaltValue Pseudorandom string
93+ * @param string $pSpinCount Number of times to iterate on a hash of a password
94+ *
95+ * @return string Hashed password
96+ */
97+ public static function hashPassword ($ pPassword , $ pAlgorithmName = '' , $ pSaltValue = '' , $ pSpinCount = 10000 )
98+ {
99+ $ algorithmName = self ::getAlgorithm ($ pAlgorithmName );
100+ if (!$ pAlgorithmName ) {
101+ return self ::defaultHashPassword ($ pPassword );
102+ }
103+
104+ $ saltValue = base64_decode ($ pSaltValue );
105+ $ password = mb_convert_encoding ($ pPassword , 'UCS-2LE ' , 'UTF-8 ' );
106+
107+ $ hashValue = hash ($ algorithmName , $ saltValue . $ password , true );
108+ for ($ i = 0 ; $ i < $ pSpinCount ; ++$ i ) {
109+ $ hashValue = hash ($ algorithmName , $ hashValue . pack ('L ' , $ i ), true );
110+ }
111+
112+ return base64_encode ($ hashValue );
113+ }
114+
115+ /**
116+ * Create a pseudorandom string.
117+ *
118+ * @param int $pSize Length of the output string in bytes
119+ *
120+ * @return string Pseudorandom string
121+ */
122+ public static function generateSalt ($ pSize = 16 )
123+ {
124+ return base64_encode (random_bytes ($ pSize ));
125+ }
37126}
0 commit comments